diff options
71 files changed, 15057 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/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/ff/GnuGPL.txt b/ff/GnuGPL.txt new file mode 100644 index 0000000..fa0bef4 --- /dev/null +++ b/ff/GnuGPL.txt @@ -0,0 +1,290 @@ +GNU GENERAL PUBLIC LICENSE
+
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom
+to share and change it. By contrast, the GNU General Public License is
+intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This General
+Public License applies to most of the Free Software Foundation's
+software and to any other program whose authors commit to using it.
+(Some other Free Software Foundation software is covered by the
+GNU Library General Public License instead.) You can apply it to your
+programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new free
+programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone
+to deny you these rights or to ask you to surrender the rights. These
+restrictions translate to certain responsibilities for you if you distribute
+copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis
+or for a fee, you must give the recipients all the rights that you have. You
+must make sure that they, too, receive or can get the source code. And
+you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on,
+we want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software patents.
+We wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program
+proprietary. To prevent this, we have made it clear that any patent must
+be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+TERMS AND CONDITIONS FOR
+COPYING, DISTRIBUTION AND
+MODIFICATION
+
+0. This License applies to any program or other work which contains a
+notice placed by the copyright holder saying it may be distributed under
+the terms of this General Public License. The "Program", below, refers
+to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it, either
+verbatim or with modifications and/or translated into another language.
+(Hereinafter, translation is included without limitation in the term
+"modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of running
+the Program is not restricted, and the output from the Program is
+covered only if its contents constitute a work based on the Program
+(independent of having been made by running the Program). Whether
+that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the notices
+that refer to this License and to the absence of any warranty; and give
+any other recipients of the Program a copy of this License along with the
+Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide a
+ warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but does
+ not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program, and
+can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based on
+the Program, the distribution of the whole must be on the terms of this
+License, whose permissions for other licensees extend to the entire
+whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your
+rights to work written entirely by you; rather, the intent is to exercise the
+right to control the distribution of derivative or collective works based
+on the Program.
+
+In addition, mere aggregation of another work not based on the
+Program with the Program (or with a work based on the Program) on a
+volume of a storage or distribution medium does not bring the other
+work under the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding
+ machine-readable source code, which must be distributed under
+ the terms of Sections 1 and 2 above on a medium customarily
+ used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your cost
+ of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a
+ medium customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with
+ such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to control
+compilation and installation of the executable. However, as a special
+exception, the source code distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access to
+copy from a designated place, then offering equivalent access to copy
+the source code from the same place counts as distribution of the source
+code, even though third parties are not compelled to copy the source
+along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt otherwise
+to copy, modify, sublicense or distribute the Program is void, and will
+automatically terminate your rights under this License. However, parties
+who have received copies, or rights, from you under this License will not
+have their licenses terminated so long as such parties remain in full
+compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and all
+its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these terms
+and conditions. You may not impose any further restrictions on the
+recipients' exercise of the rights granted herein. You are not responsible
+for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot distribute
+so as to satisfy simultaneously your obligations under this License and
+any other pertinent obligations, then as a consequence you may not
+distribute the Program at all. For example, if a patent license would not
+permit royalty-free redistribution of the Program by all those who
+receive copies directly or indirectly through you, then the only way you
+could satisfy both it and this License would be to refrain entirely from
+distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents
+or other property right claims or to contest validity of any such claims;
+this section has the sole purpose of protecting the integrity of the free
+software distribution system, which is implemented by public license
+practices. Many people have made generous contributions to the wide
+range of software distributed through that system in reliance on
+consistent application of that system; it is up to the author/donor to
+decide if he or she is willing to distribute software through any other
+system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be
+a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain
+countries either by patents or by copyrighted interfaces, the original
+copyright holder who places the Program under this License may add an
+explicit geographical distribution limitation excluding those countries, so
+that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new
+versions of the General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may differ in
+detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number
+of this License, you may choose any version ever published by the Free
+Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we
+sometimes make exceptions for this. Our decision will be guided by the
+two goals of preserving the free status of all derivatives of our free
+software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF
+CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM,
+TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT
+WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE
+PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND,
+EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
+PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD
+THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE
+COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW
+OR AGREED TO IN WRITING WILL ANY COPYRIGHT
+HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED
+ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING
+ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
+INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT
+LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
+WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR
+OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
diff --git a/ff/README b/ff/README new file mode 100644 index 0000000..628fae0 --- /dev/null +++ b/ff/README @@ -0,0 +1,19 @@ +This is a small library that implements force feedback effects for pd. +It is BETA. +I might completely change this (see the TODO) +To compile: +make sure that m_pd.h is in your include path + +patch your kernel with ff-patches from Johann Deneux: +--( http://user.it.uu.se/~johannd/projects/ff/index.shtml) + +He also has a standalone iforce driver available + + +type make. +Manually install everything :) + +have fun + +Gerard van Dongen +gml@xs4all.nl @@ -0,0 +1,13 @@ + +-add user definable waveforms for the periodic effects. + +-unify all externals into a single forcefeedback external? + +I am not sure if this is a good idea.It will make the code +cleaner because it is closer to the hardware model, and it +would allow intergration with the joystick object. +But it will make patching more cumbersome. + +-more testing with other iforce compatible devices +-add rumble,ramp,damper and inertia effects. +-learn autotools and autotoolize this.
\ No newline at end of file diff --git a/ff/ff-autocenter-help.pd b/ff/ff-autocenter-help.pd new file mode 100644 index 0000000..4338ff6 --- /dev/null +++ b/ff/ff-autocenter-help.pd @@ -0,0 +1,17 @@ +#N canvas 120 116 464 364 10; +#X text 20 66 sets the amount of "autocentering" of the force-feedback +joystick. Range is [0 \, 1]. 0 is no auto-centering; +#X floatatom 77 254 5 0 0 0 - - -; +#X text 19 149 This is affected by the overall gain \, which can be +controlled used [ff-gain].; +#X obj 4 4 cnv 15 450 30 empty empty [ff-autocenter] 4 12 1 18 -261689 +-1 0; +#X text 19 102 creation arguments are device number (i.e. 0 for /dev/input/event0) +and autocenter amount. This effects all effects for that device; +#X obj 77 277 ff-autocenter 0 0; +#X obj 253 323 pddp_open all_about_haptics; +#X text 140 325 For more info:; +#X obj 77 211 tgl 30 0 empty empty start 1 15 1 12 -90049 -1 -1 0 1 +; +#X connect 1 0 5 0; +#X connect 8 0 1 0; diff --git a/ff/ff-constant-help.pd b/ff/ff-constant-help.pd new file mode 100644 index 0000000..5bd0c75 --- /dev/null +++ b/ff/ff-constant-help.pd @@ -0,0 +1,108 @@ +#N canvas 284 83 862 620 10; +#X msg 104 432 bang; +#X msg 68 521 stop; +#X text 383 323 startlevel attack-duration endlevel decay-duration +; +#X text 416 438 direction in degrees; +#X text 464 478 duration in ms \, 0 is infinite; +#X text 525 524 level \, range = [-1 \, 1]; +#X text 39 417 start the effect; +#X text 44 433 with a; +#X text 224 150 delay before starting; +#X text 122 75 minimum time between triggers; +#X msg 232 329 envelope 0 100 0 100; +#X obj 218 555 ff-constant 0 0 500 0.5; +#X obj 476 502 hsl 190 17 -1 1 0 0 empty empty level 25 9 1 12 -261689 +-1 -1 18900 0; +#X obj 367 418 hsl 175 17 0 360 0 1 empty empty direction 25 9 1 12 +-262131 -1 -1 17400 0; +#X obj 418 458 hsl 235 17 0 5000 0 0 empty empty duration 25 9 1 12 +-261681 -1 -1 17800 0; +#X text 385 336 levels are in the range [-1 \, 1]; +#X obj 35 49 hsl 290 17 1 5000 0 1 empty empty interval 25 9 1 12 -225271 +-1 -1 2890 0; +#X msg 32 86 interval \$1; +#X msg 143 154 delay \$1; +#X obj 146 118 hsl 230 17 0 5000 0 1 empty empty delay 25 9 1 12 -228992 +-1 -1 1145 0; +#X obj 100 487 bng 25 250 50 0 empty empty empty 0 -6 0 8 -24198 -1 +-1; +#X obj 68 487 bng 25 250 50 0 empty empty empty 0 -6 0 8 -258699 -1 +-1; +#X obj 364 438 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 360 256; +#X obj 415 480 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 3803.42 256; +#X obj 473 524 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 1 256; +#X obj 143 138 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 250 256; +#X obj 32 69 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 500.9 256; +#X text 239 13 -- control a force-feedback constant force effect; +#X msg 218 307 envelope \$1 \$2 \$3 \$4; +#X obj 348 228 hsl 200 17 0 1 0 1 empty empty end_level 25 8 1 12 -261689 +-1 -1 0 0; +#X obj 221 186 hsl 200 17 0 1 0 1 empty empty start_level 25 8 1 12 +-261689 -1 -1 11200 0; +#X obj 218 269 nbx 3 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0.562814 256; +#X obj 218 286 pack float float float float; +#X obj 345 269 nbx 3 14 -1e+37 1e+37 0 0 pack_bang_env empty empty +0 -6 1 10 -233017 -1 -1 0 256; +#X obj 281 269 nbx 6 14 -1e+37 1e+37 0 0 pack_bang_env empty empty +0 -6 1 10 -233017 -1 -1 0 256; +#X obj 409 269 nbx 6 14 -1e+37 1e+37 0 0 pack_bang_env empty empty +0 -6 1 10 -233017 -1 -1 0 256; +#X obj 284 207 hsl 235 17 5 5000 1 0 empty empty attack_duration 25 +9 1 12 -261681 -1 -1 0 0; +#X obj 412 249 hsl 235 17 5 5000 1 0 empty empty decay_duration 25 +9 1 12 -261681 -1 -1 0 0; +#X obj 198 269 bng 15 250 50 0 empty pack_bang_env empty 0 -6 0 8 -262144 +-1 -1; +#X obj 3 3 cnv 15 850 30 empty empty [ff-constant] 15 15 1 18 -262131 +-1 0; +#X obj 642 111 pddp_open all_about_haptics; +#X text 530 110 For more info:; +#X text 394 556 <-- arguments are: device# direction duration level +; +#X text 258 578 the outlet gives the effect number on the device or +-1 if it is not loaded.; +#X floatatom 218 584 5 0 0 0 - - -; +#X msg 276 364 load; +#X msg 276 386 unload; +#X text 336 363 a load message \, uploads an effect to the stick; +#X text 335 386 an unload message \, removes it; +#X text 420 58 [ff-constant] sets up a constant force in one direction. +; +#X connect 0 0 11 0; +#X connect 1 0 11 0; +#X connect 10 0 11 0; +#X connect 11 0 44 0; +#X connect 12 0 24 0; +#X connect 13 0 22 0; +#X connect 14 0 23 0; +#X connect 16 0 26 0; +#X connect 17 0 11 0; +#X connect 18 0 11 0; +#X connect 19 0 25 0; +#X connect 20 0 11 0; +#X connect 21 0 1 0; +#X connect 22 0 11 1; +#X connect 23 0 11 2; +#X connect 24 0 11 3; +#X connect 25 0 18 0; +#X connect 26 0 17 0; +#X connect 28 0 11 0; +#X connect 29 0 33 0; +#X connect 30 0 31 0; +#X connect 31 0 32 0; +#X connect 32 0 28 0; +#X connect 33 0 32 2; +#X connect 34 0 32 1; +#X connect 35 0 32 3; +#X connect 36 0 34 0; +#X connect 37 0 35 0; +#X connect 38 0 32 0; +#X connect 45 0 11 0; +#X connect 46 0 11 0; diff --git a/ff/ff-friction-help.pd b/ff/ff-friction-help.pd new file mode 100644 index 0000000..970b9e4 --- /dev/null +++ b/ff/ff-friction-help.pd @@ -0,0 +1,154 @@ +#N canvas 335 29 859 661 10; +#X text 334 195 coefficients that determine how fast; +#X text 334 207 the effect increases in that direction; +#X text 334 218 range = [-1 \, 1]; +#X text 349 301 width of the dead-zone \, where there is no effect +; +#X text 349 326 range is (like the joystick output) [-32768 \, 32767] +; +#X text 350 312 one for each axis; +#X text 356 424 position of the dead-zone in the joystick range; +#X text 356 436 one for each axis; +#X text 356 448 range is also [-32768 \, 32767]; +#X text 366 526 duration in ms \, 0 is infinite; +#X text 531 555 levels \, range = [0 \, 1]; +#X text 398 74 these effects set up 2 axis (x and y) and you specify +the parameters for each direction.; +#X obj 474 556 hsl 40 15 0 1 0 0 empty empty down 5 8 1 11 -261689 +-1 -1 0 0; +#X obj 420 556 hsl 40 15 0 1 0 0 empty empty up 5 8 1 11 -261689 -1 +-1 0 0; +#X obj 367 556 hsl 40 15 0 1 0 0 empty empty left 5 8 1 11 -261689 +-1 -1 0 0; +#X obj 313 556 hsl 40 15 0 1 0 0 empty empty right 5 8 1 11 -261689 +-1 -1 0 0; +#X msg 260 483 center-y \$1; +#X msg 255 429 center-x \$1; +#X obj 263 450 hsl 75 15 -32767 32767 0 1 empty empty center-x 5 8 +1 11 -228992 -1 -1 5958 0; +#X obj 258 396 hsl 75 15 -32767 32767 0 1 empty empty center-x 5 8 +1 11 -228992 -1 -1 3700 0; +#X obj 248 340 hsl 75 15 -32767 32767 0 1 empty empty deadband-y 5 +8 1 11 -225280 -1 -1 3734 0; +#X msg 245 373 deadband-y \$1; +#X msg 242 317 deadband-x \$1; +#X obj 245 284 hsl 75 15 -32767 32767 0 1 empty empty deadband-x 5 +8 1 11 -225280 -1 -1 3836 0; +#X msg 226 258 down-coeff \$1; +#X obj 229 225 hsl 90 15 -1 1 0 1 empty empty down-coeff 5 8 1 11 -257472 +-1 -1 0 0; +#X obj 223 170 hsl 90 15 -1 1 0 1 empty empty up-coeff 5 7 1 11 -257472 +-1 -1 1335 0; +#X obj 120 225 hsl 90 15 -1 1 0 0 empty empty left-coeff 5 7 1 11 -257472 +-1 -1 0 0; +#X obj 103 170 hsl 90 15 -1 1 0 1 empty empty right-coeff 5 7 1 10 +-257472 -1 -1 8900 0; +#X msg 220 203 up-coeff \$1; +#X msg 117 258 left-coeff \$1; +#X msg 100 203 right-coeff \$1; +#X obj 471 575 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X obj 417 575 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X obj 364 575 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X obj 310 575 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X obj 312 505 hsl 245 17 0 5000 0 0 empty empty duration 25 9 1 12 +-261681 -1 -1 0 0; +#X obj 309 527 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X text 213 14 -- control force-feedback friction effect; +#X obj 195 599 ff-friction 0 2000 0.5 0.5 0 0.1; +#X msg 93 503 bang; +#X msg 84 582 stop; +#X text 32 483 start the effect; +#X text 37 499 with a; +#X obj 85 533 bng 25 250 50 0 empty empty empty 0 -6 0 8 -24198 -1 +-1; +#X obj 53 533 bng 25 250 50 0 empty empty empty 0 -6 0 8 -258699 -1 +-1; +#X text 141 140 delay before starting; +#X text 111 69 minimum time between triggers; +#X obj 24 43 hsl 290 17 0 5000 0 1 empty empty interval 25 9 1 12 -225271 +-1 -1 2890 0; +#X msg 21 80 interval \$1; +#X msg 60 144 delay \$1; +#X obj 63 108 hsl 230 17 0 5000 0 1 empty empty delay 25 9 1 12 -228992 +-1 -1 1145 0; +#X obj 60 128 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 250 256; +#X obj 21 63 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 500 256; +#X obj 100 187 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 1 256; +#X obj 117 242 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X obj 226 242 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 -1 256; +#X obj 220 187 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 -0.7 256; +#X obj 242 301 nbx 6 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 1204.41 256; +#X obj 245 357 nbx 6 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 301.102 256; +#X obj 255 413 nbx 6 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X obj 260 467 nbx 6 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 19996.7 256; +#X obj 3 3 cnv 15 850 30 empty empty [ff-friction] 15 15 1 18 -257472 +-1 0; +#X obj 568 135 pddp_open all_about_haptics; +#X text 456 134 For more info:; +#X text 428 599 arguments are: device# duration right- left- up- down-level +; +#X text 238 619 the outlet gives the effect number on the device or +-1 if it is not loaded.; +#X floatatom 195 624 5 0 0 0 - - -; +#X text 397 58 [ff-friction] is a "conditional effect".; +#X text 396 110 [ff-spring] has the same methods; +#X connect 12 0 32 0; +#X connect 13 0 33 0; +#X connect 14 0 34 0; +#X connect 15 0 35 0; +#X connect 16 0 39 0; +#X connect 17 0 39 0; +#X connect 18 0 61 0; +#X connect 19 0 60 0; +#X connect 20 0 59 0; +#X connect 21 0 39 0; +#X connect 22 0 39 0; +#X connect 23 0 58 0; +#X connect 24 0 39 0; +#X connect 25 0 56 0; +#X connect 26 0 57 0; +#X connect 27 0 55 0; +#X connect 28 0 54 0; +#X connect 29 0 39 0; +#X connect 30 0 39 0; +#X connect 31 0 39 0; +#X connect 32 0 39 5; +#X connect 33 0 39 4; +#X connect 34 0 39 3; +#X connect 35 0 39 2; +#X connect 36 0 37 0; +#X connect 37 0 39 1; +#X connect 39 0 67 0; +#X connect 40 0 39 0; +#X connect 41 0 39 0; +#X connect 44 0 39 0; +#X connect 45 0 41 0; +#X connect 48 0 53 0; +#X connect 49 0 39 0; +#X connect 50 0 39 0; +#X connect 51 0 52 0; +#X connect 52 0 50 0; +#X connect 53 0 49 0; +#X connect 54 0 31 0; +#X connect 55 0 30 0; +#X connect 56 0 24 0; +#X connect 57 0 29 0; +#X connect 58 0 22 0; +#X connect 59 0 21 0; +#X connect 60 0 17 0; +#X connect 61 0 16 0; diff --git a/ff/ff-gain-help.pd b/ff/ff-gain-help.pd new file mode 100644 index 0000000..e665ec2 --- /dev/null +++ b/ff/ff-gain-help.pd @@ -0,0 +1,17 @@ +#N canvas 607 32 460 364 10; +#X floatatom 123 231 5 0 0 0 - - -; +#X obj 126 205 hsl 195 17 0 1 0 0 empty empty gain 25 8 1 12 -261689 +-1 -1 17460 0; +#X obj 3 3 cnv 15 450 30 empty empty [ff-gain] 4 12 1 18 -261689 -1 +0; +#X obj 123 254 ff-gain 0 0.5; +#X obj 320 326 pddp_open all_about_haptics; +#X text 214 326 For more info:; +#X msg 123 178 0.9; +#X text 24 102 Creation arguments are device number (i.e. 0 for /dev/input/event0) +and gain. This affects all effects for that device.; +#X text 24 64 Sets the overall gain \, between 0 and 1 \, of the force-feedback +joystick on the device.; +#X connect 0 0 3 0; +#X connect 1 0 0 0; +#X connect 6 0 1 0; diff --git a/ff/ff-periodic-help.pd b/ff/ff-periodic-help.pd new file mode 100644 index 0000000..4138369 --- /dev/null +++ b/ff/ff-periodic-help.pd @@ -0,0 +1,154 @@ +#N canvas 360 67 857 641 10; +#X msg 65 474 bang; +#X msg 66 556 stop; +#X msg 453 340 envelope 0 100 0 100; +#X text 85 71 period time in ms (defaults 1000 ms); +#X text 471 361 startlevel attack-duration endlevel decay-duration +; +#X text 188 204 waveform phase in degrees; +#X text 10 458 start the effect; +#X text 15 474 with a; +#X obj 319 573 ff-periodic 0 0 0 0.5; +#X text 505 473 direction in degrees; +#X text 547 511 duration in ms \, 0 is infinite; +#X text 625 554 level \, range = [-1 \, 1]; +#X obj 576 532 hsl 190 17 -1 1 0 0 empty empty level 25 9 1 12 -261689 +-1 -1 0 0; +#X obj 456 453 hsl 175 17 0 360 0 0 empty empty direction 25 9 1 12 +-262131 -1 -1 0 0; +#X obj 491 491 hsl 245 17 0 5000 0 0 empty empty duration 25 9 1 12 +-261681 -1 -1 0 0; +#X obj 453 473 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X obj 488 513 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X obj 573 554 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X text 250 12 -- control a force-feedback periodic force effect; +#X obj 67 507 bng 25 250 50 0 empty empty empty 0 -6 0 8 -24198 -1 +-1; +#X obj 35 507 bng 25 250 50 0 empty empty empty 0 -6 0 8 -258699 -1 +-1; +#X text 228 272 delay before starting; +#X obj 216 316 hsl 200 17 0 5000 0 1 empty empty interval 25 9 1 12 +-225271 -1 -1 2000 0; +#X msg 213 353 interval \$1; +#X msg 175 289 delay \$1; +#X obj 178 253 hsl 200 17 0 5000 0 1 empty empty delay 25 9 1 12 -228992 +-1 -1 1000 0; +#X obj 175 273 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 251.256 256; +#X obj 213 336 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 502.513 256; +#X msg 134 223 phase \$1; +#X obj 137 184 hsl 220 17 0 360 0 1 empty empty phase 25 9 1 12 -257472 +-1 -1 5500 0; +#X obj 134 205 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 90.411 256; +#X msg 90 157 offset \$1; +#X obj 93 117 hsl 220 17 -1 1 0 1 empty empty offset 25 9 1 12 -225280 +-1 -1 13700 0; +#X obj 90 139 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0.251142 256; +#X msg 30 92 period \$1; +#X obj 33 51 hsl 300 17 2 2000 1 1 empty empty period 25 9 1 12 -261681 +-1 -1 22900 0; +#X obj 30 73 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 396.907 256; +#X msg 85 429 waveform \$1; +#X msg 40 262 square; +#X msg 51 282 sine; +#X msg 61 302 triangle; +#X msg 70 322 saw_up; +#X msg 77 342 saw_down; +#X obj 85 375 symbol; +#X text 8 244 waveform shape; +#X symbolatom 85 408 9 0 0 0 current: - -; +#X msg 453 308 envelope \$1 \$2 \$3 \$4; +#X obj 583 229 hsl 150 17 0 1 0 0 empty empty end_level 25 8 1 12 -261689 +-1 -1 0 0; +#X obj 456 187 hsl 150 17 0 1 0 0 empty empty start_level 25 8 1 12 +-261689 -1 -1 0 0; +#X obj 453 270 nbx 3 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X obj 453 287 pack float float float float; +#X obj 580 270 nbx 3 14 -1e+37 1e+37 0 0 pack_bang_env empty empty +0 -6 1 10 -233017 -1 -1 0 256; +#X obj 516 270 nbx 6 14 -1e+37 1e+37 0 0 pack_bang_env empty empty +0 -6 1 10 -233017 -1 -1 1027.59 256; +#X obj 644 270 nbx 6 14 -1e+37 1e+37 0 0 pack_bang_env empty empty +0 -6 1 10 -233017 -1 -1 101.445 256; +#X obj 519 208 hsl 180 17 5 5000 1 1 empty empty attack_duration 25 +9 1 12 -261681 -1 -1 13800 0; +#X obj 647 250 hsl 180 17 5 5000 1 1 empty empty decay_duration 25 +9 1 12 -261681 -1 -1 7800 0; +#X obj 434 270 bng 15 250 50 0 empty pack_bang_env empty 0 -6 0 8 -262144 +-1 -1; +#X obj 3 2 cnv 15 850 30 empty empty [ff-periodic] 15 15 1 18 -261681 +-1 0; +#X text 480 573 <-- arguments are: device# direction duration level +; +#X text 360 596 the outlet gives the effect number on the device or +-1 if it is not loaded.; +#X floatatom 319 603 5 0 0 0 - - -; +#X text 554 107 For more info:; +#X obj 663 106 pddp_open all_about_haptics; +#X text 300 335 minimum time; +#X text 300 350 between triggers; +#X text 422 56 [ff-periodic] generates a periodic force \, basically +a waveform that can be a pulsing or a vibration depending on the "period". +; +#X text 145 139 waveform offset from center [-1 \, 1]; +#X text 199 155 (defaults 0=centered); +#X msg 441 392 load; +#X msg 441 416 unload; +#X text 492 391 a load message \, uploads an effect to the stick; +#X text 491 416 an unload message \, removes it; +#X connect 0 0 8 0; +#X connect 1 0 8 0; +#X connect 2 0 8 0; +#X connect 8 0 60 0; +#X connect 12 0 17 0; +#X connect 13 0 15 0; +#X connect 14 0 16 0; +#X connect 15 0 8 1; +#X connect 16 0 8 2; +#X connect 17 0 8 3; +#X connect 19 0 8 0; +#X connect 20 0 1 0; +#X connect 22 0 27 0; +#X connect 23 0 8 0; +#X connect 24 0 8 0; +#X connect 25 0 26 0; +#X connect 26 0 24 0; +#X connect 27 0 23 0; +#X connect 28 0 8 0; +#X connect 29 0 30 0; +#X connect 30 0 28 0; +#X connect 31 0 8 0; +#X connect 32 0 33 0; +#X connect 33 0 31 0; +#X connect 34 0 8 0; +#X connect 35 0 36 0; +#X connect 36 0 34 0; +#X connect 37 0 8 0; +#X connect 38 0 43 0; +#X connect 39 0 43 0; +#X connect 40 0 43 0; +#X connect 41 0 43 0; +#X connect 42 0 43 0; +#X connect 43 0 45 0; +#X connect 45 0 37 0; +#X connect 46 0 8 0; +#X connect 47 0 51 0; +#X connect 48 0 49 0; +#X connect 49 0 50 0; +#X connect 50 0 46 0; +#X connect 51 0 50 2; +#X connect 52 0 50 1; +#X connect 53 0 50 3; +#X connect 54 0 52 0; +#X connect 55 0 53 0; +#X connect 56 0 50 0; +#X connect 68 0 8 0; +#X connect 69 0 8 0; diff --git a/ff/ff-spring-help.pd b/ff/ff-spring-help.pd new file mode 100644 index 0000000..05888e1 --- /dev/null +++ b/ff/ff-spring-help.pd @@ -0,0 +1,159 @@ +#N canvas 335 29 859 661 10; +#X text 334 195 coefficients that determine how fast; +#X text 334 207 the effect increases in that direction; +#X text 334 218 range = [-1 \, 1]; +#X text 386 301 width of the dead-zone \, where there is no effect +; +#X text 386 326 range is (like the joystick output) [-32768 \, 32767] +; +#X text 387 312 one for each axis; +#X text 393 424 position of the dead-zone in the joystick range; +#X text 393 436 one for each axis; +#X text 393 448 range is also [-32768 \, 32767]; +#X text 366 526 duration in ms \, 0 is infinite; +#X text 531 555 levels \, range = [0 \, 1]; +#X text 398 74 these effects set up 2 axis (x and y) and you specify +the parameters for each direction.; +#X obj 474 556 hsl 40 15 0 1 0 0 empty empty down 5 8 1 11 -261689 +-1 -1 0 0; +#X obj 420 556 hsl 40 15 0 1 0 0 empty empty up 5 8 1 11 -261689 -1 +-1 0 0; +#X obj 367 556 hsl 40 15 0 1 0 0 empty empty left 5 8 1 11 -261689 +-1 -1 0 0; +#X obj 313 556 hsl 40 15 0 1 0 0 empty empty right 5 8 1 11 -261689 +-1 -1 0 0; +#X msg 297 483 center-y \$1; +#X msg 292 429 center-x \$1; +#X obj 300 450 hsl 75 15 -32767 32767 0 1 empty empty center-x 5 8 +1 11 -228992 -1 -1 5958 0; +#X obj 295 396 hsl 75 15 -32767 32767 0 1 empty empty center-x 5 8 +1 11 -228992 -1 -1 3700 0; +#X obj 285 340 hsl 75 15 -32767 32767 0 1 empty empty deadband-y 5 +8 1 11 -225280 -1 -1 3734 0; +#X msg 282 373 deadband-y \$1; +#X msg 279 317 deadband-x \$1; +#X obj 282 284 hsl 75 15 -32767 32767 0 1 empty empty deadband-x 5 +8 1 11 -225280 -1 -1 3836 0; +#X msg 226 258 down-coeff \$1; +#X obj 229 225 hsl 90 15 -1 1 0 1 empty empty down-coeff 5 8 1 11 -257472 +-1 -1 0 0; +#X obj 223 170 hsl 90 15 -1 1 0 1 empty empty up-coeff 5 7 1 11 -257472 +-1 -1 1335 0; +#X obj 120 225 hsl 90 15 -1 1 0 0 empty empty left-coeff 5 7 1 11 -257472 +-1 -1 0 0; +#X obj 103 170 hsl 90 15 -1 1 0 1 empty empty right-coeff 5 7 1 10 +-257472 -1 -1 8900 0; +#X msg 220 203 up-coeff \$1; +#X msg 117 258 left-coeff \$1; +#X msg 100 203 right-coeff \$1; +#X obj 471 575 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X obj 417 575 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X obj 364 575 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X obj 310 575 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X obj 312 505 hsl 245 17 0 5000 0 0 empty empty duration 25 9 1 12 +-261681 -1 -1 0 0; +#X obj 309 527 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X text 213 14 -- control force-feedback spring effect; +#X obj 195 599 ff-spring 0 2000 0.5 0.5 0 0.1; +#X msg 93 503 bang; +#X msg 84 582 stop; +#X text 32 483 start the effect; +#X text 37 499 with a; +#X obj 85 533 bng 25 250 50 0 empty empty empty 0 -6 0 8 -24198 -1 +-1; +#X obj 53 533 bng 25 250 50 0 empty empty empty 0 -6 0 8 -258699 -1 +-1; +#X text 141 140 delay before starting; +#X text 111 69 minimum time between triggers; +#X obj 24 43 hsl 290 17 0 5000 0 1 empty empty interval 25 9 1 12 -225271 +-1 -1 2890 0; +#X msg 21 80 interval \$1; +#X msg 60 144 delay \$1; +#X obj 63 108 hsl 230 17 0 5000 0 1 empty empty delay 25 9 1 12 -228992 +-1 -1 1145 0; +#X obj 60 128 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 250 256; +#X obj 21 63 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 500 256; +#X obj 100 187 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 1 256; +#X obj 117 242 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X obj 226 242 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 -1 256; +#X obj 220 187 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 -0.7 256; +#X obj 279 301 nbx 6 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 1204.41 256; +#X obj 282 357 nbx 6 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 301.102 256; +#X obj 292 413 nbx 6 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 0 256; +#X obj 297 467 nbx 6 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 1 10 +-233017 -1 -1 19996.7 256; +#X obj 3 3 cnv 15 850 30 empty empty [ff-spring] 15 15 1 18 -257472 +-1 0; +#X obj 568 135 pddp_open all_about_haptics; +#X text 456 134 For more info:; +#X text 415 599 arguments are: device# duration right- left- up- down-level +; +#X text 238 619 the outlet gives the effect number on the device or +-1 if it is not loaded.; +#X floatatom 195 624 5 0 0 0 - - -; +#X text 397 58 [ff-spring] is a "conditional effect".; +#X text 396 110 [ff-friction] has the same methods; +#X msg 36 387 load; +#X msg 73 387 unload; +#X text 9 369 load/unload effect; +#X connect 12 0 32 0; +#X connect 13 0 33 0; +#X connect 14 0 34 0; +#X connect 15 0 35 0; +#X connect 16 0 39 0; +#X connect 17 0 39 0; +#X connect 18 0 61 0; +#X connect 19 0 60 0; +#X connect 20 0 59 0; +#X connect 21 0 39 0; +#X connect 22 0 39 0; +#X connect 23 0 58 0; +#X connect 24 0 39 0; +#X connect 25 0 56 0; +#X connect 26 0 57 0; +#X connect 27 0 55 0; +#X connect 28 0 54 0; +#X connect 29 0 39 0; +#X connect 30 0 39 0; +#X connect 31 0 39 0; +#X connect 32 0 39 5; +#X connect 33 0 39 4; +#X connect 34 0 39 3; +#X connect 35 0 39 2; +#X connect 36 0 37 0; +#X connect 37 0 39 1; +#X connect 39 0 67 0; +#X connect 40 0 39 0; +#X connect 41 0 39 0; +#X connect 44 0 39 0; +#X connect 45 0 41 0; +#X connect 48 0 53 0; +#X connect 49 0 39 0; +#X connect 50 0 39 0; +#X connect 51 0 52 0; +#X connect 52 0 50 0; +#X connect 53 0 49 0; +#X connect 54 0 31 0; +#X connect 55 0 30 0; +#X connect 56 0 24 0; +#X connect 57 0 29 0; +#X connect 58 0 22 0; +#X connect 59 0 21 0; +#X connect 60 0 17 0; +#X connect 61 0 16 0; +#X connect 70 0 39 0; +#X connect 71 0 39 0; @@ -0,0 +1,1528 @@ +/* forcefeedback externals for linux pd + * copyright 2003 Gerard van Dongen gml@xs4all.nl + +* 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. + + * the api is unstable. read the README for details. + + * objects: + * ff-constant [device direction duration level ] + * ff-periodic [device direction duration level ] + * ff-spring [device duration right-levelx left-level-x right-levely left-level-y] + * ff-friction [device duration right-levelx left-level-x right-levely left-level-y] + + * additional methods for all objects: + * bang :starts + * stop :stops an effect + * delay :sets the delay before the effect is activated + * interval :sets the time to wait before an effect can be re-activated + + * methods for periodic effects + * waveform : square|triangle|sine|saw_up|saw_down + * period : period time in ms + * offset : + * phase : + * + * methods for constant and periodic effects: + * envelope : start-level attack end-level decay + * + * methods for spring and friction effects: + * right-coeff : + * left-coeff : + * deadband : + * center : + * + */ + + + + +#include "m_pd.h" + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <string.h> +#include <stdio.h> +#include "input.h" + + + +#define FF_DEVICE "/dev/input/event0" + +#define BITS_PER_LONG (sizeof(long) * 8) +#define OFF(x) ((x)%BITS_PER_LONG) +#define BIT(x) (1UL<<OFF(x)) +#define LONG(x) ((x)/BITS_PER_LONG) +#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) + + +static t_class *ffConstant_class; +static t_class *ffPeriodic_class; +static t_class *ffFriction_class; +static t_class *ffSpring_class; +static t_class *ffGain_class; +static t_class *ffAutocenter_class; + + +typedef struct _ff_device { + char name[64]; + int max_fx; + int loaded_fx; +} t_ff_device; + +static t_ff_device ff_dev[4]; + + + + +typedef struct _ff { + t_object x_obj; + int ff_fd; + struct ff_effect effects; + struct input_event do_that; + unsigned int device; +} t_ff; + + +struct _waveshapes { + char* wave; + unsigned short number; + } waves[]={{"square",FF_SQUARE}, + {"triangle",FF_TRIANGLE}, + {"sine",FF_SINE}, + {"saw_up",FF_SAW_UP}, + {"saw_down",FF_SAW_DOWN}, + {NULL,0}}; + +/******************************************************************************************************** + +general ff methods + +*********************************************************************************************************/ + +void ff_bang(t_ff *x) +{ + if (x->ff_fd < 0) return; + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + x->do_that.type = EV_FF; + x->do_that.code = x->effects.id; + x->do_that.value = 1; + + if (write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that)) == -1) { + perror("Play effect error"); + } + outlet_float(x->x_obj.ob_outlet, (t_float) x->effects.id); +} + +void ff_stop(t_ff *x) +{ + + if (x->ff_fd < 0) return; + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + /* is it still playing ? */ + + x->do_that.type = EV_FF_STATUS; + x->do_that.code = x->effects.id; + + if ((read(x->ff_fd, (void *) &x->do_that, sizeof(x->do_that))) == -1) { + perror("couldn't read status of effect"); + } + + if (x->do_that.value == FF_STATUS_PLAYING) { + + x->do_that.type = EV_FF; + x->do_that.code = x->effects.id; + x->do_that.value = 0; + + if ((write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that))) == -1) { + perror("Stop effect error"); + } + } +} + +void ff_delay(t_ff *x, t_floatarg delay) +{ + if (x->ff_fd < 0) return; + x->effects.replay.delay = (unsigned short) delay; + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } +} + +void ff_interval(t_ff *x, t_floatarg interval) +{ + if (x->ff_fd < 0) return; + x->effects.trigger.interval = (unsigned short) interval; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } +} + + + +void ff_duration(t_ff *x, t_floatarg duration ) +{ + if (x->ff_fd < 0) return; + + x->effects.replay.length = (unsigned short) duration; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } +} + +void ff_direction(t_ff *x, t_floatarg direction) +{ + if (x->ff_fd < 0) return; + unsigned short shortdirection; + shortdirection = (unsigned short)(direction * 182.044444); /*map degrees to 0-0xFFFF */ + + x->effects.direction = shortdirection; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + error("Upload effects error"); + } +} + +void ff_unload(t_ff *x) +{ + + if (x->ff_fd < 0) return; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + ff_stop(x); + + /* delete effect from the stick */ + if (ioctl(x->ff_fd, EVIOCRMFF, x->effects.id) == -1) { + perror("deleting effect"); + return; + } + + x->effects.id = -1; + ff_dev[x->device].loaded_fx = (ff_dev[x->device].loaded_fx == 0 ? 0 : --ff_dev[x->device].loaded_fx); + outlet_float(x->x_obj.ob_outlet, (t_float) x->effects.id); + +} + +void ff_load(t_ff *x) +{ + + if (x->ff_fd < 0) return; + if (x->effects.id != -1) { + post("effect is allready loaded"); + return; + } + + if (ff_dev[x->device].loaded_fx == ff_dev[x->device].max_fx) { + post("maximum number of fx is loaded, you have to unload one first"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + return; + } + ff_dev[x->device].loaded_fx++; + outlet_float(x->x_obj.ob_outlet, (t_float) x->effects.id); +} + +void ff_free(t_ff *x) +{ + + if (x->ff_fd < 0) return; + + if (x->effects.id != -1) { + + /* stop effect */ + ff_stop(x); + + /* delete effect from the stick */ + ff_unload(x); + } + + /* close device */ + + close(x->ff_fd); + +} + + + + + +/******************************************************************************************************** + +ff-constant methods + +*********************************************************************************************************/ + + +void ffConstant_level(t_ff *x, t_floatarg level) +{ + if (x->ff_fd < 0) return; + + short shortlevel; + level = (level > 1 ? 1:level); + level = (level < -1 ? -1:level); + shortlevel = (short) (level * 32767 ); /*map level -1 to 1 to signed short range */ + + x->effects.u.constant.level = shortlevel; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("level update error:"); + } +} + + +void ffConstant_envelope(t_ff *x, t_floatarg startlevel, t_floatarg startduration, t_floatarg endlevel, t_floatarg endduration) +{ + if (x->ff_fd < 0) return; + unsigned short shortattack,shortdecay; + startlevel = (startlevel > 1 ? 1:startlevel); + startlevel = (startlevel < 0 ? 0:startlevel); + endlevel = (endlevel > 1 ? 1:endlevel); + endlevel = (endlevel < 0 ? 0:endlevel); + shortattack = (unsigned short) (startlevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + shortdecay = (unsigned short) (endlevel * 65534); + x->effects.u.constant.envelope.attack_level = shortattack; + x->effects.u.constant.envelope.fade_level = shortdecay; + x->effects.u.constant.envelope.attack_length = (unsigned short) startduration; + x->effects.u.constant.envelope.fade_length = (unsigned short) endduration; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } + +} + + +void *ffConstant_new(t_floatarg device,t_floatarg direction,t_floatarg duration, t_floatarg level) +{ + + unsigned short shortdirection,shortduration; + short shortlevel; + unsigned long features[4]; + int device_number; + + t_ff *x = (t_ff *)pd_new(ffConstant_class); + + device = (device > 4 ? 4:device); + device_number= (int)(device < 0 ? 0:device); + + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("direction")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("duration")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("level")); + + outlet_new(&x->x_obj, &s_float); + + if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0) { + error("ff-lib:couldn't open %s, no effect will happen",ff_dev[device_number].name); + return (void *) x; + } + + if ((ioctl(x->ff_fd, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features)) == -1) { + perror("\nCouldn't determine available ff-effects \n FF probably won't work"); + close(x->ff_fd); + return (void *) x; + } + + if (!test_bit(FF_CONSTANT, features)) { + error("Constant force effect doesn't seem to be supported\n" + "the external won't do anything"); + close(x->ff_fd); + x->ff_fd = -1; + return (void *) x; + } + + + shortdirection = (unsigned short)(direction * 182.044444); /*map degrees to 0-0xFFFF */ + shortduration = (unsigned short) duration; + level = (level > 1 ? 1:level); + level = (level < -1 ? -1:level); + shortlevel = (short) (level * 32767 ); /*map level -1 to 1 to signed short range */ + + x->effects.type = FF_CONSTANT; + x->effects.id = -1; + x->effects.direction = shortdirection; + x->effects.u.constant.level =shortlevel; + x->effects.u.constant.envelope.attack_length = 0x000; + x->effects.u.constant.envelope.attack_level = 0; + x->effects.u.constant.envelope.fade_length = 0x000; + x->effects.u.constant.envelope.fade_level = 0; + x->effects.trigger.button = 0; + x->effects.trigger.interval = 0; + x->effects.replay.length =shortduration; + x->effects.replay.delay = 0; + x->device = device_number; + ff_load(x); + return (void*)x; +} + + + +/******************************************************************************************************** + +ff-periodic methods + +*********************************************************************************************************/ + + + + +void ffPeriodic_level(t_ff *x, t_floatarg level) +{ + if (x->ff_fd < 0) return; + short shortlevel; + level = (level > 1 ? 1:level); + level = (level < -1 ? -1:level); + shortlevel = (short) (level * 32767 ); /*map level -1 to 1 to signed short range */ + x->effects.u.periodic.magnitude = shortlevel; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } +} + + +void ffPeriodic_waveform(t_ff *x, t_symbol* waveform) +{ + if (x->ff_fd < 0) return; + unsigned short shortwave = 0; + int n = 0; + + while (waves[n].wave) { + if (strcmp( waveform->s_name,waves[n].wave)) shortwave = waves[n].number; + n++; + } + + x->effects.u.periodic.waveform = shortwave; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } + +} + +void ffPeriodic_period(t_ff *x, t_floatarg period) +{ + if (x->ff_fd < 0) return; + x->effects.u.periodic.period = (unsigned short) period; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } +} + +void ffPeriodic_offset(t_ff *x, t_floatarg offset) +{ + if (x->ff_fd < 0) return; + short shortoffset; + offset = (offset > 1 ? 1:offset); + offset = (offset < -1 ? -1:offset); + shortoffset = (short) (offset * 32767 ); /*map level -1 to 1 to signed short range */ + x->effects.u.periodic.offset = shortoffset; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } +} + +void ffPeriodic_phase(t_ff *x, t_floatarg phase) +{ + if (x->ff_fd < 0) return; + unsigned short shortphase; + shortphase = (unsigned short)(phase * 182.044444); /*map degrees to 0-0xFFFF */ + x->effects.u.periodic.phase = shortphase; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + error("Upload effects error"); + } + +} + +void ffPeriodic_envelope(t_ff *x, t_floatarg startlevel, t_floatarg startduration, t_floatarg endlevel, t_floatarg endduration) +{ + if (x->ff_fd < 0) return; + unsigned short shortattack,shortdecay; + startlevel = (startlevel > 1 ? 1:startlevel); + startlevel = (startlevel < 0 ? 0:startlevel); + endlevel = (endlevel > 1 ? 1:endlevel); + endlevel = (endlevel < 0 ? 0:endlevel); + shortattack = (unsigned short) (startlevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + shortdecay = (unsigned short) (endlevel * 65534); + x->effects.u.periodic.envelope.attack_level = shortattack; + x->effects.u.periodic.envelope.fade_level = shortdecay; + x->effects.u.periodic.envelope.attack_length = (unsigned short) startduration; + x->effects.u.periodic.envelope.fade_length = (unsigned short) endduration; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } + +} + + + + +void *ffPeriodic_new(t_floatarg device,t_floatarg direction,t_floatarg duration, t_floatarg level) +{ + unsigned short shortdirection,shortduration; + short shortlevel; + unsigned long features[4]; + int device_number; + + t_ff *x = (t_ff *)pd_new(ffPeriodic_class); + + device = (device > 4 ? 4:device); + device_number= (int)(device < 0 ? 0:device); + + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("direction")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("duration")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("level")); + + outlet_new(&x->x_obj, &s_float); + + if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0 ){ + error("ff-lib:couldn't open %s, no effect will happen",ff_dev[device_number].name); + return (void *) x; + } + + if ((ioctl(x->ff_fd, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features)) == -1) { + error("Couldn't determine available ff-effects \n FF probably won't work"); + close(x->ff_fd); + return (void *) x; + } + + if (!test_bit(FF_PERIODIC, features)) { + error("Periodic effect doesn't seem to be supported\n" + "the external won't do anything"); + close(x->ff_fd); + x->ff_fd = -1; + return (void *) x; + } + + shortdirection = (unsigned short)(direction * 182.044444); /*map degrees to 0-0xFFFF */ + shortduration = (unsigned short) duration; + level = (level > 1 ? 1:level); + level = (level < -1 ? -1:level); + shortlevel = (short) (level * 32767 ); /*map level -1 to 1 to signed short range */ + + x->effects.type = FF_PERIODIC; + x->effects.id = -1; + x->effects.direction = shortdirection; + x->effects.u.periodic.waveform = FF_SQUARE; + x->effects.u.periodic.period = 1000; + x->effects.u.periodic.magnitude = shortlevel; + x->effects.u.periodic.offset = 0; + x->effects.u.periodic.phase = 0; + x->effects.u.periodic.envelope.attack_length = 0x000; + x->effects.u.periodic.envelope.attack_level = 0; + x->effects.u.periodic.envelope.fade_length = 0x000; + x->effects.u.periodic.envelope.fade_level = 0; + x->effects.trigger.button = 0; + x->effects.trigger.interval = 0; + x->effects.replay.length = shortduration; + x->effects.replay.delay = 0; + x->device = device_number; + + ff_load(x); + + return (void*)x; +} + + + +/******************************************************************************************************** + +ff-spring and ff-friction methods + +*********************************************************************************************************/ + + +void ffCondition_setLevel(t_ff *x, t_floatarg level, int axis) +{ + unsigned short shortlevel; + + + if (x->ff_fd < 0) return; + + level = (level > 1 ? 1:level); + level = (level < 0 ? 0:level); + shortlevel = (unsigned short) (level * 65534 ); /*map level 0 to 1 to unsigned short range */ + + switch (axis) { + case 0: x->effects.u.condition[0].right_saturation = shortlevel; + break; + case 1: x->effects.u.condition[0].left_saturation = shortlevel; + break; + case 2: x->effects.u.condition[1].right_saturation = shortlevel; + break; + case 3: x->effects.u.condition[1].left_saturation = shortlevel; + break; + } + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } + + +} + + + +void ffCondition_setCoeff(t_ff *x, t_floatarg coeff, int axis) +{ + short shortcoeff; + if (x->ff_fd < 0) return; + + coeff = (coeff > 1 ? 1:coeff); + coeff = (coeff < -1 ? -1:coeff); + shortcoeff = (short) (coeff * 32767 ); /*map level -1 to 1 to unsigned short range */ + switch (axis) { + case 0: x->effects.u.condition[0].right_coeff = shortcoeff; + break; + + case 1: x->effects.u.condition[0].left_coeff = shortcoeff; + break; + + case 2: x->effects.u.condition[1].right_coeff = shortcoeff; + break; + + + case 3: x->effects.u.condition[1].left_coeff = shortcoeff; + break; + } + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } + +} + + +void ffCondition_deadband(t_ff *x, t_floatarg deadband, int axis) +{ + if (x->ff_fd < 0) return; + x->effects.u.condition[axis].deadband = (unsigned short)deadband; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } + +} + +void ffCondition_center(t_ff *x, t_floatarg center, int axis) +{ + if (x->ff_fd < 0) return; + x->effects.u.condition[axis].center = (short)center; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } + + +} + +void ffCondition_rightLevel(t_ff *x, t_floatarg rightLevel) +{ + + ffCondition_setLevel(x,rightLevel,0); + +} + +void ffCondition_leftLevel(t_ff *x, t_floatarg leftLevel) +{ + ffCondition_setLevel(x,leftLevel,1); + +} +void ffCondition_upLevel(t_ff *x, t_floatarg upLevel) +{ + ffCondition_setLevel(x,upLevel,3); + +} + +void ffCondition_downLevel(t_ff *x, t_floatarg downLevel) +{ + ffCondition_setLevel(x,downLevel,2); + +} + +void ffCondition_rightCoeff(t_ff *x, t_floatarg rightCoeff) +{ + ffCondition_setCoeff(x,rightCoeff,0); +} + +void ffCondition_leftCoeff(t_ff *x, t_floatarg leftCoeff) +{ + ffCondition_setCoeff(x,leftCoeff,1); +} +void ffCondition_upCoeff(t_ff *x, t_floatarg upCoeff) +{ + ffCondition_setCoeff(x,upCoeff,3); +} + +void ffCondition_downCoeff(t_ff *x, t_floatarg downCoeff) +{ + ffCondition_setCoeff(x,downCoeff,2); +} + + +void ffCondition_deadbandx(t_ff *x, t_floatarg deadband) +{ + ffCondition_deadband(x,deadband,0); +} + +void ffCondition_deadbandy(t_ff *x, t_floatarg deadband) +{ + ffCondition_deadband(x,deadband,1); +} + + +void ffCondition_centerx(t_ff *x, t_floatarg center) +{ + ffCondition_center(x,center,0); +} + + +void ffCondition_centery(t_ff *x, t_floatarg center) +{ + ffCondition_center(x,center,1); +} + + + +void *ffFriction_new(t_floatarg device,t_floatarg duration, t_floatarg rightLevel, t_floatarg leftLevel, + t_floatarg upLevel, t_floatarg downLevel) +{ + unsigned short shortduration,shortrightLevel,shortleftLevel,shortupLevel,shortdownLevel; + unsigned long features[4]; + int device_number; + + t_ff *x = (t_ff *)pd_new(ffFriction_class); + + device = (device > 4 ? 4:device); + device_number= (int)(device < 0 ? 0:device); + + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("duration")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("right-level")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("left-level")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("up-level")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("down-level")); + + outlet_new(&x->x_obj, &s_float); + + if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0) { + error("ff-lib:couldn't open %s, no effect will happen",ff_dev[device_number].name); + return (void *) x; + } + + if ((ioctl(x->ff_fd, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features)) == -1) { + error("Couldn't determine available ff-effects \n FF probably won't work"); + close(x->ff_fd); + return (void *) x; + } + + if (!test_bit(FF_FRICTION, features)) { + error("Friction effect doesn't seem to be supported\n" + "the external won't do anything"); + close(x->ff_fd); + x->ff_fd = -1; + return (void *) x; + } + + + shortduration = (unsigned short) duration; + + rightLevel = (rightLevel > 1 ? 1:rightLevel); + rightLevel = (rightLevel < 0 ? 0:rightLevel); + shortrightLevel = (unsigned short) (rightLevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + + leftLevel = (leftLevel > 1 ? 1:leftLevel); + leftLevel = (leftLevel < 0 ? 0:leftLevel); + shortleftLevel = (unsigned short) (leftLevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + + upLevel = (upLevel > 1 ? 1:upLevel); + upLevel = (upLevel < 0 ? 0:upLevel); + shortupLevel = (unsigned short) (upLevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + downLevel = (downLevel > 1 ? 1:downLevel); + downLevel = (downLevel < 0 ? 0:downLevel); + shortdownLevel = (unsigned short) (downLevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + + + x->effects.type = FF_FRICTION; + x->effects.id = -1; + x->effects.u.condition[0].right_saturation = shortrightLevel; + x->effects.u.condition[0].left_saturation = shortleftLevel; + x->effects.u.condition[0].right_coeff = 0x8000; + x->effects.u.condition[0].left_coeff = 0x8000; + x->effects.u.condition[0].deadband = 0; + x->effects.u.condition[0].center = 0; + x->effects.u.condition[1].right_saturation = shortdownLevel; + x->effects.u.condition[1].left_saturation = shortupLevel; + x->effects.u.condition[1].right_coeff = 0x8000; + x->effects.u.condition[1].left_coeff = 0x8000; + x->effects.u.condition[1].deadband = 0; + x->effects.u.condition[1].center = 0; + x->effects.trigger.button = 0; + x->effects.trigger.interval = 0; + x->effects.replay.length = shortduration; + x->effects.replay.delay = 0; + x->device = device_number; + + + ff_load(x); + + return (void*)x; +} + +void *ffSpring_new(t_floatarg device,t_floatarg duration, t_floatarg rightLevel, t_floatarg leftLevel, + t_floatarg upLevel, t_floatarg downLevel) +{ + unsigned short shortduration,shortrightLevel,shortleftLevel,shortupLevel,shortdownLevel; + unsigned long features[4]; + int device_number; + + t_ff *x = (t_ff *)pd_new(ffFriction_class); + + device = (device > 4 ? 4:device); + device_number= (int)(device < 0 ? 0:device); + + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("duration")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("right-level")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("left-level")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("up-level")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("down-level")); + + outlet_new(&x->x_obj, &s_float); + + if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0) { + error("ff-lib:couldn't open %s, no effect will happen",ff_dev[device_number].name); + return (void *) x; + } + + if ((ioctl(x->ff_fd, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features)) == -1) { + error("Couldn't determine available ff-effects \n FF probably won't work"); + close(x->ff_fd); + return (void *) x; + } + + if (!test_bit(FF_SPRING, features)) { + error("Spring effect doesn't seem to be supported\n" + "the external won't do anything"); + close(x->ff_fd); + x->ff_fd = -1; + return (void *) x; + } + + shortduration = (unsigned short) duration; + + leftLevel = (leftLevel > 1 ? 1:leftLevel); + leftLevel = (leftLevel < 0 ? 0:leftLevel); + shortleftLevel = (unsigned short) (leftLevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + rightLevel = (rightLevel > 1 ? 1:rightLevel); + rightLevel = (rightLevel < 0 ? 0:rightLevel); + shortrightLevel = (unsigned short) (rightLevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + upLevel = (upLevel > 1 ? 1:upLevel); + upLevel = (upLevel < 0 ? 0:upLevel); + shortupLevel = (unsigned short) (upLevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + downLevel = (downLevel > 1 ? 1:downLevel); + downLevel = (downLevel < 0 ? 0:downLevel); + shortdownLevel = (unsigned short) (downLevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + + + x->effects.type = FF_SPRING; + x->effects.id = -1; + x->effects.u.condition[0].right_saturation = shortrightLevel; + x->effects.u.condition[0].left_saturation = shortleftLevel; + x->effects.u.condition[0].right_coeff = 0x8000; + x->effects.u.condition[0].left_coeff = 0x8000; + x->effects.u.condition[0].deadband = 0; + x->effects.u.condition[0].center = 0; + x->effects.u.condition[1].right_saturation = shortdownLevel; + x->effects.u.condition[1].left_saturation = shortupLevel; + x->effects.u.condition[1].right_coeff = 0x8000; + x->effects.u.condition[1].left_coeff = 0x8000; + x->effects.u.condition[1].deadband = 0; + x->effects.u.condition[1].center = 0; + x->effects.trigger.button = 0; + x->effects.trigger.interval = 0; + x->effects.replay.length = shortduration; + x->effects.replay.delay = 0; + x->device = device_number; + + ff_load(x); + + return (void*)x; +} + +/******************************************************************************************************** + +ff-gain methods + +*********************************************************************************************************/ + + +void ffGain_set(t_ff *x, t_floatarg gain) +{ + gain = (gain > 1 ? 1:gain); + gain = (gain < 0 ? 0:gain); + + x->do_that.type = EV_FF; + x->do_that.code = FF_GAIN; + x->do_that.value = (unsigned int)(65536.0 * gain); + if (x->ff_fd > 0) + if ((write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that))) == -1) + error("ff-lib: couldn't set gain"); + +} + + + + + +void *ffGain_new(t_floatarg device,t_floatarg gain) +{ + int device_number; + unsigned short shortgain; + t_ff *x = (t_ff *)pd_new(ffGain_class); + + device = (device > 4 ? 4:device); + device_number= (int)(device < 0 ? 0:device); + + gain = (gain > 1 ? 1:gain); + gain = (gain < 0 ? 0:gain); + shortgain = (unsigned short) (gain * 65536); + if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0) { + error("ff-lib:couldn't open %s, no effect will happen",ff_dev[device_number].name); + return (void *) x; + } + x->do_that.type = EV_FF; + x->do_that.code = FF_GAIN; + x->do_that.value = shortgain; + + if ((write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that))) == -1) + error("ff-lib: couldn't set gain"); + return (void*)x; +} + + +/******************************************************************************************************** + +ff-autocenter methods + +*********************************************************************************************************/ + +void ffAutocenter_set(t_ff *x, t_floatarg autocenter) +{ + + autocenter = (autocenter > 1 ? 1:autocenter); + autocenter = (autocenter < -1 ? -1:autocenter); + + x->do_that.type = EV_FF; + x->do_that.code = FF_AUTOCENTER; + x->do_that.value = (short)(32767.0 * autocenter); + if (x->ff_fd > 0) + if ((write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that))) == -1) + error("ff-lib:couldn't set autocenter"); + +} + + +void *ffAutocenter_new(t_floatarg device,t_floatarg autocenter) +{ + int device_number; + t_ff *x = (t_ff *)pd_new(ffAutocenter_class); + device = (device > 4 ? 4:device); + device_number= (int)(device < 0 ? 0:device); + + autocenter = (autocenter > 1 ? 1:autocenter); + autocenter = (autocenter < 0 ? 0:autocenter); + + if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0) { + error("ff-lib:couldn't open %s, no effect will happen", ff_dev[device_number].name); + return (void *) x; + } + x->do_that.type = EV_FF; + x->do_that.code = FF_AUTOCENTER; + x->do_that.value = (short)(32767.0 * autocenter ); + + if ((write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that))) == -1) + error("ff-lib:couldn't set autocenter"); + return (void *) x; + +} + + + + + + + + +/****************************************************************************************** +initialisation functions +*******************************************************************************************/ + +void add_general_ff_methods(t_class* ff_class) +{ + class_addbang(ff_class,ff_bang); + class_addmethod(ff_class, (t_method)ff_stop,gensym("stop"),0); + class_addmethod(ff_class, (t_method)ff_duration,gensym("duration"),A_DEFFLOAT,0); + class_addmethod(ff_class, (t_method)ff_interval,gensym("interval"),A_DEFFLOAT,0); + class_addmethod(ff_class, (t_method)ff_delay,gensym("delay"),A_DEFFLOAT,0); + class_addmethod(ff_class, (t_method)ff_load,gensym("load"),0); + class_addmethod(ff_class, (t_method)ff_unload,gensym("unload"),0); +} + +void init_ffConstant(void) +{ + + ffConstant_class = class_new(gensym("ff-constant"), + (t_newmethod)ffConstant_new, + (t_method)ff_free, + sizeof(t_ff), + CLASS_DEFAULT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + 0); + + add_general_ff_methods(ffConstant_class); + class_addmethod(ffConstant_class, + (t_method)ff_direction, + gensym("direction"), + A_DEFFLOAT, + 0); + class_addmethod(ffConstant_class, + (t_method)ffConstant_level, + gensym("level"), + A_DEFFLOAT, + 0); + class_addmethod(ffConstant_class, + (t_method)ffConstant_envelope, + gensym("envelope"), + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + 0); + +} + +void init_ffPeriodic(void) +{ + ffPeriodic_class = class_new(gensym("ff-periodic"), + (t_newmethod)ffPeriodic_new, + (t_method)ff_free, + sizeof(t_ff), + CLASS_DEFAULT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + 0); + add_general_ff_methods(ffPeriodic_class); + class_addmethod(ffPeriodic_class, + (t_method)ff_direction, + gensym("direction"), + A_DEFFLOAT, + 0); + class_addmethod(ffPeriodic_class, + (t_method)ffPeriodic_level, + gensym("level"), + A_DEFFLOAT, + 0); + class_addmethod(ffPeriodic_class, + (t_method)ffPeriodic_envelope, + gensym("envelope"), + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + 0); + class_addmethod(ffPeriodic_class, + (t_method)ffPeriodic_waveform, + gensym("waveform"), + A_DEFSYMBOL, + 0); + class_addmethod(ffPeriodic_class, + (t_method)ffPeriodic_period, + gensym("period"), + A_DEFFLOAT, + 0); + class_addmethod(ffPeriodic_class, + (t_method)ffPeriodic_offset, + gensym("offset"), + A_DEFFLOAT, + 0); + class_addmethod(ffPeriodic_class, + (t_method)ffPeriodic_phase, + gensym("phase"), + A_DEFFLOAT, + 0); + + + + +} + +void init_ffSpring(void) +{ + ffSpring_class = class_new(gensym("ff-spring"), + (t_newmethod)ffSpring_new, + (t_method)ff_free, + sizeof(t_ff), + CLASS_DEFAULT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + 0); + add_general_ff_methods(ffSpring_class); + + class_addmethod(ffSpring_class, + (t_method)ffCondition_rightLevel, + gensym("right-level"), + A_DEFFLOAT, + 0); + class_addmethod(ffSpring_class, + (t_method)ffCondition_leftLevel, + gensym("left-level"), + A_DEFFLOAT, + 0); + class_addmethod(ffSpring_class, + (t_method)ffCondition_upLevel, + gensym("up-level"), + A_DEFFLOAT, + 0); + class_addmethod(ffSpring_class, + (t_method)ffCondition_downLevel, + gensym("down-level"), + A_DEFFLOAT, + 0); + + + class_addmethod(ffSpring_class, + (t_method)ffCondition_rightCoeff, + gensym("right-coeff"), + A_DEFFLOAT, + 0); + class_addmethod(ffSpring_class, + (t_method)ffCondition_leftCoeff, + gensym("left-coeff"), + A_DEFFLOAT, + 0); + class_addmethod(ffSpring_class, + (t_method)ffCondition_upCoeff, + gensym("up-coeff"), + A_DEFFLOAT, + 0); + class_addmethod(ffSpring_class, + (t_method)ffCondition_downCoeff, + gensym("down-coeff"), + A_DEFFLOAT, + 0); + + class_addmethod(ffSpring_class, + (t_method)ffCondition_deadbandx, + gensym("deadband-x"), + A_DEFFLOAT, + 0); + class_addmethod(ffSpring_class, + (t_method)ffCondition_deadbandy, + gensym("deadband-y"), + A_DEFFLOAT, + 0); + + class_addmethod(ffSpring_class, + (t_method)ffCondition_centerx, + gensym("center-x"), + A_DEFFLOAT, + 0); + class_addmethod(ffSpring_class, + (t_method)ffCondition_centery, + gensym("center-y"), + A_DEFFLOAT, + 0); + + +} + +void init_ffFriction(void) +{ + ffFriction_class = class_new(gensym("ff-friction"), + (t_newmethod)ffFriction_new, + (t_method)ff_free, + sizeof(t_ff), + CLASS_DEFAULT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + 0); + add_general_ff_methods(ffFriction_class); + + class_addmethod(ffFriction_class, + (t_method)ffCondition_rightLevel, + gensym("right-level"), + A_DEFFLOAT, + 0); + class_addmethod(ffFriction_class, + (t_method)ffCondition_leftLevel, + gensym("left-level"), + A_DEFFLOAT, + 0); + class_addmethod(ffFriction_class, + (t_method)ffCondition_upLevel, + gensym("up-level"), + A_DEFFLOAT, + 0); + class_addmethod(ffFriction_class, + (t_method)ffCondition_downLevel, + gensym("down-level"), + A_DEFFLOAT, + 0); + + class_addmethod(ffFriction_class, + (t_method)ffCondition_rightCoeff, + gensym("right-coeff"), + A_DEFFLOAT, + 0); + class_addmethod(ffFriction_class, + (t_method)ffCondition_leftCoeff, + gensym("left-coeff"), + A_DEFFLOAT, + 0); + class_addmethod(ffFriction_class, + (t_method)ffCondition_upCoeff, + gensym("up-coeff"), + A_DEFFLOAT, + 0); + class_addmethod(ffFriction_class, + (t_method)ffCondition_downCoeff, + gensym("down-coeff"), + A_DEFFLOAT, + 0); + + class_addmethod(ffFriction_class, + (t_method)ffCondition_deadbandx, + gensym("deadband-x"), + A_DEFFLOAT, + 0); + class_addmethod(ffFriction_class, + (t_method)ffCondition_centerx, + gensym("center-x"), + A_DEFFLOAT, + 0); + + + class_addmethod(ffFriction_class, + (t_method)ffCondition_deadbandy, + gensym("deadband-y"), + A_DEFFLOAT, + 0); + class_addmethod(ffFriction_class, + (t_method)ffCondition_centery, + gensym("center-y"), + A_DEFFLOAT, + 0); + +} + +void init_ffGain(void) + +{ + ffGain_class = class_new(gensym("ff-gain"), + (t_newmethod)ffGain_new, + 0, + sizeof(t_ff), + CLASS_DEFAULT, + A_DEFFLOAT, + A_DEFFLOAT, + 0); + + class_addfloat(ffGain_class,(t_method)ffGain_set); + +} + +void init_ffAutocenter(void) +{ + ffAutocenter_class = class_new(gensym("ff-autocenter"), + (t_newmethod)ffAutocenter_new, + 0, + sizeof(t_ff), + CLASS_DEFAULT, + A_DEFFLOAT, + A_DEFFLOAT, + 0); + + class_addfloat(ffAutocenter_class,(t_method)ffAutocenter_set); + +} + + +void ff_setup(void) +{ + /* open event device and determine available effects and memory */ + /* the externals themselves also check, this is just to give some info to the user on startup */ + + + char device_file_name[4][18]; + unsigned long features[4]; + int n_effects; /* Number of effects the device can play at the same time */ + int j,ffdevice_count,fftest,fd; + + post("//////////////////////////////////////////\n" + "/////Force feedback external library///// \n" + "////Gerard van Dongen, gml@xs4all.nl//// \n" + "///testing for available ff devices////.\n" + "//////////////////////////////////////"); + + + ffdevice_count = 0; + for (j=0;j<4;j++){ + fftest = 0; + sprintf(device_file_name[j], "/dev/input/event%i",j); + + /* Open device */ + fd = open(device_file_name[j], O_RDWR | O_NONBLOCK); + if (fd == -1) { + continue; + } + post("Device %s opened\n", device_file_name[j]); + + /* Query device */ + if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features) == -1) { + error("Couldn't determine available ff-effects \n FF probablz won't work"); + close(fd); + continue; + } + post("the following externals will work on %s",device_file_name[j]); + + if (test_bit(FF_CONSTANT, features)) { + post("ff-constant "); + fftest++; + } + if (test_bit(FF_PERIODIC, features)) { + post("ff-periodic "); + fftest++; + } + if (test_bit(FF_SPRING, features)) { + post("ff-spring "); + fftest++; + } + if (test_bit(FF_FRICTION, features)) { + post("ff-friction "); + fftest++; + } + if (test_bit(FF_RUMBLE, features)) { + post("The rumble effect is supported by the device,\n" + "but there is no external to control this in pd (yet) "); + fftest++; + } + if (test_bit(FF_RAMP, features)) { + post("The ramp effect is supported by the device,\n" + "but there is no external to control this in pd (yet) "); + fftest++; + } + if (test_bit(FF_DAMPER, features)){ + post("The damper effect is supported by the device,\n" + "but there is no external to control this in pd (yet) "); + fftest++; + } + if (test_bit(FF_INERTIA, features)){ + post("The inertia effect is supported by the device,\n" + "but there is no external to control this in pd (yet) "); + fftest++; + } + + + if (ioctl(fd, EVIOCGEFFECTS, &n_effects) == -1) { + error("Ioctl number of effects"); + } + post("Number of simultaneous effects: %i",n_effects); + + close(fd); + if (fftest != 0 && n_effects !=0) { + ffdevice_count++; + ff_dev[j].max_fx = n_effects; + ff_dev[j].loaded_fx = 0; + strncpy(ff_dev[j].name,device_file_name[j],64); + } + + } + + if (ffdevice_count >0) + post("%i ff-device(s) found",ffdevice_count); + else + post("NO ff capable devices found"); + + + init_ffConstant(); + init_ffPeriodic(); + init_ffSpring(); + init_ffFriction(); + init_ffGain(); + init_ffAutocenter(); +} + + + + diff --git a/ff/input.h b/ff/input.h new file mode 100644 index 0000000..970e163 --- /dev/null +++ b/ff/input.h @@ -0,0 +1,831 @@ +#ifndef _INPUT_H +#define _INPUT_H + +/* + * $Id: input.h,v 1.1.1.1 2003-10-18 13:37:21 vdongen Exp $ + * + * Copyright (c) 1999-2001 Vojtech Pavlik + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#ifdef __KERNEL__ +#include <linux/time.h> +#else +#include <sys/time.h> +#include <sys/ioctl.h> +#include <asm/types.h> +#endif + +/* + * The event structure itself + */ + +struct input_event { + struct timeval time; + unsigned short type; + unsigned short code; + unsigned int value; +}; + +/* + * Protocol version. + */ + +#define EV_VERSION 0x010000 + +/* + * IOCTLs (0x00 - 0x7f) + */ + +#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */ +#define EVIOCGID _IOR('E', 0x02, short[4]) /* get device ID */ +#define EVIOCGREP _IOR('E', 0x03, int[2]) /* get repeat settings */ +#define EVIOCSREP _IOW('E', 0x03, int[2]) /* get repeat settings */ +#define EVIOCGKEYCODE _IOR('E', 0x04, int[2]) /* get keycode */ +#define EVIOCSKEYCODE _IOW('E', 0x04, int[2]) /* set keycode */ + +#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */ +#define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */ +#define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) /* get unique identifier */ + +#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) /* get global keystate */ +#define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) /* get all LEDs */ +#define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len) /* get all sounds status */ + +#define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + ev, len) /* get event bits */ +#define EVIOCGABS(abs) _IOR('E', 0x40 + abs, int[5]) /* get abs value/limits */ + +#define EVIOCSFF _IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect)) /* send a force effect to a force feedback device */ +#define EVIOCRMFF _IOW('E', 0x81, int) /* Erase a force effect */ +#define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */ + +/* + * Event types + */ + +#define EV_RST 0x00 +#define EV_KEY 0x01 +#define EV_REL 0x02 +#define EV_ABS 0x03 +#define EV_MSC 0x04 +#define EV_LED 0x11 +#define EV_SND 0x12 +#define EV_REP 0x14 +#define EV_FF 0x15 +#define EV_PWR 0x16 +#define EV_FF_STATUS 0x17 +#define EV_MAX 0x1f + +/* + * Keys and buttons + */ + +#define KEY_RESERVED 0 +#define KEY_ESC 1 +#define KEY_1 2 +#define KEY_2 3 +#define KEY_3 4 +#define KEY_4 5 +#define KEY_5 6 +#define KEY_6 7 +#define KEY_7 8 +#define KEY_8 9 +#define KEY_9 10 +#define KEY_0 11 +#define KEY_MINUS 12 +#define KEY_EQUAL 13 +#define KEY_BACKSPACE 14 +#define KEY_TAB 15 +#define KEY_Q 16 +#define KEY_W 17 +#define KEY_E 18 +#define KEY_R 19 +#define KEY_T 20 +#define KEY_Y 21 +#define KEY_U 22 +#define KEY_I 23 +#define KEY_O 24 +#define KEY_P 25 +#define KEY_LEFTBRACE 26 +#define KEY_RIGHTBRACE 27 +#define KEY_ENTER 28 +#define KEY_LEFTCTRL 29 +#define KEY_A 30 +#define KEY_S 31 +#define KEY_D 32 +#define KEY_F 33 +#define KEY_G 34 +#define KEY_H 35 +#define KEY_J 36 +#define KEY_K 37 +#define KEY_L 38 +#define KEY_SEMICOLON 39 +#define KEY_APOSTROPHE 40 +#define KEY_GRAVE 41 +#define KEY_LEFTSHIFT 42 +#define KEY_BACKSLASH 43 +#define KEY_Z 44 +#define KEY_X 45 +#define KEY_C 46 +#define KEY_V 47 +#define KEY_B 48 +#define KEY_N 49 +#define KEY_M 50 +#define KEY_COMMA 51 +#define KEY_DOT 52 +#define KEY_SLASH 53 +#define KEY_RIGHTSHIFT 54 +#define KEY_KPASTERISK 55 +#define KEY_LEFTALT 56 +#define KEY_SPACE 57 +#define KEY_CAPSLOCK 58 +#define KEY_F1 59 +#define KEY_F2 60 +#define KEY_F3 61 +#define KEY_F4 62 +#define KEY_F5 63 +#define KEY_F6 64 +#define KEY_F7 65 +#define KEY_F8 66 +#define KEY_F9 67 +#define KEY_F10 68 +#define KEY_NUMLOCK 69 +#define KEY_SCROLLLOCK 70 +#define KEY_KP7 71 +#define KEY_KP8 72 +#define KEY_KP9 73 +#define KEY_KPMINUS 74 +#define KEY_KP4 75 +#define KEY_KP5 76 +#define KEY_KP6 77 +#define KEY_KPPLUS 78 +#define KEY_KP1 79 +#define KEY_KP2 80 +#define KEY_KP3 81 +#define KEY_KP0 82 +#define KEY_KPDOT 83 +#define KEY_103RD 84 +#define KEY_F13 85 +#define KEY_102ND 86 +#define KEY_F11 87 +#define KEY_F12 88 +#define KEY_F14 89 +#define KEY_F15 90 +#define KEY_F16 91 +#define KEY_F17 92 +#define KEY_F18 93 +#define KEY_F19 94 +#define KEY_F20 95 +#define KEY_KPENTER 96 +#define KEY_RIGHTCTRL 97 +#define KEY_KPSLASH 98 +#define KEY_SYSRQ 99 +#define KEY_RIGHTALT 100 +#define KEY_LINEFEED 101 +#define KEY_HOME 102 +#define KEY_UP 103 +#define KEY_PAGEUP 104 +#define KEY_LEFT 105 +#define KEY_RIGHT 106 +#define KEY_END 107 +#define KEY_DOWN 108 +#define KEY_PAGEDOWN 109 +#define KEY_INSERT 110 +#define KEY_DELETE 111 +#define KEY_MACRO 112 +#define KEY_MUTE 113 +#define KEY_VOLUMEDOWN 114 +#define KEY_VOLUMEUP 115 +#define KEY_POWER 116 +#define KEY_KPEQUAL 117 +#define KEY_KPPLUSMINUS 118 +#define KEY_PAUSE 119 +#define KEY_F21 120 +#define KEY_F22 121 +#define KEY_F23 122 +#define KEY_F24 123 +#define KEY_KPCOMMA 124 +#define KEY_LEFTMETA 125 +#define KEY_RIGHTMETA 126 +#define KEY_COMPOSE 127 + +#define KEY_STOP 128 +#define KEY_AGAIN 129 +#define KEY_PROPS 130 +#define KEY_UNDO 131 +#define KEY_FRONT 132 +#define KEY_COPY 133 +#define KEY_OPEN 134 +#define KEY_PASTE 135 +#define KEY_FIND 136 +#define KEY_CUT 137 +#define KEY_HELP 138 +#define KEY_MENU 139 +#define KEY_CALC 140 +#define KEY_SETUP 141 +#define KEY_SLEEP 142 +#define KEY_WAKEUP 143 +#define KEY_FILE 144 +#define KEY_SENDFILE 145 +#define KEY_DELETEFILE 146 +#define KEY_XFER 147 +#define KEY_PROG1 148 +#define KEY_PROG2 149 +#define KEY_WWW 150 +#define KEY_MSDOS 151 +#define KEY_COFFEE 152 +#define KEY_DIRECTION 153 +#define KEY_CYCLEWINDOWS 154 +#define KEY_MAIL 155 +#define KEY_BOOKMARKS 156 +#define KEY_COMPUTER 157 +#define KEY_BACK 158 +#define KEY_FORWARD 159 +#define KEY_CLOSECD 160 +#define KEY_EJECTCD 161 +#define KEY_EJECTCLOSECD 162 +#define KEY_NEXTSONG 163 +#define KEY_PLAYPAUSE 164 +#define KEY_PREVIOUSSONG 165 +#define KEY_STOPCD 166 +#define KEY_RECORD 167 +#define KEY_REWIND 168 +#define KEY_PHONE 169 +#define KEY_ISO 170 +#define KEY_CONFIG 171 +#define KEY_HOMEPAGE 172 +#define KEY_REFRESH 173 +#define KEY_EXIT 174 +#define KEY_MOVE 175 +#define KEY_EDIT 176 +#define KEY_SCROLLUP 177 +#define KEY_SCROLLDOWN 178 +#define KEY_KPLEFTPAREN 179 +#define KEY_KPRIGHTPAREN 180 + +#define KEY_INTL1 181 +#define KEY_INTL2 182 +#define KEY_INTL3 183 +#define KEY_INTL4 184 +#define KEY_INTL5 185 +#define KEY_INTL6 186 +#define KEY_INTL7 187 +#define KEY_INTL8 188 +#define KEY_INTL9 189 +#define KEY_LANG1 190 +#define KEY_LANG2 191 +#define KEY_LANG3 192 +#define KEY_LANG4 193 +#define KEY_LANG5 194 +#define KEY_LANG6 195 +#define KEY_LANG7 196 +#define KEY_LANG8 197 +#define KEY_LANG9 198 + +#define KEY_PLAYCD 200 +#define KEY_PAUSECD 201 +#define KEY_PROG3 202 +#define KEY_PROG4 203 +#define KEY_SUSPEND 205 +#define KEY_CLOSE 206 +#define KEY_PLAY 207 +#define KEY_FASTFORWARD 208 +#define KEY_BASSBOOST 209 +#define KEY_PRINT 210 +#define KEY_HP 211 +#define KEY_CAMERA 212 +#define KEY_SOUND 213 +#define KEY_QUESTION 214 +#define KEY_EMAIL 215 +#define KEY_CHAT 216 +#define KEY_SEARCH 217 +#define KEY_CONNECT 218 +#define KEY_FINANCE 219 +#define KEY_SPORT 220 +#define KEY_SHOP 221 +#define KEY_ALTERASE 222 +#define KEY_CANCEL 223 +#define KEY_BRIGHTNESSDOWN 224 +#define KEY_BRIGHTNESSUP 225 + +#define KEY_UNKNOWN 240 + +#define KEY_BRIGHTNESSDOWN 224 +#define KEY_BRIGHTNESSUP 225 + +#define BTN_MISC 0x100 +#define BTN_0 0x100 +#define BTN_1 0x101 +#define BTN_2 0x102 +#define BTN_3 0x103 +#define BTN_4 0x104 +#define BTN_5 0x105 +#define BTN_6 0x106 +#define BTN_7 0x107 +#define BTN_8 0x108 +#define BTN_9 0x109 + +#define BTN_MOUSE 0x110 +#define BTN_LEFT 0x110 +#define BTN_RIGHT 0x111 +#define BTN_MIDDLE 0x112 +#define BTN_SIDE 0x113 +#define BTN_EXTRA 0x114 +#define BTN_FORWARD 0x115 +#define BTN_BACK 0x116 + +#define BTN_JOYSTICK 0x120 +#define BTN_TRIGGER 0x120 +#define BTN_THUMB 0x121 +#define BTN_THUMB2 0x122 +#define BTN_TOP 0x123 +#define BTN_TOP2 0x124 +#define BTN_PINKIE 0x125 +#define BTN_BASE 0x126 +#define BTN_BASE2 0x127 +#define BTN_BASE3 0x128 +#define BTN_BASE4 0x129 +#define BTN_BASE5 0x12a +#define BTN_BASE6 0x12b +#define BTN_DEAD 0x12f + +#define BTN_GAMEPAD 0x130 +#define BTN_A 0x130 +#define BTN_B 0x131 +#define BTN_C 0x132 +#define BTN_X 0x133 +#define BTN_Y 0x134 +#define BTN_Z 0x135 +#define BTN_TL 0x136 +#define BTN_TR 0x137 +#define BTN_TL2 0x138 +#define BTN_TR2 0x139 +#define BTN_SELECT 0x13a +#define BTN_START 0x13b +#define BTN_MODE 0x13c +#define BTN_THUMBL 0x13d +#define BTN_THUMBR 0x13e + +#define BTN_DIGI 0x140 +#define BTN_TOOL_PEN 0x140 +#define BTN_TOOL_RUBBER 0x141 +#define BTN_TOOL_BRUSH 0x142 +#define BTN_TOOL_PENCIL 0x143 +#define BTN_TOOL_AIRBRUSH 0x144 +#define BTN_TOOL_FINGER 0x145 +#define BTN_TOOL_MOUSE 0x146 +#define BTN_TOOL_LENS 0x147 +#define BTN_TOUCH 0x14a +#define BTN_STYLUS 0x14b +#define BTN_STYLUS2 0x14c + +#define BTN_WHEEL 0x150 +#define BTN_GEAR_DOWN 0x150 +#define BTN_GEAR_UP 0x151 + +#define KEY_MAX 0x1ff + +/* + * Relative axes + */ + +#define REL_X 0x00 +#define REL_Y 0x01 +#define REL_Z 0x02 +#define REL_HWHEEL 0x06 +#define REL_DIAL 0x07 +#define REL_WHEEL 0x08 +#define REL_MISC 0x09 +#define REL_MAX 0x0f + +/* + * Absolute axes + */ + +#define ABS_X 0x00 +#define ABS_Y 0x01 +#define ABS_Z 0x02 +#define ABS_RX 0x03 +#define ABS_RY 0x04 +#define ABS_RZ 0x05 +#define ABS_THROTTLE 0x06 +#define ABS_RUDDER 0x07 +#define ABS_WHEEL 0x08 +#define ABS_GAS 0x09 +#define ABS_BRAKE 0x0a +#define ABS_HAT0X 0x10 +#define ABS_HAT0Y 0x11 +#define ABS_HAT1X 0x12 +#define ABS_HAT1Y 0x13 +#define ABS_HAT2X 0x14 +#define ABS_HAT2Y 0x15 +#define ABS_HAT3X 0x16 +#define ABS_HAT3Y 0x17 +#define ABS_PRESSURE 0x18 +#define ABS_DISTANCE 0x19 +#define ABS_TILT_X 0x1a +#define ABS_TILT_Y 0x1b +#define ABS_VOLUME 0x20 +#define ABS_MISC 0x28 +#define ABS_MAX 0x3f + +/* + * Misc events + */ + +#define MSC_SERIAL 0x00 +#define MSC_PULSELED 0x01 +#define MSC_MAX 0x07 + +/* + * LEDs + */ + +#define LED_NUML 0x00 +#define LED_CAPSL 0x01 +#define LED_SCROLLL 0x02 +#define LED_COMPOSE 0x03 +#define LED_KANA 0x04 +#define LED_SLEEP 0x05 +#define LED_SUSPEND 0x06 +#define LED_MUTE 0x07 +#define LED_MISC 0x08 +#define LED_MAX 0x0f + +/* + * Autorepeat values + */ + +#define REP_DELAY 0x00 +#define REP_PERIOD 0x01 +#define REP_MAX 0x01 + +/* + * Sounds + */ + +#define SND_CLICK 0x00 +#define SND_BELL 0x01 +#define SND_MAX 0x07 + +/* + * IDs. + */ + +#define ID_BUS 0 +#define ID_VENDOR 1 +#define ID_PRODUCT 2 +#define ID_VERSION 3 + +#define BUS_PCI 0x01 +#define BUS_ISAPNP 0x02 +#define BUS_USB 0x03 +#define BUS_HIL 0x04 + +#define BUS_ISA 0x10 +#define BUS_I8042 0x11 +#define BUS_XTKBD 0x12 +#define BUS_RS232 0x13 +#define BUS_GAMEPORT 0x14 +#define BUS_PARPORT 0x15 +#define BUS_AMIGA 0x16 +#define BUS_ADB 0x17 +#define BUS_I2C 0x18 + +/* + * Values describing the status of an effect + */ +#define FF_STATUS_STOPPED 0x00 +#define FF_STATUS_PLAYING 0x01 +#define FF_STATUS_MAX 0x01 + +/* + * Structures used in ioctls to upload effects to a device + * The first structures are not passed directly by using ioctls. + * They are sub-structures of the actually sent structure (called ff_effect) + */ + +struct ff_replay { + __u16 length; /* Duration of an effect in ms. + All other times are also expressed in ms. + 0 means "play for ever" */ + __u16 delay; /* Time to wait before to start playing an effect */ +}; + +struct ff_trigger { + __u16 button; /* Number of button triggering an effect */ + __u16 interval; /* Time to wait before an effect can be re-triggered (ms) */ +}; + +struct ff_envelope { + __u16 attack_length; /* Duration of attack (ms) */ + __u16 attack_level; /* Level at beginning of attack */ + __u16 fade_length; /* Duration of fade (ms) */ + __u16 fade_level; /* Level at end of fade */ +}; + +/* FF_CONSTANT */ +struct ff_constant_effect { + __s16 level; /* Strength of effect. Negative values are OK */ + struct ff_envelope envelope; +}; + +/* FF_RAMP */ +struct ff_ramp_effect { + __s16 start_level; + __s16 end_level; + struct ff_envelope envelope; +}; + +/* FF_SPRING of FF_FRICTION */ +struct ff_condition_effect { + __u16 right_saturation; /* Max level when joystick is on the right */ + __u16 left_saturation; /* Max level when joystick in on the left */ + + __s16 right_coeff; /* Indicates how fast the force grows when the + joystick moves to the right */ + __s16 left_coeff; /* Same for left side */ + + __u16 deadband; /* Size of area where no force is produced */ + __s16 center; /* Position of dead zone */ + +}; + +/* FF_PERIODIC */ +struct ff_periodic_effect { + __u16 waveform; /* Kind of wave (sine, square...) */ + __u16 period; /* in ms */ + __s16 magnitude; /* Peak value */ + __s16 offset; /* Mean value of wave (roughly) */ + __u16 phase; /* 'Horizontal' shift */ + + struct ff_envelope envelope; + +/* Only used if waveform == FF_CUSTOM */ + __u32 custom_len; /* Number of samples */ + __s16 *custom_data; /* Buffer of samples */ +/* Note: the data pointed by custom_data is copied by the driver. You can + * therefore dispose of the memory after the upload/update */ +}; + +/* FF_RUMBLE */ +/* Some rumble pads have two motors of different weight. + strong_magnitude represents the magnitude of the vibration generated + by the heavy motor. +*/ +struct ff_rumble_effect { + __u16 strong_magnitude; /* Magnitude of the heavy motor */ + __u16 weak_magnitude; /* Magnitude of the light one */ +}; + +/* + * Structure sent through ioctl from the application to the driver + */ +struct ff_effect { + __u16 type; +/* Following field denotes the unique id assigned to an effect. + * If user sets if to -1, a new effect is created, and its id is returned in the same field + * Else, the user sets it to the effect id it wants to update. + */ + __s16 id; + + __u16 direction; /* Direction. 0 deg -> 0x0000 (down) + 90 deg -> 0x4000 (left) + 180 deg -> 0x8000 (up) + 270 deg -> 0xC000 (right) + */ + + struct ff_trigger trigger; + struct ff_replay replay; + + union { + struct ff_constant_effect constant; + struct ff_ramp_effect ramp; + struct ff_periodic_effect periodic; + struct ff_condition_effect condition[2]; /* One for each axis */ + struct ff_rumble_effect rumble; + } u; +}; + +/* + * Force feedback effect types + */ + +#define FF_RUMBLE 0x50 +#define FF_PERIODIC 0x51 +#define FF_CONSTANT 0x52 +#define FF_SPRING 0x53 +#define FF_FRICTION 0x54 +#define FF_DAMPER 0x55 +#define FF_INERTIA 0x56 +#define FF_RAMP 0x57 + +/* + * Force feedback periodic effect types + */ + +#define FF_SQUARE 0x58 +#define FF_TRIANGLE 0x59 +#define FF_SINE 0x5a +#define FF_SAW_UP 0x5b +#define FF_SAW_DOWN 0x5c +#define FF_CUSTOM 0x5d + +/* + * Set ff device properties + */ + +#define FF_GAIN 0x60 +#define FF_AUTOCENTER 0x61 + +#define FF_MAX 0x7f + +#ifdef __KERNEL__ + +/* + * In-kernel definitions. + */ + +#include <linux/sched.h> +#include <linux/devfs_fs_kernel.h> + +#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) +#define BIT(x) (1UL<<((x)%BITS_PER_LONG)) +#define LONG(x) ((x)/BITS_PER_LONG) + +struct input_dev { + + void *private; + + char *name; + char *phys; + char *uniq; + unsigned short idbus; + unsigned short idvendor; + unsigned short idproduct; + unsigned short idversion; + + unsigned long evbit[NBITS(EV_MAX)]; + unsigned long keybit[NBITS(KEY_MAX)]; + unsigned long relbit[NBITS(REL_MAX)]; + unsigned long absbit[NBITS(ABS_MAX)]; + unsigned long mscbit[NBITS(MSC_MAX)]; + unsigned long ledbit[NBITS(LED_MAX)]; + unsigned long sndbit[NBITS(SND_MAX)]; + unsigned long ffbit[NBITS(FF_MAX)]; + int ff_effects_max; + + unsigned int keycodemax; + unsigned int keycodesize; + void *keycode; + + unsigned int repeat_key; + struct timer_list timer; + + struct pm_dev *pm_dev; + int state; + + int abs[ABS_MAX + 1]; + int rep[REP_MAX + 1]; + + unsigned long key[NBITS(KEY_MAX)]; + unsigned long led[NBITS(LED_MAX)]; + unsigned long snd[NBITS(SND_MAX)]; + + int absmax[ABS_MAX + 1]; + int absmin[ABS_MAX + 1]; + int absfuzz[ABS_MAX + 1]; + int absflat[ABS_MAX + 1]; + + int (*open)(struct input_dev *dev); + void (*close)(struct input_dev *dev); + int (*accept)(struct input_dev *dev, struct file *file); + int (*flush)(struct input_dev *dev, struct file *file); + int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); + int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect); + int (*erase_effect)(struct input_dev *dev, int effect_id); + + struct input_handle *handle; + struct input_dev *next; +}; + +/* + * Structure for hotplug & device<->driver matching. + */ + +#define INPUT_DEVICE_ID_MATCH_BUS 1 +#define INPUT_DEVICE_ID_MATCH_VENDOR 2 +#define INPUT_DEVICE_ID_MATCH_PRODUCT 4 +#define INPUT_DEVICE_ID_MATCH_VERSION 8 + +#define INPUT_DEVICE_ID_MATCH_EVBIT 0x010 +#define INPUT_DEVICE_ID_MATCH_KEYBIT 0x020 +#define INPUT_DEVICE_ID_MATCH_RELBIT 0x040 +#define INPUT_DEVICE_ID_MATCH_ABSBIT 0x080 +#define INPUT_DEVICE_ID_MATCH_MSCIT 0x100 +#define INPUT_DEVICE_ID_MATCH_LEDBIT 0x200 +#define INPUT_DEVICE_ID_MATCH_SNDBIT 0x400 +#define INPUT_DEVICE_ID_MATCH_FFBIT 0x800 + +#define INPUT_DEVICE_ID_MATCH_DEVICE\ + (INPUT_DEVICE_ID_MATCH_BUS | INPUT_DEVICE_ID_MATCH_VENDOR | INPUT_DEVICE_ID_MATCH_PRODUCT) +#define INPUT_DEVICE_ID_MATCH_DEVICE_AND_VERSION\ + (INPUT_DEVICE_ID_MATCH_DEVICE | INPUT_DEVICE_ID_MATCH_VERSION) + +struct input_device_id { + + unsigned long flags; + + unsigned short idbus; + unsigned short idvendor; + unsigned short idproduct; + unsigned short idversion; + + unsigned long evbit[NBITS(EV_MAX)]; + unsigned long keybit[NBITS(KEY_MAX)]; + unsigned long relbit[NBITS(REL_MAX)]; + unsigned long absbit[NBITS(ABS_MAX)]; + unsigned long mscbit[NBITS(MSC_MAX)]; + unsigned long ledbit[NBITS(LED_MAX)]; + unsigned long sndbit[NBITS(SND_MAX)]; + unsigned long ffbit[NBITS(FF_MAX)]; + + unsigned long driver_info; +}; + +struct input_handler { + + void *private; + + void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); + struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id); + void (*disconnect)(struct input_handle *handle); + + struct file_operations *fops; + int minor; + char *name; + + struct input_device_id *id_table; + + struct input_handle *handle; + struct input_handler *next; +}; + +struct input_handle { + + void *private; + + int open; + char *name; + + struct input_dev *dev; + struct input_handler *handler; + + struct input_handle *dnext; + struct input_handle *hnext; +}; + +void input_register_device(struct input_dev *); +void input_unregister_device(struct input_dev *); + +void input_register_handler(struct input_handler *); +void input_unregister_handler(struct input_handler *); + +int input_open_device(struct input_handle *); +void input_close_device(struct input_handle *); + +int input_accept_process(struct input_handle *handle, struct file *file); +int input_flush_device(struct input_handle* handle, struct file* file); + +devfs_handle_t input_register_minor(char *name, int minor, int minor_base); +void input_unregister_minor(devfs_handle_t handle); + +void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value); + +#define input_report_key(a,b,c) input_event(a, EV_KEY, b, !!(c)) +#define input_report_rel(a,b,c) input_event(a, EV_REL, b, c) +#define input_report_abs(a,b,c) input_event(a, EV_ABS, b, c) +#define input_report_ff(a,b,c) input_event(a, EV_FF, b, c) +#define input_report_ff_status(a,b,c) input_event(a, EV_FF_STATUS, b, c) + +#endif +#endif diff --git a/ff/makefile b/ff/makefile new file mode 100644 index 0000000..0ed84d5 --- /dev/null +++ b/ff/makefile @@ -0,0 +1,14 @@ + +CFLAGS = -O2 -W -Wall + +all: ff + +ff: ff.c + $(CC) $(CFLAGS) $(LINUXCFLAGS) $(LINUXINCLUDE) -o ff.o -c ff.c + ld --export-dynamic -shared -o ff.pd_linux ff.o -lc -lm + strip --strip-unneeded ff.pd_linux + rm ff.o + + +clean: + rm -f *~ *.pd_* *.dll *.o 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/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;"$(VCInstallDir)include";"$(VCInstallDir)atlmfc\include";C:\WinDDK\7600.16385.1\inc\api;"$(WindowsSdkDir)\include";"$(FrameworkSDKDir)include";"C:\Program Files\pd-0.43.0\src"" + 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;"C:\Program Files\pd-0.43.0\bin"" + 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;"$(VCInstallDir)include";"$(VCInstallDir)atlmfc\include";C:\WinDDK\7600.16385.1\inc\api;"$(WindowsSdkDir)\include";"$(FrameworkSDKDir)include";"$(ProgramFiles)\pd\src";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;"$(ProgramFiles)\pd\bin"" + 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;"$(VCInstallDir)include";"$(VCInstallDir)atlmfc\include";C:\WinDDK\7600.16385.1\inc\api;"$(WindowsSdkDir)\include";"$(FrameworkSDKDir)include";"C:\Program Files\pd-0.43.0\src"" + 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;"C:\Program Files\pd-0.43.0\bin"" + 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;"$(VCInstallDir)include";"$(VCInstallDir)atlmfc\include";C:\WinDDK\7600.16385.1\inc\api;"$(WindowsSdkDir)\include";"$(FrameworkSDKDir)include";"C:\Program Files\pd-0.43.0\src"" + 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;"C:\Program Files\pd-0.43.0\bin"" + 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 |