[Demudi-commits] r15 - / lakai lakai/branches lakai/branches/upstream lakai/branches/upstream/current

Free Ekanayaka free-guest@haydn.debian.org
Thu, 28 Oct 2004 03:32:28 -0600


Author: free-guest
Date: 2004-10-28 03:32:22 -0600 (Thu, 28 Oct 2004)
New Revision: 15

Added:
   lakai/
   lakai/branches/
   lakai/branches/upstream/
   lakai/branches/upstream/current/
   lakai/branches/upstream/current/AUTHORS
   lakai/branches/upstream/current/COPYING
   lakai/branches/upstream/current/Makefile
   lakai/branches/upstream/current/README
   lakai/branches/upstream/current/ROADMAP
   lakai/branches/upstream/current/akaiascii.txt
   lakai/branches/upstream/current/lakai.c
   lakai/branches/upstream/current/lakai.h
   lakai/branches/upstream/current/lakbak.c
   lakai/branches/upstream/current/lakclear.c
   lakai/branches/upstream/current/lakres.c
   lakai/tags/
Log:
[svn-inject] Installing original source of lakai

Added: lakai/branches/upstream/current/AUTHORS
===================================================================
--- lakai/branches/upstream/current/AUTHORS	2004-10-28 08:35:57 UTC (rev 14)
+++ lakai/branches/upstream/current/AUTHORS	2004-10-28 09:32:22 UTC (rev 15)
@@ -0,0 +1,4 @@
+
+The currently only author of Lakai is Frank Neumann.
+
+Your name here..? :-)

Added: lakai/branches/upstream/current/COPYING
===================================================================
--- lakai/branches/upstream/current/COPYING	2004-10-28 08:35:57 UTC (rev 14)
+++ lakai/branches/upstream/current/COPYING	2004-10-28 09:32:22 UTC (rev 15)
@@ -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.

Added: lakai/branches/upstream/current/Makefile
===================================================================
--- lakai/branches/upstream/current/Makefile	2004-10-28 08:35:57 UTC (rev 14)
+++ lakai/branches/upstream/current/Makefile	2004-10-28 09:32:22 UTC (rev 15)
@@ -0,0 +1,52 @@
+
+# Makefile for lakai: link library and user programs
+# Frank Neumann, February 2004
+#
+
+PREFIX=/usr/local
+
+CC=gcc
+CFLAGS=-g -Wall -Wstrict-prototypes # add -DDEBUG=1   for debugging
+OBJS=lakai.o
+LIB=liblakai.a
+
+# These 3 programs should go into one binary some day.
+all: $(LIB) lakclear lakbak lakres
+
+progs: lakclear lakbak lakres
+
+lakclear: lakclear.c $(LIB)
+	$(CC) $< -o $@ -L. -llakai
+
+lakbak: lakbak.c $(LIB)
+	$(CC) $< -o $@ -L. -llakai
+
+lakres: lakres.c $(LIB)
+	$(CC) $< -o $@ -L. -llakai
+
+$(LIB): $(OBJS)
+	rm -f $@
+	ar rcu $@ $<
+	ranlib $@
+
+lakai.o: lakai.c lakai.h
+	$(CC) $(CFLAGS) -c $< -o $@
+
+install: $(LIB) progs
+	install -d $(PREFIX)/bin
+	install -m 755 lakbak lakres lakclear $(PREFIX)/bin
+	install -d $(PREFIX)/include
+	install -m 644 lakai.h $(PREFIX)/include
+	install -d $(PREFIX)/lib
+	install -m 644 liblakai.a $(PREFIX)/lib
+	
+uninstall:
+	rm -f $(PREFIX)/bin/lakbak
+	rm -f $(PREFIX)/bin/lakres
+	rm -f $(PREFIX)/bin/lakclear
+	rm -f $(PREFIX)/include/lakai.h
+	rm -f $(PREFIX)/lib/liblakai.a
+
+clean:
+	rm -f *.o $(OBJS) $(LIB) lakclear lakbak lakres
+

Added: lakai/branches/upstream/current/README
===================================================================
--- lakai/branches/upstream/current/README	2004-10-28 08:35:57 UTC (rev 14)
+++ lakai/branches/upstream/current/README	2004-10-28 09:32:22 UTC (rev 15)
@@ -0,0 +1,423 @@
+
+Lakai - Linux Akai sampler tools
+================================
+
+Author:       Frank Neumann
+Version:      0.1
+Date:         05.02.2004
+Homepage:     http://lakai.sourceforge.net
+
+
+What is it?
+-----------
+Lakai is a small set of tools (+ a link library) used to transfer sampler 
+data (programs, samples) between a PC with a SCSI host adapter and an AKAI 
+sampler (S1000, S2000..). The current tools allow an easy way to create a
+full backup of the sampler's memory contents to the PC and a full restore
+of this data back from the PC to the sampler.
+Future versions might contain more fine-grained control over the data you
+exchange, but this is still in planning stage.
+
+
+Additions/changes in this release
+---------------------------------
+
+This is the first release. Enter at your own risk, stranger!
+
+
+Supported hardware
+------------------
+
+Currently, this software has only been tested on my hardware, which is a
+1 GHz Athlon desktop PC with an Adaptec 2904CD SCSI host adapter connected 
+to an S2000 sampler. Other hardware might work, but people need to test 
+and report this first with their respective systems. Hopefully the list
+of supported hardware will then soon grow.
+
+
+Requirements
+------------
+
+- Hardware
+  Naturally, you need a PC with SCSI host adapter and an AKAI sampler. Also,
+  this software has only been written with Linux in my mind, and has been
+  developed solely on ia32 hardware - but in theory it should also work on
+  e.g. MIPS, UltraSparc or Alpha. Still to be tested.
+
+- Cabling
+  You might know about this already, but SCSI requires termination.
+  That means: SCSI is a bus; a bus has two ends; and these two ends need
+  to be terminated correctly. If you fail to do that, you might get strange
+  errors sometimes. My setup looks like this:
+
+  ---       ---       ---
+ X| | <---> | | <---> | |X
+  ---       ---       ---
+  PC      CD-ROM     Sampler
+
+  The two "X" indicate that in my case the bus is terminated at the PC side
+  (inside the SCSI host adapter) and on the sampler. The external CD-ROM drive
+  is in the middle, and thus needs no termination. The sampler only has one
+  SCSI connection, so it has to be one end of the bus anyway. When you bought 
+  it, it should have come with termination resistors installed inside.
+  To check for sure, you need to open the sampler and look for 2 resistor
+  arrays plugged in.
+  
+- To compile, you will need the usual developer tools:
+  - glibc 2.x with its -dev package (include files etc)
+  - gcc, the Gnu C compiler. 2.95 or 3.x, both should work.
+  - binutils - should have been installed in parallel with gcc
+  - make
+
+- For analysis of your SCSI setup, the "scsiadd" tools is quite useful -
+  so I suggest to install it (see "Links" at the end for download resources)
+  
+- There is also a graphical frontend to scsiadd called "scsiaddgui" for those
+  who rather point-click-point-click-knock-over-my-coffee-cup than
+  tippety-tappety-tip. Again, see the "Links" section at the end of this 
+  file for URLs.
+
+- Kernel: You will need a scsi generic driver ('sg'), either built statically
+  into the kernel or as a loadable module. It should provide the
+  "sg3" driver interface which comes with kernel 2.4 series and above.
+  I am using a 2.4.16 kernel successfully, but I sometimes see warnings
+  from the kernel about "scsi: Someone has reset channel A". These warnings
+  are, as far as I can tell, harmless. However, I made a few tests with a
+  2.4.20 kernel once, and things went a little more smooth there.
+
+  I saw that in 2.4.16, after booting the PC, the sampler would not always
+  immediately "see" the SCSI CD-ROM - perhaps because the PC has reset
+  the entire SCSI bus at kernel startup, and the sampler first has to
+  find out about this (and reset the bus again himself). Normally, just
+  trying to access the CD-ROM once more after a few seconds worked for me.
+
+  No statement on 2.6 kernels yet - I still have to try them. I wouldn't
+  expect any big problems here, though.
+
+  When you need to build a new kernel, you will find the SCSI Generic (or
+  "sg") option under "SCSI generic support" in the "SCSI support" submenu 
+  of a  "make menuconfig" or "make xconfig" run.
+  If you build the sg module statically into the kernel, you need to reboot,
+  but after the reboot everything is already "in place". If you opt for an
+  "sg" module instead, you need to load it (e.g. through "insmod sg.o" or
+  with modprobe - this requires root privileges), and you will have to let
+  your kernel search for SCSI devices with scsiadd.
+
+  After a restart (and/or possibly having loaded the sg.o module manually),
+  you should see the sg device driver is listed in the available devices 
+  when you do a 
+  $ cat /proc/devices
+  It will show up there under "Character devices" as "21 sg".
+
+  If the module was already loaded when the kernel boots up (that is, if
+  the sg driver was compiled into the kernel), any connected (and powered-up)
+  SCSI sampler should appear in the list of probed SCSI devices. For instance,
+  on my system where the sg driver is built into the kernel, I see the
+  following info when the kernel comes up:
+
+   Vendor: AKAI EMI  Model: S2000 SAMPLER     Rev: 2.00
+   Type:   Processor                          ANSI SCSI revision: 01 CCS
+
+   If you have installed the "scsiadd" tool, you can also use "scsiadd -p"
+   to see a more verbose listing of the available SCSI devices, with their
+   channel/unit/lun numbers, perhaps similar to this:
+
+   franky@faramir:~> scsiadd -p
+   Attached devices: 
+   Host: scsi0 Channel: 00 Id: 05 Lun: 00
+     Vendor: SONY     Model: CD-ROM CDU-8003A Rev: 1.9a
+     Type:   CD-ROM                           ANSI SCSI revision: 02
+   Host: scsi0 Channel: 00 Id: 06 Lun: 00
+     Vendor: AKAI EMI Model: S2000 SAMPLER    Rev: 2.00
+     Type:   Processor                        ANSI SCSI revision: 01 CCS
+   Host: scsi1 Channel: 00 Id: 00 Lun: 00
+     Vendor: PLEXTOR  Model: CD-R   PX-W1210A Rev: 1.05
+     Type:   CD-ROM                           ANSI SCSI revision: 02
+
+  This shows you an old Sony SCSI-CDROM at (0,5,0), followed by the 
+  sampler at (0,6,0). The Plextor CD-RW is only an IDE device with
+  emulated SCSI command set.
+
+  If you loaded the sg module manually, you can ask that the SCSI bus is
+  to be probed again at any time by issueing (as root) the command
+  "scsiadd -s". This is especially useful when you did not turn on the
+  sampler before the Linux kernel came up.
+
+  Finding out what SCSI generic devices are available (and online):
+  Do a "cat /proc/scsi/sg/device_strsi /proc/scsi/sg/devices". This
+  will yield an output something like this:
+  
+  franky@faramir:/proc/scsi/sg> cat device_hdr devices
+  host    chan    id      lun     type    opens   qdepth  busy    online
+  0       0       5       0       5       0       2       0       1
+  0       0       6       0       3       0       2       0       1
+  1       0       0       0       5       0       5       0       1
+
+  This is basically the same data as above, but in a more condensed form.
+  Please take a note of the position of your sampler in this list; this
+  is required for later when you want to use the backup/restore tools.
+  The first device in this list (the one with (0,5,0) as chan/id/lun)
+  becomes SCSI node /dev/sg0, the second becomes /dev/sg1 and so on.
+  So, in this case my sampler is "/dev/sg1" at this time.
+
+  The last column ("online") is interesting here: When for some reason the
+  sampler does not answer to kernel commands in time, the device gets marked
+  as "offline" by the Linux kernel, and any more attempts to access this 
+  device will fail. In this case, you can put the device online again by 
+  issuing a command like
+
+  scsiadd -r <host> <id> <lun>
+  scsiadd -a <host> <id> <lun>
+  
+  In my example above, the sampler is connected with host (or channel) 0,
+  id 6, lun 0, so
+  scsiadd -r 0 6 0  removes the sampler from the device list, and
+  scsiadd -a 0 6 0  puts it in again, and sets it to "online".
+  
+  Generally, avoid accessing the sampler while it is busy, e.g. when it is
+  booting up, loading a sample set from CD/harddisk etc. The sampler does
+  not answer to SCSI commands in this time, and the PC will believe it's
+  unavailable, and will set it to "offline".
+
+  One trap you can easily fall into is also that MIDI communications between
+  the PC and the sampler over SCSI will happen on a MIDI channel which has
+  to be the same on both sides. The lakai tools will only use the lowest
+  MIDI communications channel (1), so it should be set to this on the sampler
+  as well. When you load a sample set from a CD or harddisk, it can change
+  the sampler's channel, and suddenly you find lakai tools not to work
+  anymore. In that case, you will have to
+  a) Set the sampler's MIDI channel to 1
+  b) Re-'online' it on the PC with the above mentioned scsiadd command as
+     it will most likely have been set to offline at this time.
+    
+
+Building
+--------
+
+autoconf/automake is not supported - so there is NO ./configure script
+to call here. But the Makefile is there and is very simple, so just doing
+
+make
+make install
+
+should do all you need. "make install" copies the files to /usr/local/ by
+default - if you do not want that, please edit the simple Makefile yourself.
+
+
+Usage
+-----
+
+Users:
+
+Three small tools are coming with this Lakai release, namely lakclear,
+lakbak and lakres:
+- lakclear - erase all samples and programs currently resident in the sampler
+- lakbak   - performs a complete backup of sampler's data to PC.
+- lakres   - performs a restore of data from PC to the sampler.
+
+Typically, you would work e.g. like this:
+- boot the sampler;
+- erase all data in it with lakclear;
+- record some samples through its LineIn connectors
+- adjust programs, keygroups etc through the sampler's console
+- back up all of the sampler to the PC with lakbak;
+- (on the next day, when you return to your work): boot the sampler
+  again, clear it with lakclear and restore yesterday's data with lakres.
+
+In order to use these tools, you will need privileged access to a SCSI
+generic device, located in /dev/sg* (as mentioned above).
+
+Normally, those device nodes have these permissions:
+crw-------    1 root     disk      21,   0 May 25  2001 /dev/sg0
+crw-------    1 root     disk      21,   1 May 25  2001 /dev/sg1
+...
+
+To use one of them as a non-root user, you could do the following:
+- Add yourself to the "disk" group in /etc/group. One line in there reads
+  like this before:
+  disk:x:6:
+  and for me it now reads:
+  disk:x:6:franky
+
+  After having made this change, you might need to re-login. Use the "id"
+  command to see if you are really member of the disk group afterwards.
+  
+- Modify the permission for the affected /dev/sg* device entry as root:
+
+# chmod 660 /dev/sg1
+.. do the same for possible other sg devices.
+
+I know this presents a possible security risk, but I assume you are not
+using these tools on a networked 24/7 multi-user database/server machine.
+
+When you have done this, you should be able to get a complete backup from
+the sampler like this:
+
+lakbak /dev/sg1 <name.list>
+
+where /dev/sg1 is my SCSI node name for the sampler, and <name.list> is
+a (text) file that will be created and to which lakbak will write the 
+names and types of all data it is downloading from the sampler. 
+All retrieved files (see below) will be written into the current directory -
+so please create and enter a subdirectory first if you want to avoid file
+clutter.
+
+Similarly, a
+
+lakclear /dev/sg1
+
+clears out all samples and programs from the sampler (actually, there will
+always be at least one "Test program" in the sampler because it must
+not contain 0 programs), and
+
+lakres /dev/sg1 <name.list>
+
+will try to send all files listed in <name.list> back to the sampler.
+
+All programs spit out some diagnostic information as they run.
+
+The <name.list> file is a simple ASCII text file that contains:
+- empty lines
+- comment lines (starting with the "#" character to indicate this is a 
+  comment)
+- "SAMP" or "PROG" lines which refer to sample (.s) and program (.p) files.
+
+The .p files contain program data: How to arrange samples into keygroups,
+splits, velocity switches, what filter to apply, LFO settings etc.
+
+The .s files contain the actual PCM data, but they also include some
+AKAI-specific header info, like root note, loop points etc. To convert
+a .s file into, say, a standard .wav file, you need to cut off these
+header bytes (for an S2000, this is 192 bytes), and stuff the remaining
+data through sox (SOund eXchange) which can then create a .wav from it.
+Example: Assume you have a file named "COOLDRUM.s", to convert it into
+a WAV you would do:
+
+dd if=COOLDRUM.s bs=192 skip=1 | sox -t raw -r 44100 -sw - cooldrum.wav
+
+Developers:
+
+If you are a developer, you will want to know about using the link library,
+liblakai.a. Please read the lakai.h header file and the example applications'
+source code to understand how it works. There is still lots of work to be
+done, but the current code is (I think) quite simple to understand and start
+with.
+
+
+Performance
+-----------
+
+My measurements so far gave up/download speed of up to 620 KBytes/sec which
+does not sound all that bad. The uploading of programs take relatively long,
+and clearing out the sampler when it carries a lot of data also takes quite
+long (at the beginning; the more samples get deleted, the faster it gets :-).
+I don't know right now if performance can still be increased, but I was told
+that with Windows or Mac software the speeds were similar or even lower,
+so I have quite a good feeling. This still means that entirely filling a
+fully equipped (32MByte RAM) S2000 takes about 1 minute.
+
+
+Known problems
+--------------
+
+- This is new code, only tested on MY equipment so far. For other setups 
+  it may work or not, but I hope for people to tell me about their success
+  stories. Do not use it for production systems yet until you feel somewhat
+  confident with these tools.
+  I am quite sure that at this point an S1000 will not work because it uses
+  a different "block" size for its basic data structures (keygroups, sample
+  headers, programs). This needs both testers and somecode changes. This 
+  might also be try for S2800/S3000/S3200 etc, none of which I own or 
+  could test.
+
+- Multis are not handled yet, neither in backup nor restore procedures.
+
+- I have seen problems in transferring large samples back to the PC (files
+  larger than about 7 MBytes). After some point, I only get 0 data. To
+  be examined. For smaller samples it has worked fine so far, though.
+
+- When sending a new sample, after creating the sample header but before
+  beginning the bulk data upload, I needed to put in a sleep(1) for now
+  which ruins upload speeds. Working on it.
+  Comparison: Loading a 16 MB sample bank (67 samples) from a 2x Apple
+  CD-ROM: 48 seconds; uploading through lakai: 1min48sec. Subtract the
+  67*1sec artifical delay, and I am close to the CD-ROM speed. Is even
+  higher throughput possible? To be determined...
+
+- No signal handling yet - specifically, when hitting Ctrl-C while a
+  backup or restore procedure is running, the sampler might stay in "MIDI
+  over SCSI" mode. Just don't do that for now.
+
+- Memory consumption is a little "generous" - to be optimized later.
+
+
+Reporting bugs
+--------------
+
+If you discover bugs in the source, documentation or elsewhere, or if you
+have something to add, please let me know: franky@users.sourceforge.net
+
+There is no mailing list or other discussion forum for this project yet; 
+also, there is no CVS repository yet. If the need arises, all of this 
+will probably be created, courtesy of SourceForge.
+
+
+Authors
+-------
+Frank Neumann <franky@users.sourceforge.net>
+
+
+Links
+-----
+
+Things you might be interested in:
+
+http://www.mda-vst.com/akai/index.htm
+Paul Kellett's pages on Akai disk formats etc
+
+http://www.nal.ics.es.osaka-u.ac.jp/~oosaki/akaitools/index.html
+Akaitools - Perl scripts to extract samples, programs and other
+data from Akai sample CD-ROMs
+
+http://llg.cubic.org/tools/
+The scsiadd tool
+
+http://www.8ung.at/klappnase/scsiaddgui/scsiaddgui.html
+scsiaddgui, a graphical frontend to scsiadd
+
+http://www.torque.net/sg
+The source of the kernel's sg driver
+
+http://www.linuxdj.com/audio/lad
+Home of the Linux Audio Developers (LAD)
+
+http://www.linuxsampler.org
+The Linuxsampler project - somewhat related
+
+http://lmuse.sourceforge.net
+The MusE MIDI/Audio sequencer - my tool of choice.
+
+http://www.alsa-project.org
+The Advanced Linux Sound Architecture. You want this.
+
+http://www.macanet.com
+Holds (among others) a nice selection of downloads drumkit set of old
+analog drum machines.
+
+http://home.sprynet.com/sprynet/cbagwell/projects.html
+sox, a universal sound sample translator
+
+
+Thanks
+------
+
+My thanks go to:
+- The people at SourceForge for putting up this great service of offering
+  free webspace, logistics etc. to open source projects.
+- 'Laslo' (you know who you are :-) for providing me with invaluable
+  information and source code to start this project.
+- Paul Kellett for his very helpful pages on the Akai disk and file formats
+- Akai themselves for actually making available some SysEx communication
+  protocols. Not as much as was really required, but a start.
+
+

Added: lakai/branches/upstream/current/ROADMAP
===================================================================
--- lakai/branches/upstream/current/ROADMAP	2004-10-28 08:35:57 UTC (rev 14)
+++ lakai/branches/upstream/current/ROADMAP	2004-10-28 09:32:22 UTC (rev 15)
@@ -0,0 +1,25 @@
+
+This is not really a roadmap, but rather a brain dump of what this
+package can do now and what it should perhaps be able to do at some
+point in the future. Let's call it "Dream Zone" :-).
+
+What I have now:
+- Backup from / restore to sampler. The very basic thing required to
+  call this package useful.
+
+What could be done in the future:
+- Next actual milestone: Tools to read lists of .wav files (read that as:
+  drumkits etc) that can be sent to the sampler in one go.
+
+- A graphical sampler editor a la MESA for Win****. That's already a big
+  project, and estimating from my development speed so far, this will take
+  me about 15-20 years :-). It should include functions like a keygroup 
+  editor with velocity, crossfades, layers and split handling, using mono
+  and stereo data, sample pool maintenance, project setups etc.
+
+- A tools that splits a drumploop into its beats and sends the distinct 
+  parts to the sampler. This allows Recycle-like pattern variations.
+
+- ..more ideas to come here in the future.
+
+

Added: lakai/branches/upstream/current/akaiascii.txt
===================================================================
--- lakai/branches/upstream/current/akaiascii.txt	2004-10-28 08:35:57 UTC (rev 14)
+++ lakai/branches/upstream/current/akaiascii.txt	2004-10-28 09:32:22 UTC (rev 15)
@@ -0,0 +1,42 @@
+00 0
+01 1
+02 2
+03 3
+04 4
+05 5
+06 6
+07 7
+08 8
+09 9
+0a ' '
+0b A
+0c B
+0d C
+0e D
+0f E
+10 F
+11 G
+12 H
+13 I
+14 J
+15 K
+16 L 
+17 M
+18 N
+19 O
+1a P
+1b Q
+1c R
+1d S
+1e T
+1f U
+20 V
+21 W
+22 X
+23 Y
+24 Z
+25 #
+26 +
+27 -
+28 .
+

Added: lakai/branches/upstream/current/lakai.c
===================================================================
--- lakai/branches/upstream/current/lakai.c	2004-10-28 08:35:57 UTC (rev 14)
+++ lakai/branches/upstream/current/lakai.c	2004-10-28 09:32:22 UTC (rev 15)
@@ -0,0 +1,1806 @@
+
+/*
+ * lakai.c - central liblakai implementation file
+ *
+ * Copyright (c) 2002-2004 Frank Neumann <franky@users.sourceforge.net>
+ * 
+ */
+
+/*** INCLUDES ***/
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <scsi/sg.h>
+#include <scsi/scsi.h>
+
+#include "lakai.h"
+
+/*** DEFINES ***/
+#define MAX_OPEN 16
+#define SCSI_CMD_LEN 6   /* all command blocks I produce are 6-byte commands */
+//#define TRANSFER_LIMIT 16382    /* Known to work; try larger values for better performance */
+#define TRANSFER_LIMIT_RECEIVE	65534
+#define TRANSFER_LIMIT_SEND		65534
+#define LAK_DIR_IN 0   /* incoming data packets */
+#define LAK_DIR_OUT 1  /* outgoing data packets */
+
+/*** GLOBAL VARIABLES ***/
+static int open_cnt = -1;
+static int fhandles[MAX_OPEN];
+
+/*** PROTOTYPES ***/
+static int send_command(int sg_fd, char *cmdblk, int dirflag,
+		void *tosamp_ptr, int tosamp_len,
+		void *fromsamp_ptr, int fromsamp_len);
+static void hexprint(unsigned char *buf, int len);
+static void hexprint_midi(unsigned char *buf, int len);
+static int sendrecv_scsimidi(LHANDLE handle, char *cmdblk, unsigned int blksize, char *destbuf);
+static int sendrecv_scsimidi2(LHANDLE handle, char *cmdblk, unsigned int blksize, char *buf);
+static long get_availbytes(LHANDLE handle);
+static long getbulkdata(LHANDLE handle, int numbytes, char *buf);
+static long putbulkdata(LHANDLE handle, int numbytes, char *buf);
+
+
+/*
+ * lakai_init() - do a few generic library initializations
+ * TODO: Still to be fixed when going for .so.
+ */
+
+void lakai_init()
+{
+	int i;
+	/* currently no open files, clear all handle slots */
+	if (open_cnt == -1)
+	{
+		for (i = 0; i < MAX_OPEN; i++)
+			fhandles[i] = -1;
+	}
+}
+
+/*
+ * lakai_open() - open a given SCSI generic file, perform some tests.
+ * Returns LHANDLE on success, -1 on failure.
+ */
+
+LHANDLE lakai_open(char *devname)
+{
+	int sg_fd, k, slotpos;
+	struct sg_scsi_id sg_scsiid;
+
+	/* search for a free "slot" */
+	if (open_cnt == MAX_OPEN-1)
+	{
+		fprintf(stderr, "lakai_open: Maximum open count of liblakai reached.\n");
+		return -1;
+	}
+
+	for (slotpos = 0; slotpos < MAX_OPEN; slotpos++)
+	{
+		if (fhandles[slotpos] == -1)
+			break;
+	}
+	if (slotpos == MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_open: no free slot found\n");
+		return -1;
+	}
+	
+	
+	sg_fd = open(devname, O_RDWR);
+	if (sg_fd < 0)
+	{
+		fprintf(stderr, "lakai_open: error opening file: %s\n", devname);
+		return -1;
+	}
+    /* Check if we have a new (sg3) device driver */
+    if (ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0)
+	 {
+		fprintf(stderr, "Unable to do ioctl() SG_GET_VERSION_NUM on sg device\n");
+	 	return -1;
+	}
+		 
+	if ((k < 30000))
+	{
+		fprintf(stderr, "sg device's version is too old (below version 3)\n");
+		fprintf(stderr, "Please get a more recent kernel\n");
+		close(sg_fd);
+		return -1;
+    }
+
+	/* Check if device is a processor device */
+    if (ioctl(sg_fd, SG_GET_SCSI_ID, &sg_scsiid) < 0)
+	 {
+		fprintf(stderr, "Unable to do ioctl() SG_GET_SCSI_ID on sg device\n");
+		close(sg_fd);
+		return -1;
+	}
+
+#if DEBUG
+	fprintf(stderr, "SCSI_ID data:\n");
+	fprintf(stderr, "Host #          : %d\n", sg_scsiid.host_no);
+	fprintf(stderr, "Channel         : %d\n", sg_scsiid.channel);
+	fprintf(stderr, "SCSI ID         : %d\n", sg_scsiid.scsi_id);
+	fprintf(stderr, "LUN             : %d\n", sg_scsiid.lun);
+	fprintf(stderr, "SCSI Type       : %d\n", sg_scsiid.scsi_type);
+	fprintf(stderr, "Maxcmds per LUN : %d\n", sg_scsiid.h_cmd_per_lun);
+	fprintf(stderr, "Max Queue length: %d\n", sg_scsiid.d_queue_depth);
+#endif
+	if (sg_scsiid.scsi_type != TYPE_PROCESSOR)
+	{
+		fprintf(stderr, "Incorrect device type - should be a PROCESSOR device\n");
+		close(sg_fd);
+		return -1;
+	}
+
+	/* open succeeded, ready to go */
+	open_cnt++;
+	fhandles[slotpos] = sg_fd;
+
+	return slotpos;
+}
+
+
+/*
+ * lakai_close() - close a previously opened lakai device
+ * Returns 0 on success, -1 on failure.
+ */
+
+int lakai_close(LHANDLE handle)
+{
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_close: Bad file handle\n");
+		return -1;
+	}
+	close(fhandles[handle]);
+	fhandles[handle] = -1;
+	open_cnt--;
+	return 0;
+}
+
+
+/*
+ * lakai_setmode() - sets the mode of the sampler.
+ * Returns 0 on success, -1 on failure. See LAKAI_MODE_* in lakai.h
+ */
+
+int lakai_setmode(LHANDLE handle, int mode)
+{
+	static unsigned char cmdblk[SCSI_CMD_LEN] = {
+		S2000_MODE, /* command */
+		0,				/* lun/AEN */
+		1, 			/* len msb */  /* gets set to correct mode before usage */
+		0,				/* len */
+		0,				/* len lsb */
+		0 };			/* control */
+
+	/* 
+	 * Possible mode settings:
+	 * MIDI:  cmdblk[2] = 0 ("MIDIMode"), cmdblk[3] = 0 (dummy)
+	 * SCSI MIDI: cmdblk[2] = 1 ("SCSIMode") + cmdblk[3] = 0 ("SCSI_MIDI")
+	 * SCSI BULK: cmdblk[2] = 1 ("SCSIMode") + cmdblk[3] = 1 ("SCSI_Bulk")
+	 */
+	if (mode == LAKAI_MODE_NORMAL)
+	{
+		cmdblk[2]= 0;
+		cmdblk[3] = 0;
+	}
+	else if (mode == LAKAI_MODE_SCSI_MIDI)
+	{
+		cmdblk[2] = 1;
+		cmdblk[3] = 0;
+	}
+	else if (mode == LAKAI_MODE_SCSI_BULK)
+	{
+		cmdblk[2] = 1;
+		cmdblk[3] = 1;
+	}
+	else
+	{
+		fprintf(stderr, "Illegal mode in lakai_setmode()\n");
+		return -1;
+	}
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_setmode: Bad file handle\n");
+		return -1;
+	}
+
+	if (send_command(fhandles[handle], cmdblk, LAK_DIR_OUT, NULL, 0, NULL, 0) != 0)
+	{
+		fprintf(stderr, "lakai_setmode() FAILED\n");
+		return -1; /* error */
+	}
+	return 0; /* success */
+}
+
+
+/*
+ * lakai_getstatus() - gets the sampler's status
+ * Returns 0 on success, -1 on failure.
+ */
+int lakai_get_status_report(LHANDLE handle, LakaiStatus *ls)
+{
+	unsigned char statusbuf[100];
+	int statuslen;
+
+	static unsigned char cmdblk1[] = {
+		0xf0, 0x47, 0x00, LC_RSTAT, 0x48, 0xf7};	/* SysEx: "Get Status" */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_get_status_report(): Bad file handle\n");
+		return -1;
+	}
+
+	/* TODO Question: Can I make lakai_setmode() an internal function? */
+	/* TODO: Should I reduce the # of setmode() calls during transfers? */
+
+	statuslen = sendrecv_scsimidi(handle, cmdblk1, sizeof(cmdblk1), statusbuf);
+	if (statuslen > 0)
+	{
+#if DEBUG
+		fprintf(stderr, "lakai_get_status_report() succeeded; hexdump of result follows:\n");
+		hexprint(statusbuf, statuslen);
+#endif
+	}
+	else
+	{
+		fprintf(stderr, "Seems there were problems in receiving..\n");
+	}
+
+	/* TODO: parse MIDI data, fill LakaiStatus struct */
+	/* TODO: also in here: check if answer type is correct (STAT) */
+
+	return 0; /* success */
+}
+
+/*
+ * lakai_get_program_names() - gets the list of resident program names
+ * Returns 0 on success, -1 on failure.
+ */
+int lakai_get_program_list(LHANDLE handle, LakaiProgramList *lp)
+{
+	unsigned char finalbuf[2048];
+	int i, finallen, numprogs;
+
+	static unsigned char cmdblk1[] = {
+		0xf0, 0x47, 0x00, LC_RPLIST, 0x48, 0xf7};	/* SysEx: "req. list of res. prg names" */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_get_program_list(): Bad file handle\n");
+		return -1;
+	}
+
+	finallen = sendrecv_scsimidi(handle, cmdblk1, sizeof(cmdblk1), finalbuf);
+	if (finallen > 0)
+	{
+#if DEBUG
+		fprintf(stderr, "lakai_get_program_list() succeeded; hexdump of result follows:\n");
+		hexprint(finalbuf, finallen);
+#endif
+	}
+	else
+	{
+		fprintf(stderr, "Seems there were problems in receiving..\n");
+	}
+	
+	numprogs = finalbuf[5] + 128 * finalbuf[6];
+
+	if (*finalbuf == 0xf0 && *(finalbuf+3) == LC_PLIST)  /* SysEx/PLIST ok */
+	{
+		lp->prognames = malloc(numprogs* sizeof (char *));
+		if (!lp->prognames)
+		{
+			fprintf(stderr, "lakai_get_program_list(): Unable to malloc prognames\n");
+			return -1;
+		}
+		for (i = 0; i < numprogs; i++)
+		{
+			lp->prognames[i] = malloc(13); /* program names are 12 bytes long + '\0' */
+			if (!lp->prognames[i])
+			{
+				fprintf(stderr, "lakai_get_program_list(): Unable to malloc progname slot\n");
+				return -1;
+			}
+			lakai_akaitoascii(finalbuf+7+(i*12), lp->prognames[i], 12);
+			*(lp->prognames[i]+12) = '\0';
+		}
+		lp->numprogs = numprogs;
+	}
+	else
+		fprintf(stderr, "lakai_get_program_list(): bad answer from sampler\n");
+
+	return numprogs; /* success */
+}
+
+
+/*
+ * lakai_free_program_list() - release memory occupied by lakai_get_program_list()
+ */
+void lakai_free_program_list(LakaiProgramList *lp)
+{
+	int i;
+	
+	if (lp)
+	{
+		if (lp->numprogs > 0)
+		{
+			for (i = 0; i < lp->numprogs; i++)
+			{
+				if (lp->prognames[i])
+				{
+//					fprintf(stderr, "Freeing mem at $%lx\n", lp->prognames[i]);
+					free(lp->prognames[i]);
+				}
+			}
+//			fprintf(stderr, "Freeing pointer list at $%lx\n", lp->prognames);
+			free(lp->prognames);
+		}
+	}
+}
+
+/*
+ * lakai_get_sample_list() - gets the list of resident sample names
+ * Returns # of samples on success, -1 on failure.
+ */
+int lakai_get_sample_list(LHANDLE handle, LakaiSampleList *ls)
+{
+	unsigned char finalbuf[2048];
+	int i, finallen, numsamples;
+
+	static unsigned char cmdblk1[] = {
+		0xf0, 0x47, 0x00, LC_RSLIST, 0x48, 0xf7};	/* SysEx: "req. list of res. sample names" */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_get_sample_list(): Bad file handle\n");
+		return -1;
+	}
+
+	finallen = sendrecv_scsimidi(handle, cmdblk1, sizeof(cmdblk1), finalbuf);
+	if (finallen > 0)
+	{
+#if DEBUG
+		fprintf(stderr, "lakai_get_sample_list() succeeded; hexdump of result follows:\n");
+		hexprint(finalbuf, finallen);
+#endif
+	}
+	else
+	{
+		fprintf(stderr, "Seems there were problems in receiving..\n");
+	}
+	
+	numsamples = finalbuf[5] + 128 * finalbuf[6];
+
+	if (*finalbuf == 0xf0 && *(finalbuf+3) == LC_SLIST)  /* SysEx/SLIST ok */
+	{
+		ls->samplenames = malloc(numsamples* sizeof (char *));
+		if (!ls->samplenames)
+		{
+			fprintf(stderr, "lakai_get_sample_list(): Unable to malloc samplenames\n");
+			return -1;
+		}
+		for (i = 0; i < numsamples; i++)
+		{
+			ls->samplenames[i] = malloc(13); /* sample names are 12 bytes long + '\0' */
+			if (!ls->samplenames[i])
+			{
+				fprintf(stderr, "lakai_get_sample_list(): Unable to malloc samplename slot\n");
+				return -1;
+			}
+			lakai_akaitoascii(finalbuf+7+(i*12), ls->samplenames[i], 12);
+			*(ls->samplenames[i]+12) = '\0';
+		}
+		ls->numsamples = numsamples;
+	}
+	else
+	{
+		fprintf(stderr, "lakai_get_sample_list(): bad answer from sampler\n");
+		return -1;
+	}
+		
+	return numsamples; /* success */
+}
+
+
+/*
+ * lakai_free_sample_list() - release memory occupied by lakai_get_sample_list()
+ */
+void lakai_free_sample_list(LakaiSampleList *ls)
+{
+	int i;
+	
+	if (ls)
+	{
+		if (ls->numsamples > 0)
+		{
+			for (i = 0; i < ls->numsamples; i++)
+			{
+				if (ls->samplenames[i])
+				{
+//					fprintf(stderr, "Freeing mem at $%lx\n", ls->samplenames[i]);
+					free(ls->samplenames[i]);
+				}
+			}
+//			fprintf(stderr, "Freeing pointer list at $%lx\n", ls->samplenames);
+			free(ls->samplenames);
+		}
+	}
+}
+
+
+/*
+ * int lakai_get_program() - gets one program's common data from the sampler
+ * and puts the data into the supplied LakaiProgram structure. This does not
+ * transfer the keygroup(s) - these will have to be retrieved through the
+ * corresponding lakai_get_keygroup(..) function.
+ */
+int lakai_get_program(LHANDLE handle, int prognum, unsigned char *data)
+{
+	unsigned char finalbuf[1024];
+	int i, finallen, numvals;
+
+	static unsigned char cmdblk1[] = {
+		/* SysEx: "receive prog common data" */
+		0xf0, 0x47, 0x00, LC_RPDATA, 0x48, 0x00, 0x00, 0xf7};
+		/* the program number is filled in bytes 5/6 (counting from 0) later */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_get_program(): Bad file handle\n");
+		return -1;
+	}
+
+	if (prognum < 0 || prognum > 16383)
+	{
+		fprintf(stderr, "lakai_get_program(): Illegal program number\n");
+		return -1;
+	}
+
+	cmdblk1[6] = prognum / 128;
+	cmdblk1[5] = prognum - (cmdblk1[6] *128);
+
+	finallen = sendrecv_scsimidi(handle, cmdblk1, sizeof(cmdblk1), finalbuf);
+	if (finallen <= 0)
+	{
+		fprintf(stderr, "Seems there were problems in receiving..\n");
+		return -1;
+	}
+
+#if DEBUG
+	fprintf(stderr, "lakai_get_program() succeeded; hexdump of result follows:\n");
+	hexprint(finalbuf, finallen);
+#endif
+
+	numvals = (finallen - 8) / 2;   /* subtract SysEx header/EOX */
+	for (i = 0; i < numvals; i++)
+	{
+		data[i] = (finalbuf[7 + (i*2)]) | (finalbuf[7 + (i*2) + 1 ] << 4);
+	}
+
+#if DEBUG
+	fprintf(stderr, "Decoded buffer dump:\n");
+	hexprint(data, numvals);
+#endif
+	
+	/* TODO (done): Fuer alle Funktionen, die über mehrere Teile hinweg Daten anfordern: 
+	 * Diese Funktion ist zentralisierbar, sofern grob die max. Datenmenge 
+	 * abgeschätzt werden kann. Einfach receive() bis status == 0 liefert.
+	 * Obere Abschätzung für Datenmenge:
+	 * - Get Status: trivial (single pass)
+	 * - GetProgramNames: max. ca 500 Namen? + Header/EOX
+	 * - GetSampleNames: dto.
+	 * - GetProgramCommon: fix
+	 * - GetSampleHeader: fix
+	 * - GetKeygroup: Fix
+	 * - GetPCMData/.. : Anderer Transfer-Mechanismus
+	 *
+	 * Also: Die aufrufende Funktion bereitet ihren cmdblk[..] vor, trägt alles
+	 * nötige dort bereits ein (cmd-Länge, Program# etc) und liefert das 
+	 * zusammen mit einem char * für die Zieldaten ab -> get_scsimididata(...)
+	 *
+	 */
+
+	/* TODO: Hier weiter: LakaiProgram-Struktur auffüllen */
+	/* Wirklich so? Oder soll die App die "rohen" Daten bekommen? */
+	/* Argumente:
+	 * - pro "raw": Die Daten auf Platte sollen kompatibel zu denen sein, die
+	 * von cdXtract geschrieben werden. DIE sind zwar zusammengehaengt, aber
+	 * eben raw. Beim spaeteren Restore-to-Sampler kann man die Daten mit
+	 * minimalem Umwandlungs-Aufwand zurueckschicken.
+	 * - pro "per-cooked": Die App soll spaeter die Daten dem Benutzer per GUI
+	 * anbieten; dafuer muessen sie auf jeden Fall pre-cooked sein. Diese
+	 * Arbeit wuerde dann nicht von der App, sondern von der Lib erledigt werden.
+	 *
+	 * Dritte Loesung: Die Lib bietet beides an - sowohl das Ausgeben der rohen Daten
+	 * als auch (mit einer weiteren Funktion) das Umwandeln eines raw-Blocks in
+	 * eine ausgefuellte Struktur. Klingt gut und sinnvoll!
+	 * Dann braucht man allerdings natuerlich Konverter in beide Richtungen,
+	 * xxx_tohw und xxx_fromhw.
+	 * Diese Konverter sind dann noetig fuer Status (OK), MiscData (trivial),
+	 * Program, SampleHeader, Keygroup, DrumSettings.
+	 */
+
+//	lpr->groups = f2buf[42];  /* number of keygroups */
+
+	return numvals;  /* TODO */
+}
+
+
+/*
+ * int lakai_get_keygroup() - gets one keygroup of one program from the sampler
+ * and puts the data into the supplied LakaiKeygroup structure. Usually, you will first
+ * get a program, determine its number of keygroups (lk->groups attribute) and then
+ * retrieve/store all this data or process it further.
+ */
+int lakai_get_keygroup(LHANDLE handle, int prognum, int keygroupnum, unsigned char *data)
+{
+	unsigned char finalbuf[1024];
+	int i, finallen, numvals;
+
+	static unsigned char cmdblk1[] = {
+		0xf0, 0x47, 0x00, LC_RKDATA, 0x48, 0x00, 0x00, 0x00, 0xf7};
+		/* SysEx: "receive keygroup data" */
+		/* the program number and keygroup numbers get filled in bytes 5/6/7 (counting
+		 * from 0) later
+		 */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_get_keygroup(): Bad file handle\n");
+		return -1;
+	}
+
+	if (prognum < 0 || prognum > 16383)
+	{
+		fprintf(stderr, "lakai_get_keygroup(): Illegal program number\n");
+		return -1;
+	}
+
+	cmdblk1[6] = prognum / 128;
+	cmdblk1[5] = prognum - (cmdblk1[6] *128);
+
+	if (keygroupnum < 0 || keygroupnum > 99)
+	{
+		fprintf(stderr, "lakai_get_keygroup(): Illegal keygroup number\n");
+		return -1;
+	}
+
+	cmdblk1[7] = keygroupnum;
+
+	finallen = sendrecv_scsimidi(handle, cmdblk1, sizeof(cmdblk1), finalbuf);
+	if (finallen <= 0)
+	{
+		fprintf(stderr, "Seems there were problems in receiving..\n");
+		return -1;
+	}
+
+#if DEBUG
+	fprintf(stderr, "lakai_get_keygroup() succeeded; hexdump of result follows:\n");
+	hexprint(finalbuf, finallen);
+#endif
+
+	numvals = (finallen - 9) / 2;   /* subtract SysEx header/EOX */
+	for (i = 0; i < numvals; i++)
+	{
+		data[i] = (finalbuf[8 + (i*2)]) | (finalbuf[8 + (i*2) + 1 ] << 4);
+	}
+		
+#if DEBUG
+	fprintf(stderr, "Decoded buffer dump:\n");
+	hexprint(data, numvals);
+#endif
+
+	return numvals;
+}
+
+/*
+ * int lakai_get_sample_header() - gets the sample header of one sample
+ * and puts the data into the supplied buffer. Usually, you will first
+ * need to find out how many samples are currently loaded into the sampler with
+ * lakai_get_sample_list().
+ * TODO: Please note that this function will return the raw data in the supplied buffer; no
+ * interpretation of the data is performed. For that, another function (something like
+ * lakai_shdr_to_struct() ) will have to be written which would then convert it all
+ * into a LakaiSampleHeader struct.
+ */
+int lakai_get_sample_header(LHANDLE handle, int samplenum, unsigned char *data)
+{
+	unsigned char finalbuf[1024];
+	int i, finallen, numvals;
+
+	static unsigned char cmdblk1[] = {
+		0xf0, 0x47, 0x00, LC_RSDATA, 0x48, 0x00, 0x00, 0xf7};
+		/* SysEx: "receive sample header data" */
+		/* the sample number gets filled in bytes 5/6 (counting
+		 * from 0) later
+		 */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_get_sample_header(): Bad file handle\n");
+		return -1;
+	}
+
+	if (samplenum < 0 || samplenum > 16383)
+	{
+		fprintf(stderr, "lakai_get_sample_header(): Illegal sample number\n");
+		return -1;
+	}
+
+	cmdblk1[6] = samplenum / 128;
+	cmdblk1[5] = samplenum - (cmdblk1[6] *128);
+
+	finallen = sendrecv_scsimidi(handle, cmdblk1, sizeof(cmdblk1), finalbuf);
+	if (finallen <= 0)
+	{
+		fprintf(stderr, "Seems there were problems in receiving..\n");
+		return -1;
+	}
+
+#if DEBUG
+	fprintf(stderr, "lakai_get_sample_header() succeeded; hexdump of result follows:\n");
+	hexprint(finalbuf, finallen);
+#endif
+
+	numvals = (finallen - 8) / 2;   /* subtract SysEx header/EOX */
+	for (i = 0; i < numvals; i++)
+	{
+		data[i] = (finalbuf[7 + (i*2)]) | (finalbuf[7 + (i*2) + 1 ] << 4);
+	}
+		
+#if DEBUG
+	fprintf(stderr, "Decoded buffer dump:\n");
+	hexprint(f2buf, numvals);
+#endif
+	
+	/* TODO: Finish this */
+	/* slocat and slngth are multiplied by 2 to have them in bytes, not words. */
+#if 0
+	lsh->slocat = (f2buf[25] * 16777216 + f2buf[24] * 65536 + f2buf[23] * 256 + f2buf[22]) * 2;
+	lsh->slngth = (f2buf[29] * 16777216 + f2buf[28] * 65536 + f2buf[27] * 256 + f2buf[26]) * 2;
+#endif
+	
+	return numvals;
+}
+
+
+/*
+ * int lakai_put_sample_header() - sends a sample header for one sample
+ * to the supplied buffer. Usually, you will first
+ * need to find out how many samples are currently loaded into the sampler with
+ * lakai_get_sample_list().
+ *
+ * By sending a sample header with a large number (larger than the largest
+ * sampler currently resident in the sampler) you create a new sample header
+ * entry. The actual sample PCM data is then to be sent right afterwards.
+ */
+int lakai_put_sample_header(LHANDLE handle, int samplenum, unsigned char *data, int len)
+{
+	unsigned char finalbuf[1024];
+	unsigned char resbuf[1024];
+	int i, finallen, numvals;
+
+	static unsigned char cmdblk1[] = {
+		0xf0, 0x47, 0x00, LC_SDATA, 0x48, 0x00, 0x00};
+		/* SysEx: "sample header data" */
+		/* the sample number gets filled in bytes 5/6 (counting
+		 * from 0) later
+		 */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_put_sample_header(): Bad file handle\n");
+		return -1;
+	}
+
+	if (samplenum < 0 || samplenum > 16383)
+	{
+		fprintf(stderr, "lakai_put_sample_header(): Illegal sample number\n");
+		return -1;
+	}
+
+	cmdblk1[6] = samplenum / 128;
+	cmdblk1[5] = samplenum - (cmdblk1[6] *128);
+
+	/* assemble the whole message from header, payload and EOX */
+	memcpy(finalbuf, cmdblk1, sizeof(cmdblk1));
+	
+	for (i = 0; i < len; i++)
+	{
+		finalbuf[sizeof(cmdblk1) + i*2] = data[i] & 0x0f;
+		finalbuf[sizeof(cmdblk1) + i*2 + 1] = (data[i] & 0xf0) >> 4;
+	}
+	finalbuf[sizeof(cmdblk1)+2*len] = 0xf7; /* EOX */
+
+
+	finallen = sendrecv_scsimidi(handle, finalbuf, sizeof(cmdblk1)+(len*2)+1, resbuf);
+
+	if (finallen <= 0)
+	{
+		fprintf(stderr, "Seems there were problems in receiving..\n");
+		return -1;
+	}
+	
+	/* TODO: return real return code here */
+	return numvals;
+}
+
+/*
+ * int lakai_delete_program() - delete a program and all keygroups associated with it.
+ * Will not touch any samples or sample header data.
+ */
+int lakai_delete_program(LHANDLE handle, int prognum)
+{
+	unsigned char statusbuf[100];
+	int statuslen;
+
+	static unsigned char cmdblk1[] = {
+		0xf0, 0x47, 0x00, LC_DELP, 0x48, 0x00, 0x00, 0xf7};	/* SysEx: "Delete Program" */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_delete_program(): Bad file handle\n");
+		return -1;
+	}
+	
+	if (prognum < 0 || prognum > 16383)
+	{
+		fprintf(stderr, "lakai_delete_program(): Illegal program number\n");
+		return -1;
+	}
+
+	cmdblk1[6] = prognum / 128;
+	cmdblk1[5] = prognum - (cmdblk1[6] *128);
+
+	statuslen = sendrecv_scsimidi(handle, cmdblk1, sizeof(cmdblk1), statusbuf);
+	/* TODO: Check return code (statusbuf[5]) for error and hand that upwards */
+	if (statuslen > 0)
+	{
+#if DEBUG
+		fprintf(stderr, "lakai_delete_program() succeeded; hexdump of result follows:\n");
+		hexprint(statusbuf, statuslen);
+#endif
+	}
+	else
+	{
+		fprintf(stderr, "Seems there were problems in receiving..\n");
+	}
+
+	return 0; /* success */
+}
+
+
+/*
+ * int lakai_delete_keygroup() - delete a keygroup within a program.
+ * Will not touch any samples or sample header data.
+ */
+int lakai_delete_keygroup(LHANDLE handle, int prognum, int kgrpnum)
+{
+	unsigned char statusbuf[100];
+	int statuslen;
+
+	static unsigned char cmdblk1[] = {
+		0xf0, 0x47, 0x00, LC_DELK, 0x48, 0x00, 0x00, 0x00, 0xf7};	/* SysEx: "Delete Keygroup" */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_delete_keygroup(): Bad file handle\n");
+		return -1;
+	}
+	
+	if (prognum < 0 || prognum > 16383)
+	{
+		fprintf(stderr, "lakai_delete_keygroup(): Illegal program number\n");
+		return -1;
+	}
+
+	if (kgrpnum < 0 || kgrpnum > 99)
+	{
+		fprintf(stderr, "lakai_delete_keygroup(): Illegal keygroup number\n");
+		return -1;
+	}
+	cmdblk1[6] = prognum / 128;
+	cmdblk1[5] = prognum - (cmdblk1[6] *128);
+
+	cmdblk1[7] = kgrpnum;
+	
+	statuslen = sendrecv_scsimidi(handle, cmdblk1, sizeof(cmdblk1), statusbuf);
+	/* TODO: Check return code (statusbuf[5]) for error and hand that upwards */
+	if (statuslen > 0)
+	{
+#if DEBUG
+		fprintf(stderr, "lakai_delete_keygroup() succeeded; hexdump of result follows:\n");
+		hexprint(statusbuf, statuslen);
+#endif
+	}
+	else
+	{
+		fprintf(stderr, "Seems there were problems in receiving..\n");
+	}
+
+	return 0; /* success */
+	
+}
+
+
+/*
+ * int lakai_delete_sample() - delete a sample header and its
+ * associated sample PCM data.
+ * Will not touch any programs or keygroups.
+ */
+int lakai_delete_sample(LHANDLE handle, int samplenum)
+{
+	unsigned char statusbuf[100];
+	int statuslen;
+
+	static unsigned char cmdblk1[] = {
+		0xf0, 0x47, 0x00, LC_DELS, 0x48, 0x00, 0x00, 0xf7};	/* SysEx: "Delete Sample" */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_delete_sample(): Bad file handle\n");
+		return -1;
+	}
+	
+	if (samplenum < 0 || samplenum > 16383)
+	{
+		fprintf(stderr, "lakai_delete_sample(): Illegal sample number\n");
+		return -1;
+	}
+
+	cmdblk1[6] = samplenum / 128;
+	cmdblk1[5] = samplenum - (cmdblk1[6] *128);
+
+	statuslen = sendrecv_scsimidi(handle, cmdblk1, sizeof(cmdblk1), statusbuf);
+	/* TODO: Check return code (statusbuf[5]) for error and hand that upwards */
+	if (statuslen > 0)
+	{
+#if DEBUG
+		fprintf(stderr, "lakai_delete_sample() succeeded; hexdump of result follows:\n");
+		hexprint(statusbuf, statuslen);
+#endif
+	}
+	else
+	{
+		fprintf(stderr, "Seems there were problems in receiving..\n");
+	}
+
+	return 0; /* success */
+}
+
+/*
+ * lakai_get_sample() - perform the actual bulk data transfer
+ * from the sampler to the PC.
+ * The supplied buffer should be big enough to hold "len" bytes.
+ */
+
+long lakai_get_sample(LHANDLE handle, int samplenum, char *buffer, long locat, long len)
+{
+	int availbytes, stilltoget, getnow, ret;
+	unsigned char statusbuf[100];
+	unsigned char *ptr;
+	unsigned char cmdblk1[] = {
+		/* SysEx: "Request Sample Packets"; bytes 0x05 and 0x06 contain sample # */
+		0xf0, 0x47, 0x00, LC_RSPACK, 0x48,
+		0x00,	0x00, /* sample # */
+		0x00, 0x00, 0x00, 0x00, /* start address */
+		0x00, 0x00, 0x00, 0x00, /* # of samples */
+		0x01, 0x00, 0xf7 }; /* interval etc */
+	
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_get_sample: Bad file handle\n");
+		return -1;
+	}
+
+	/* put in sample number */
+	cmdblk1[5] = samplenum & 0x7f;
+	cmdblk1[6] = (samplenum >> 7) & 0x7f;
+
+	/* put in locat and len in 7-bit notation ( if required; should be 0 normally) */
+	cmdblk1[7] = (locat & 0x7f);
+	cmdblk1[8] = (locat >> 7) & 0x7f;
+	cmdblk1[9] = (locat >> 14) & 0x7f;
+	cmdblk1[10] = (locat >> 21) & 0x7f;
+	
+	cmdblk1[11] = len & 0x7f;
+	cmdblk1[12] = (len >> 7) & 0x7f;
+	cmdblk1[13] = (len >> 14) & 0x7f;
+	cmdblk1[14] = (len >> 21) & 0x7f;
+	
+	lakai_setmode(handle, LAKAI_MODE_SCSI_BULK); /* switch to BULK transfer mode */
+
+	availbytes =  sendrecv_scsimidi2(handle, cmdblk1, sizeof(cmdblk1), statusbuf);
+	if (availbytes > 0)
+	{
+#if DEBUG
+		fprintf(stderr, "lakai_get_sample() succeeded; %d bytes waiting\n", availbytes);
+#endif
+	}
+	else
+	{
+		fprintf(stderr, "Seems there were problems in receiving..\n");
+	}
+
+	stilltoget = availbytes;
+	ptr = buffer;
+	while (stilltoget > 0)
+	{
+		if (stilltoget < TRANSFER_LIMIT_RECEIVE)
+			getnow = stilltoget;
+		else
+			getnow = TRANSFER_LIMIT_RECEIVE;
+		ret = getbulkdata(handle, getnow, ptr);
+#if DEBUG
+		fprintf(stderr, "This getbulkdata() call yielded %d bytes\n", ret);
+#endif
+		stilltoget -= getnow;
+		ptr += getnow;
+	}
+
+	lakai_setmode(handle, LAKAI_MODE_NORMAL); /* switch back to normal */
+
+	return 0; /* TODO */
+}
+
+
+/*
+ * int lakai_get_miscdata() - retrieves miscellaneous data from the sampler
+ */
+int lakai_get_miscdata(LHANDLE handle, unsigned char *data)
+{
+	unsigned char finalbuf[1024];
+	int i, finallen, numvals;
+
+	static unsigned char cmdblk1[] = {
+		0xf0, 0x47, 0x00, LC_RMDATA, 0x48, 0xf7}; /* SysEx: "request misc data" */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_get_get_miscata(): Bad file handle\n");
+		return -1;
+	}
+
+	finallen = sendrecv_scsimidi(handle, cmdblk1, sizeof(cmdblk1), finalbuf);
+	if (finallen <= 0)
+	{
+		fprintf(stderr, "Seems there were problems in receiving..\n");
+		return -1;
+	}
+
+#if DEBUG
+	fprintf(stderr, "lakai_get_miscdata() succeeded; hexdump of result follows:\n");
+	hexprint(finalbuf, finallen);
+#endif
+
+	numvals = (finallen - 6) / 2;   /* subtract SysEx header/EOX */
+	for (i = 0; i < numvals; i++)
+	{
+		data[i] = (finalbuf[5 + (i*2)]) | (finalbuf[5 + (i*2) + 1 ] << 4);
+	}
+		
+#if DEBUG
+	fprintf(stderr, "Decoded buffer dump:\n");
+	hexprint(data, numvals);
+#endif
+
+	return numvals;
+}
+
+
+
+/* 
+ * int lakai_put_program() - create a new program or replace an existing
+ */
+int lakai_put_program(LHANDLE handle, int prognum, unsigned char *data, int len)
+{
+	unsigned char finalbuf[1024];
+	unsigned char resbuf[128];
+	int i, finallen;
+
+	static unsigned char cmdblk1[] = {
+		/* SysEx: "send prog common data" */
+		0xf0, 0x47, 0x00, LC_PDATA, 0x48, 0x00, 0x00 };
+		/* the program number is filled in bytes 5/6 (counting from 0) later,
+		 * EOX is appended after the payload has been converted to nibble format */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_put_program(): Bad file handle\n");
+		return -1;
+	}
+
+	if (prognum < 0 || prognum > 16383)
+	{
+		fprintf(stderr, "lakai_put_program(): Illegal program number\n");
+		return -1;
+	}
+
+#if DEBUG
+	fprintf(stderr, "Input buffer dump:\n");
+	hexprint(data, len);
+#endif
+	
+	cmdblk1[6] = prognum / 128;
+	cmdblk1[5] = prognum - (cmdblk1[6] * 128);
+
+	/* assemble the whole message from header, payload and EOX */
+	memcpy(finalbuf, cmdblk1, sizeof(cmdblk1));
+	
+	for (i = 0; i < len; i++)
+	{
+		finalbuf[sizeof(cmdblk1) + i*2] = data[i] & 0x0f;
+		finalbuf[sizeof(cmdblk1) + i*2 + 1] = (data[i] & 0xf0) >> 4;
+	}
+	finalbuf[sizeof(cmdblk1)+2*len] = 0xf7; /* EOX */
+
+#if DEBUG
+	fprintf(stderr, "Encoded buffer dump:\n");
+	hexprint(finalbuf, sizeof(cmdblk1)+1+len*2);
+#endif
+
+	finallen = sendrecv_scsimidi(handle, finalbuf, sizeof(cmdblk1)+(len*2)+1, resbuf);
+	if (finallen <= 0)
+	{
+		fprintf(stderr, "lakai_put_program(): Seems there were problems in receiving..\n");
+		return -1;
+	}	
+#if DEBUG
+	else
+	{
+		fprintf(stderr, "lakai_put_program: resbuf dump:\n");
+		hexprint(resbuf, finallen);
+	}
+#endif
+	return 0;
+}
+
+
+
+/*
+ * int lakai_put_keygroup() - create a new keygroup in a program or 
+ * replace an existing keygroup
+ * Use prognum = 255 to replace/create a keygroup in the previously created program
+ */
+
+int lakai_put_keygroup(LHANDLE handle, int prognum, int kgrpnum, unsigned char *data, int len)
+{
+	unsigned char finalbuf[1024];
+	unsigned char resbuf[128];
+	int i, finallen;
+
+	static unsigned char cmdblk1[] = {
+		/* SysEx: "send keygroup data" */
+		0xf0, 0x47, 0x00, LC_KDATA, 0x48, 0x00, 0x00, 0x00 };
+		/* the program number and keygroup number are filled in 
+		 * bytes 5/6 resp 7 (counting from 0) later, EOX is appended 
+		 * after the payload has been converted to nibble format
+		 */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_put_keygroup(): Bad file handle\n");
+		return -1;
+	}
+
+	if (prognum < 0 || prognum > 16383)
+	{
+		fprintf(stderr, "lakai_put_keygroup(): Illegal program number\n");
+		return -1;
+	}
+	
+	if (kgrpnum > 255 )
+	{
+		fprintf(stderr, "lakai_put_keygroup(): Illegal keygroup number\n");
+		return -1;
+	}
+
+#if DEBUG
+	fprintf(stderr, "Input buffer dump:\n");
+	hexprint(data, len);
+#endif
+	
+	cmdblk1[6] = prognum / 128;
+	cmdblk1[5] = prognum - (cmdblk1[6] * 128);
+
+	cmdblk1[7] = kgrpnum;
+	
+	/* assemble the whole message from header, payload and EOX */
+	memcpy(finalbuf, cmdblk1, sizeof(cmdblk1));
+	
+	for (i = 0; i < len; i++)
+	{
+		finalbuf[sizeof(cmdblk1) + i*2] = data[i] & 0x0f;
+		finalbuf[sizeof(cmdblk1) + i*2 + 1] = (data[i] & 0xf0) >> 4;
+	}
+	finalbuf[sizeof(cmdblk1)+2*len] = 0xf7; /* EOX */
+
+#if DEBUG
+	fprintf(stderr, "Encoded buffer dump:\n");
+	hexprint(finalbuf, sizeof(cmdblk1)+1+len*2);
+#endif
+
+	finallen = sendrecv_scsimidi(handle, finalbuf, sizeof(cmdblk1)+(len*2)+1, resbuf);
+	if (finallen <= 0)
+	{
+		fprintf(stderr, "lakai_put_keygroup(): Seems there were problems in receiving..\n");
+		return -1;
+	}	
+	return 0;
+}
+
+
+
+/*
+ * lakai_put_sample() - perform the actual bulk data transfer
+ * from the PC to the sampler.
+ * The supplied buffer should hold "len" bytes.
+ */
+
+long lakai_put_sample(LHANDLE handle, int samplenum, char *buffer, long locat, long len)
+{
+	int availbytes, stilltoput, putnow, ret;
+	unsigned char statusbuf[100];
+	unsigned char *ptr;
+	int len_samples = len / 2;
+	unsigned char cmdblk1[] = 
+	{
+		/* SysEx: "Accept Sample Packets"; bytes 0x05 and 0x06 contain sample # */
+		0xf0, 0x47, 0x00, LC_ASPACK, 0x48,
+		0x00,	0x00, /* sample # */
+		0x00, 0x00, 0x00, 0x00, /* start address */
+		0x00, 0x00, 0x00, 0x00, /* # of samples */
+		0xf7 /* EOX */
+	};
+	
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "lakai_put_sample: Bad file handle\n");
+		return -1;
+	}
+
+	/* put in sample number */
+	cmdblk1[5] = samplenum & 0x7f;
+	cmdblk1[6] = (samplenum >> 7) & 0x7f;
+
+	/* put in locat and len in 7-bit notation ( if required; should be 0 normally) */
+	cmdblk1[7] = (locat & 0x7f);
+	cmdblk1[8] = (locat >> 7) & 0x7f;
+	cmdblk1[9] = (locat >> 14) & 0x7f;
+	cmdblk1[10] = (locat >> 21) & 0x7f;
+	
+	cmdblk1[11] = len_samples & 0x7f;
+	cmdblk1[12] = (len_samples >> 7) & 0x7f;
+	cmdblk1[13] = (len_samples >> 14) & 0x7f;
+	cmdblk1[14] = (len_samples >> 21) & 0x7f;
+	
+	/* TODO: TEsting here.. */
+	lakai_setmode(handle, LAKAI_MODE_SCSI_BULK); /* switch to BULK transfer mode */
+
+	availbytes = sendrecv_scsimidi2(handle, cmdblk1, sizeof(cmdblk1), statusbuf);
+	if (availbytes > 0)
+	{
+#if DEBUG
+		fprintf(stderr, "lakai_put_sample() succeeded; %d bytes\n", availbytes);
+		hexprint(statusbuf, availbytes);
+#endif
+	}
+	else
+	{
+//		fprintf(stderr, "lakai_put_sample(): availbytes returned with 0..\n");
+	}
+
+//fprintf(stderr, "First part of lakai_put_sample() done..\n");
+/* TODO: This is unfortunately necessary, but why? */
+/* TODO: IDEA: We need to check status (receive the "ok") first, right? */
+//sleep(1);
+usleep(100000);
+
+	stilltoput = len;
+	ptr = buffer;
+	while (stilltoput > 0)
+	{
+		if (stilltoput < TRANSFER_LIMIT_SEND)
+			putnow = stilltoput;
+		else
+			putnow = TRANSFER_LIMIT_SEND;
+		ret = putbulkdata(handle, putnow, ptr);
+//fprintf(stderr, "This putbulkdata() call sent %d bytes\n", ret);
+		stilltoput -= putnow;
+		ptr += putnow;
+	}
+
+	lakai_setmode(handle, LAKAI_MODE_NORMAL); /* switch back to normal */
+
+	return 0; /* TODO */
+}
+
+
+
+/*
+ * void lakai_asciitoakai() - converts a string from ascii to Akai representation
+ * user is responsible for providing big enough src/dest buffers etc.
+ * Non-convertible characters in src get mapped to Space (' ') char in dest.
+ */
+void lakai_asciitoakai(char *src, char *dst, int len)
+{
+	int i;
+	unsigned char val_in, val_out;
+	for (i = 0; i < len; i++)
+	{
+		val_in = *(src+i);
+		if (val_in >= 48 && val_in <= 57)   /* '0'..'9' */
+			val_out = val_in - 48;
+		else if (val_in >= 65 && val_in <= 90)  /* 'A'..'Z' */
+			val_out = val_in - 54;
+		else if (val_in >= 97 && val_in <= 122) /* 'a'..'z' */
+			val_out = val_in - 86;
+		else if (val_in == 35)   /* '#' */
+			val_out = 37;
+		else if (val_in == 43)   /* '+' */
+			val_out = 38;
+		else if (val_in == 45)   /* '-' */
+			val_out = 39;
+		else if (val_in == 46)   /* '&' */
+			val_out = 40;
+		else
+			val_out = 38;			/* non-convertible ASCII char get changed to "+" for now.. */
+
+		*(dst+i) = val_out;
+	}
+}
+
+
+/*
+ * void lakai_akaitoascii() - converts a string from Akai to ASCII representation
+ * User is responsible for providing big enough src/dest buffers etc.
+ * Non-convertible characters (though there shouldn't be any :-) in src get mapped to 
+ * Space (' ') char in dest.
+ */
+void lakai_akaitoascii(char *src, char *dst, int len)
+{
+	int i;
+	unsigned char val_in, val_out;
+	for (i = 0; i < len; i++)
+	{
+		val_in = *(src+i);
+		if (val_in <= 9)   /* '0'..'9' */
+			val_out = val_in + 48;
+		else if (val_in >= 11 && val_in <= 36)  /* 'A'..'Z' */
+			val_out = val_in + 54;
+		else if (val_in == 37)   /* '#' */
+			val_out = 35;
+		else if (val_in == 38)   /* '+' */
+			val_out = 43;
+		else if (val_in == 39)   /* '-' */
+			val_out = 45;
+		else if (val_in == 40)   /* '&' */
+			val_out = 46;
+		else
+			val_out = 32;
+
+		*(dst+i) = val_out;
+	}
+}
+
+
+/*****************************************************************************
+ * The remaining functions in this file are internal to lakai.c and are not  *
+ * to be used outside. They are utility functions, mostly used for debugging *
+ * liblakai itself. Enter at your own risk. There may be dragons here.       *
+ *****************************************************************************/
+
+/*
+ * send_command() - internal SCSI command processing func
+ * INPUTS:
+ *		int sg_fd - file descriptor to talk to
+ * 	char *cmdblk - pointer to SCSI command block
+ *    int dirflag - direction flag (LAK_DIR_IN or LAK_DIR_OUT)
+ *		void *tosamp_ptr: buffer that holds data that is sent out (if needed - otherwise put NULL here)
+ *		int tosamp_len: number of bytes we want to send
+ *    void *fromsamp_ptr: buffer that will store returned data from sampler (if needed,
+ *     otherwise NULL)
+ *    int fromsamp_ptr: length of block expected from sampler (size of fromsamp_ptr buffer)
+ * 
+ * OUTPUTS:
+ *    TODO: ... to be specified...
+ *    
+ * If a command does not expect any data sent back to it (like in lakai_setmode()),
+ * it's ok to leave outbuf as NULL and outlen as 0.
+ * For other commands that expect data to be sent back, allocate a buffer of the
+ * required size and pass it over, together with the buffer size.
+ */
+
+int send_command(int sg_fd, char *cmdblk, int dirflag, 
+		void *tosamp_ptr, int tosamp_len,
+		void *fromsamp_ptr, int fromsamp_len)
+{
+	sg_io_hdr_t io_hdr;
+	unsigned char sense_buffer[32];
+
+	/* Prepare command */
+	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+	io_hdr.interface_id = 'S';
+	io_hdr.cmd_len = SCSI_CMD_LEN; 
+	/* io_hdr.iovec_count = 0; */  /* memset takes care of this */
+	io_hdr.mx_sb_len = sizeof(sense_buffer);
+
+	if (dirflag == LAK_DIR_IN)
+	{
+		/* incoming data */
+		io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+		io_hdr.dxfer_len = fromsamp_len;
+		io_hdr.dxferp = fromsamp_ptr;
+	}
+	else   /* LAK_DIR_OUT */
+	{
+		/* outgoing data */
+		io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
+		io_hdr.dxfer_len = tosamp_len;
+		io_hdr.dxferp = tosamp_ptr;
+		
+#if 0
+		io_hdr.dxfer_len = SCSI_CMD_LEN+len;	/* seems I cannot make this 0, though there is no data */
+		/* $$$ TODO: set dxferp correctly, depending on transfer type */
+/* TODO: FIX THIS		io_hdr.dxferp = cmdblk+cdbsz;	* to be transferred..hence, a dummy buffer */
+
+#endif
+	}
+
+	io_hdr.cmdp = cmdblk;
+	io_hdr.sbp = sense_buffer;
+	io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+	/* io_hdr.flags = 0; */     /* take defaults: indirect IO, etc */
+	/* io_hdr.pack_id = 0; */
+	/* io_hdr.usr_ptr = NULL; */
+
+	if (ioctl(sg_fd, SG_IO, &io_hdr) < 0)
+	{
+		fprintf(stderr, "send_command(): ioctl(SG_IO, ..) failed\n");
+		return -1;
+	}
+
+#if DEBUG
+	if (outbuf)
+	{
+		fprintf(stderr, "Dump of outbuf follows:\n");
+		hexprint((unsigned char *)outbuf, outlen);
+	}
+#endif
+	
+	if ((io_hdr.info & SG_INFO_OK_MASK) == SG_INFO_OK)
+	{
+//		fprintf(stderr, "### return code: OK\n");
+	}
+	else
+	{
+		fprintf(stderr, "### return code: NOT OK\n");
+		return -1;
+	}
+
+	if (io_hdr.sb_len_wr > 0)
+	{
+		fprintf(stderr, "Something (%d bytes) is in the sense buffer.\n", io_hdr.sb_len_wr);
+		fprintf(stderr, "Dump of sense buffer follows:\n");
+		hexprint(io_hdr.sbp, io_hdr.sb_len_wr);
+		return -1;
+	}
+
+	return 0;  /* success */
+}
+
+/*
+ * hexprint() - print out a data buffer as a hex listing
+ */
+
+void hexprint(unsigned char *buf, int len)
+{
+	unsigned char hexlist[] ="0123456789ABCDEF";
+	unsigned short i;
+	
+	for (i = 0; i < len; i++)
+	{
+		if ((i % 16) == 0)
+		{
+			/* print 4-digit address */
+			putchar(hexlist[(i >> 12) & 0x0f]);
+			putchar(hexlist[(i >> 8) & 0x0f]);
+			putchar(hexlist[(i >> 4) & 0x0f]);
+			putchar(hexlist[i & 0x0f]);
+			putchar(':');
+		}
+		if ((i % 8) == 0)
+			putchar(' ');
+		
+		putchar(hexlist[*(buf+i) >> 4]);
+		putchar(hexlist[*(buf+i) & 0x0f]);
+		putchar(' ');
+		
+		if ((i+1) % 16 == 0)
+			putchar('\n');
+	}
+	putchar('\n');
+}
+
+/*
+ * hexprint_midi() - print out a data buffer of MIDI bytes as a hex listing
+ */
+void hexprint_midi(unsigned char *buf, int len)
+{
+	unsigned char hexlist[] ="0123456789ABCDEF";
+	unsigned short i;
+	unsigned char val;
+	
+	for (i = 0; i < len; i++)
+	{
+		if ((i % 16) == 0)
+		{
+			/* print 4-digit address */
+			putchar(hexlist[(i >> 12) & 0x0f]);
+			putchar(hexlist[(i >> 8) & 0x0f]);
+			putchar(hexlist[(i >> 4) & 0x0f]);
+			putchar(hexlist[i & 0x0f]);
+			putchar(':');
+		}
+		if ((i % 8) == 0)
+			putchar(' ');
+		
+		/* MIDI data bytes are spread over 2 bytes: first is lower nibble,
+		 * second is higher nibble.
+		 */
+		val = *(buf + i*2) + (*(buf + i*2 + 1) << 4);
+		putchar(hexlist[val >> 4]);
+		putchar(hexlist[val & 0x0f]);
+		putchar(' ');
+		
+		if ((i+1) % 16 == 0)
+			putchar('\n');
+	}
+	putchar('\n');
+}
+
+/*
+ * sendrecv_scsimidi() - internal func that gets a SysEx command block, sends it out
+ * to the device and collects the returned data.
+ * TODO: destbuf must be given always (has to be checked here...?)
+ *
+ * If call fails, returns -1.
+ * Otherwise, returns # of bytes transferred.
+ */
+int sendrecv_scsimidi(LHANDLE handle, char *sysexblk, unsigned int sysexblksize, char *destbuf)
+{
+	unsigned char outbuf[32];
+	int templen, currentlen;
+
+	static unsigned char cmdblk1[SCSI_CMD_LEN] = {
+		S2000_SEND, /* command */
+		0,				/* lun/AEN */
+		0, 			/* len msb */
+		0,				/* len */
+		0xff,			/* len lsb */
+		0};			/* control */
+
+	static unsigned char cmdblk2[SCSI_CMD_LEN] = {
+		S2000_STATUS,	/* command */
+		0,					/* lun/AEN */
+		0, 				/* len msb */
+		0,					/* len */
+		0,					/* len lsb */
+		0};				/* control */
+
+	static unsigned char cmdblk3[SCSI_CMD_LEN] = {
+		S2000_RECEIVE,	/* command */
+		0,					/* lun/AEN */
+		0, 				/* len msb */
+		0,					/* len */
+		0,					/* len lsb - these 3 get filled in before the send_command call */
+		0};				/* control */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "sendrecv_scsimidi: Bad file handle\n");
+		return -1;
+	}
+
+
+	if (sysexblksize > 65534)
+	{
+		fprintf(stderr, "sendrecv_scsimidi: Error: SysEx msg too long\n!");
+		return -1;
+	}
+
+	cmdblk1[4] = (unsigned char)sysexblksize & 0xff;
+	cmdblk1[3] = (unsigned char)((sysexblksize & 0xff00) >> 8);
+	cmdblk1[2] = (unsigned char)((sysexblksize & 0xff0000) >> 16);
+	
+	lakai_setmode(handle, LAKAI_MODE_SCSI_MIDI); /* switch to MIDI-over-SCSI mode */
+
+	/* This is 3 tasks in one function:
+	 * 1) S2000_SEND: Send off the MIDI SysEx command block
+	 * 2) S2000_STATUS: Get SCSI status to determine # of bytes waiting for us at the sampler
+	 * 3) S2000_RECEIVE: Send out RECEIVE command to get the actual data from sampler
+	 */
+	if (send_command(fhandles[handle], cmdblk1, LAK_DIR_OUT, sysexblk, sysexblksize, NULL, 0) < 0)
+	{
+		fprintf(stderr, "sendrecv_scsimidi: Problems in part 1\n");
+		lakai_setmode(handle, LAKAI_MODE_NORMAL); /* switch back to normal mode */
+		return -1;
+	}
+
+// fprintf(stderr, "Part 1 OK.\n");
+	currentlen = 0;
+
+	do
+	{
+		/* 2nd part: Send STATUS to get # of bytes waiting */
+		if (send_command(fhandles[handle], cmdblk2, LAK_DIR_IN, NULL, 0, outbuf, 3) < 0)
+		{
+			fprintf(stderr, "sendrecv_scsimidi: Problems in part 2\n");
+			lakai_setmode(handle, LAKAI_MODE_NORMAL); /* switch back to normal mode */
+			return -1;
+		}
+
+		/* 3rd part: Send RECEIVE to get the sampler's status data */
+
+		/* length of result is encoded in 3 bytes, MSB first */
+		templen = (outbuf[0] << 16) + (outbuf[1] << 8) + outbuf[2];
+//		fprintf(stderr, "Determined templen as %d bytes.\n", templen);
+	
+		if (templen > 0)
+		{
+			cmdblk3[2] = outbuf[0];
+			cmdblk3[3] = outbuf[1];
+			cmdblk3[4] = outbuf[2];
+	
+			/* Receive the first/next block of data */
+			if (send_command(fhandles[handle], cmdblk3, LAK_DIR_IN, 
+						NULL, 0, destbuf+currentlen, templen) < 0)
+			{
+				fprintf(stderr, "sendrecv_scsimidi: Problems in part 3\n");
+				lakai_setmode(handle, LAKAI_MODE_NORMAL); /* switch back to normal mode */
+				return -1;
+			}
+
+			currentlen += templen;
+		}
+	} while (templen > 0);
+
+//	fprintf(stderr, "sendrecv_scsimidi: Transfer done; received %d bytes.\n", currentlen);
+	
+	lakai_setmode(handle, LAKAI_MODE_NORMAL); /* switch back to normal mode */
+	return currentlen;
+}
+
+/*
+ * Slightly simplified form of the previous function - this one only sends a SysEx
+ * command block, determines the # of bytes waiting, and that's it. Someone else
+ * will have to receive the data from the sampler.
+ */
+int sendrecv_scsimidi2(LHANDLE handle, char *sysexblk, unsigned int sysexblksize, char *buf)
+{
+	unsigned char outbuf[32];
+	int templen;
+
+	static unsigned char cmdblk1[SCSI_CMD_LEN] = {
+		S2000_SEND, /* command */
+		0,				/* lun/AEN */
+		0, 			/* len msb */
+		0,				/* len */
+		0xff,			/* len lsb - gets filled in below */
+		0};			/* control */
+
+	static unsigned char cmdblk2[SCSI_CMD_LEN] = {
+		S2000_STATUS,	/* command */
+		0,					/* lun/AEN */
+		0, 				/* len msb */
+		0,					/* len */
+		0,					/* len lsb */
+		0x80};			/* control */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "sendrecv_scsimidi2: Bad file handle\n");
+		return -1;
+	}
+
+	if (sysexblksize > 65534)
+	{
+		fprintf(stderr, "sendrecv_scsimidi2: Error: SysEx msg too long\n!");
+		return -1;
+	}
+
+	/* assemble the data packet */
+	cmdblk1[4] = (unsigned char)sysexblksize & 0xff;
+	cmdblk1[3] = (unsigned char)((sysexblksize & 0xff00) >> 8);
+	cmdblk1[2] = (unsigned char)((sysexblksize & 0xff0000) >> 16);
+
+	/* This is 2 tasks in one function:
+	 * 1) S2000_SEND: Send off the MIDI SysEx command block
+	 * 2) S2000_STATUS: Get SCSI status to determine # of bytes waiting for us at the sampler
+	 */
+	if (send_command(fhandles[handle], cmdblk1, LAK_DIR_OUT, sysexblk, sysexblksize, NULL, 0) < 0)
+	{
+		fprintf(stderr, "sendrecv_scsimidi2: Problems in part 1\n");
+		lakai_setmode(handle, LAKAI_MODE_NORMAL); /* switch back to normal mode */
+		return -1;
+	}
+	
+	/* 2nd part: Send STATUS to get # of bytes waiting */
+	if (send_command(fhandles[handle], cmdblk2, LAK_DIR_IN, NULL, 0, outbuf, 3) < 0)
+	{
+		fprintf(stderr, "sendrecv_scsimidi2: Problems in part 2\n");
+		lakai_setmode(handle, LAKAI_MODE_NORMAL); /* switch back to normal mode */
+		return -1;
+	}
+
+	/* length of result is encoded in 3 bytes, MSB first */
+	templen = (outbuf[0] << 16) + (outbuf[1] << 8) + outbuf[2];
+//	fprintf(stderr, "srsm2: Determined templen as %d bytes.\n", templen);
+	
+//	lakai_setmode(handle, LAKAI_MODE_NORMAL); /* switch back to normal mode */
+	return templen;
+}
+
+
+#if 0
+/*
+ * get_availbytes() - retrieves the number of bytes currently waiting to be received
+ * by the host (in SCSI_BULK mode)
+ *
+ * The sampler must already be put into LAKAI_MODE_SCSI_BULK when this gets called.
+ */
+
+/* TODO: This is unfinished, and probably not even necessary at all.. */
+static long get_availbytes(LHANDLE handle)
+{
+	unsigned char outbuf[32];
+	
+	static unsigned char cmdblk1[SCSI_CMD_LEN] = {
+		S2000_STATUS,	/* command */
+		0,					/* lun/AEN */
+		0, 				/* len msb */
+		0,					/* len */
+		0,					/* len lsb */
+		0x80};			/* control - Why 0x80? Laszlo says so. */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "get_availbytes: Bad file handle\n");
+		return -1;
+	}
+
+	/* Send "raw" STATUS to get # of bytes waiting */
+	if (send_command(fhandles[handle], cmdblk1, LAK_DIR_IN, 0, outbuf, 3) < 0)
+	{
+		fprintf(stderr, "get_availbytes: Problems in part 2\n");
+		lakai_setmode(handle, LAKAI_MODE_NORMAL); /* switch back to normal mode */
+		return -1;
+	}
+
+	/* dump the answer */
+	hexprint(outbuf, 32);	
+
+	return 0;
+}
+#endif
+
+
+/*
+ * getbulkdata() - this is the "bulk receiver". Before it can be called, data
+ * should already have been requested from the sampler through e.g. the
+ * sendrecv_scsimidi2() function. Also, the sampler should be in mode
+ * LAKAI_SCSI_BULK when this function is called.
+ * 
+ * Parameters:
+ * 
+ * LHANDLE handle: the usual handle
+ * int numbytes: number of bytes coming in
+ * char *buf: where to store the incoming data
+ *
+ * Results:
+ * long numreceived: number of bytes actually received in this call
+ */
+
+long getbulkdata(LHANDLE handle, int numbytes, char *buf)
+{
+	static unsigned char cmdblk[SCSI_CMD_LEN] = {
+		S2000_RECEIVE,	/* command */
+		0,					/* lun/AEN */
+		0,					/* len msb */
+		0,					/* len */
+		0,					/* len lsb - gets filled in below */
+		0};				/* control */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "getbulkdata: Bad file handle\n");
+		return -1;
+	}
+
+	if (numbytes > 65536)
+	{
+		fprintf(stderr, "getbulkdata: Error: numbytes too large\n!");
+		return -1;
+	}
+
+	/* assemble the data packet */
+	cmdblk[3] = numbytes >> 8;
+	cmdblk[4] = numbytes & 0xff;
+	cmdblk[5] = 0x80;
+	
+	if (send_command(fhandles[handle], cmdblk, LAK_DIR_IN, NULL, 0, buf, numbytes) < 0)
+	{
+		fprintf(stderr, "getbulkdata: Problems in part 1\n");
+		lakai_setmode(handle, LAKAI_MODE_NORMAL); /* switch back to normal mode */
+		return -1;
+	}
+	
+	return numbytes;  /* TODO: Fixme: This is a fake yet...need to get this from the sg layer */
+}
+
+
+/*
+ * putbulkdata() - this is the "bulk sender". Before it can be called, data
+ * should already have been announced to the sampler through e.g. the
+ * sendrecv_scsimidi2() function. Also, the sampler should be in mode
+ * LAKAI_SCSI_BULK when this function is called.
+ * 
+ * Parameters:
+ * 
+ * LHANDLE handle: the usual handle
+ * int numbytes: number of bytes coming in
+ * char *buf: where to store the incoming data
+ *
+ * Results:
+ * long numreceived: number of bytes actually received in this call
+ */
+
+long putbulkdata(LHANDLE handle, int numbytes, char *buf)
+{
+	static unsigned char cmdblk[SCSI_CMD_LEN] = {
+		S2000_SEND,		/* command */
+		0,					/* lun/AEN */
+		0,					/* len msb */
+		0,					/* len */
+		0,					/* len lsb - gets filled in below */
+		0};				/* control */
+
+	if (fhandles[handle] == -1 || handle < 0 || handle > MAX_OPEN)
+	{
+		fprintf(stderr, "putbulkdata: Bad file handle\n");
+		return -1;
+	}
+
+	if (numbytes > 65536)
+	{
+		fprintf(stderr, "putbulkdata: Error: numbytes too large\n!");
+		return -1;
+	}
+
+	/* assemble the data packet */
+	cmdblk[3] = numbytes >> 8;
+	cmdblk[4] = numbytes & 0xff;
+	cmdblk[5] = 0x80; /* $$$ ??*/
+	
+	if (send_command(fhandles[handle], cmdblk, LAK_DIR_OUT, buf, numbytes, NULL, 0) < 0)
+	{
+		fprintf(stderr, "putbulkdata: Problems in part 1\n");
+		lakai_setmode(handle, LAKAI_MODE_NORMAL); /* switch back to normal mode */
+		return -1;
+	}
+//	else
+//		fprintf(stderr, "getbulkdata: send_command() returned ok\n");
+	
+	return numbytes;  /* TODO: Fixme: This is a fake yet...need to get this from the sg layer */
+}
+

Added: lakai/branches/upstream/current/lakai.h
===================================================================
--- lakai/branches/upstream/current/lakai.h	2004-10-28 08:35:57 UTC (rev 14)
+++ lakai/branches/upstream/current/lakai.h	2004-10-28 09:32:22 UTC (rev 15)
@@ -0,0 +1,1305 @@
+
+/*
+ * lakai.h - liblakai header file
+ *
+ * Copyright (c) 2002-2004 Frank Neumann <franky@users.sourceforge.net>
+ */
+
+/* AKAI's extension of the SCSI opcodes (see /usr/include/scsi/scsi.h). */
+/* These IDs are unallocated in that file..reserved for customer use?   */
+#define S2000_MODE    0x09
+#define S2000_SEND    0x0c
+#define S2000_STATUS  0x0d
+#define S2000_RECEIVE 0x0e
+
+/* S1000 SysEx "command" bytes, used both for "send" and "receive" direction */
+/* This is typically the fourth byte of a SysEx message */
+#define LC_RSTAT   0x00  /* request status report */
+#define LC_STAT    0x01  /* status report */
+#define LC_RPLIST  0x02  /* request list of resident program names */
+#define LC_PLIST   0x03  /* list of resident program names */
+#define LC_RSLIST  0x04  /* request list of resident sample names */
+#define LC_SLIST   0x05  /* list of resident sample names */
+#define LC_RPDATA  0x06  /* request program common data */
+#define LC_PDATA   0x07  /* program common data */
+#define LC_RKDATA  0x08  /* request keygroup data */
+#define LC_KDATA   0x09  /* keygroup data */
+#define LC_RSDATA  0x0a  /* request sample header data */
+#define LC_SDATA   0x0b  /* sample header data */
+#define LC_RSPACK  0x0c  /* request sample data packet(s) */
+#define LC_ASPACK  0x0d  /* accept sample data packet(s) */
+#define LC_RDDATA  0x0e  /* request drum input settings */
+#define LC_DDATA   0x0f  /* drum input settings */
+#define LC_RMDATA  0x10  /* request miscellaneous data */
+#define LC_MDATA   0x11  /* miscellaneous data */
+#define LC_DELP    0x12  /* delete program and its keygroups */
+#define LC_DELK    0x13  /* delete keygroup */
+#define LC_DELS    0x14  /* delete sample header and data */
+#define LC_SETEX   0x15  /* set Sx000 exclusive channel */
+#define LC_REPLY   0x16  /* Sx000 command reply (error or ok) */
+#define LC_CASPACK 0x1d  /* corrected ASPACK */
+
+/* new operation codes for S3000 (/S2000?) */
+#define LC_S3RPDATA  0x27 /* request for program header bytes */
+#define LC_S3PDATA   0x28 /* program header bytes */
+#define LC_S3RKDATA  0x29 /* request keygroup header bytes */
+#define LC_S3KDATA   0x2a /* keygroup header bytes */
+#define LC_S3RSDATA  0x2b /* request sample header bytes */
+#define LC_S3SDATA   0x2c /* sample header bytes */
+#define LC_S3RFDATA  0x2d /* request fx/reverb bytes */
+#define LC_S3FDATA   0x2e /* fx/reverb bytes */
+#define LC_S3RCLDATA 0x2f /* request Cue-list data */
+#define LC_S3CLDATA  0x30 /* Cue-list data */
+#define LC_S3RTLDATA 0x31 /* request for take list bytes */
+#define LC_S3TLDATA  0x32 /* take list bytes */
+#define LC_S3RMDATA  0x33 /* request miscellaneous bytes */
+#define LC_S3MDATA   0x34 /* miscellaneous bytes */
+#define LC_S3RVLI    0x35 /* request volume list item */
+#define LC_S3VLI     0x36 /* volume list item (only used in response to requ) */
+#define LC_S3RHDENT  0x37 /* request harddisk directory entry */
+#define LC_S3HDENT   0x38 /* harddisk directory entry (only in response to req) */
+
+
+/*
+ * The access to all elements inside the sampler should be handled through
+ * this library.
+ *
+ * In doing so, two different internal communication methods are used
+ * (which are invisible to the user of the API, though):
+ *
+ * - When requesting basic, low-volume data (like program lists, sample
+ *   header data etc), the protocol used is "MIDI-over-SCSI".
+ *
+ * - When requesting large blocks of data (typically the actual sample
+ *   data), the protocol used is "raw SCSI".
+ *
+ * An application program will first have to use the "low-volume"
+ * inquiry functions to get at all the basic information it needs
+ * (what samples are in the sampler right now, where are they located,
+ * how large are they), and after that can use the "high-volume"
+ * functions to transfer samples to/from the sampler.
+ *
+ * In all of these transfers, the application has to allocate the
+ * required memory structures; as the library does not know most of
+ * the information until it's parsed, there would probably be too much
+ * code replication because the application will have to parse the data
+ * anyway (and right now I can't think of good ways to put this into
+ * utility functions in the library).
+ *
+ */
+
+
+/* An LHANDLE is your key to accessing a LAKAI device */
+typedef int LHANDLE;
+
+/* TODO: Decide if the data that gets passed back to the user program is the
+ * original raw data or if it has been pre-processed for easier use.
+ * I think the latter should not be too complicated. It's mostly stripping off
+ * the SysEx header/EOX and filling in the data fields of the respective
+ * structures.
+ */
+
+/* a lakai_status structure contains such information as OS version,
+ * number of programs&samples in memory, free memory etc
+ */
+typedef struct
+{
+	int osversion;			/* OS version of currently booted Akai OS */
+	int nummaxblocks;		/* maximum number of sample/program/keygroup blocks */
+	int numfreeblocks;	/* number of currently free blocks */
+	int nummaxsamples;	/* maximum number of sample words */
+	int numfreesamples;	/* number of currently available sample words */
+	int eoxchannel;		/* current MIDI channel for exclusive data */
+} LakaiStatus;
+
+/* A LakaiProgramList holds a list of currently resident program names in
+ * the sampler.
+ */
+typedef struct
+{
+	int numprogs;
+	char **prognames;
+} LakaiProgramList;
+
+/* A LakaiSampleList holds a list of currently resident sample names in
+ * the sampler.
+ */
+typedef struct
+{
+	int numsamples;
+	char **samplenames;
+} LakaiSampleList;
+
+
+/* A LakaiProgram structure holds all information that make up one
+ * program - tuning, filter settings, keygroups etc.
+ */
+typedef struct
+{
+	/* TODO: Incomplete.. */
+	unsigned char prident;		/* 1=Program header block identifier */
+	/* a pad byte is inserted here by the compiler */
+	unsigned short kgrp1;		/* 1st keygroup block address (internal use) */
+	unsigned char prname[12];	/* Name */
+	unsigned char prgnum;		/* MIDI program number (0-127) */
+	unsigned char pmchan;		/* MIDI channel (0-15, FFh=OMNI) */
+	unsigned char polyph;		/* Polyphony (1-16) */
+	unsigned char priort;		/* Priority (0=low 1=normal 2=high 3=hold) */
+	unsigned char playlo;		/* Play-range low (24-127 = C0-G8) */
+	unsigned char playhi;		/* Play-range high (24-127 = C0-G8) */
+	unsigned char oshift;		/* Play octave (keyboard) shift(+/-2) */
+	unsigned char output;		/* Output number (0-7,FFh=off) */
+	unsigned char stereo;		/* Left and right level (0-99) */
+	unsigned char panpos;		/* Left/right balance (+/-50) */
+	unsigned char prloud;		/* Basic loudness (0-99) */
+	unsigned char v_loud;		/* Velocity&Loudness (+/-50) */
+	unsigned char k_loud;		/* Key&Loudness (+/-50) */
+	unsigned char p_loud;		/* Pressure&Loudness (+/-50) */
+	unsigned char panrat;		/* Pan LFO rate (0-99) */
+	unsigned char pandep;		/* Pan depth (0-99) */
+	unsigned char pandel;		/* Pan LFO delay (0-99) */
+	unsigned char k_panp;		/* Key&Pan position (+/-50) */
+	unsigned char lforat;		/* LFO speed (0-99) */
+	unsigned char lfodep;		/* LFO fixed depth (0-99) */
+	unsigned char lfodel;		/* LFO delay (0-99) */
+	unsigned char mwldep;		/* Modwheel&LFO depth (0-99) */
+	unsigned char prsdep;		/* Pressure&LFO depth (0-99) */
+	unsigned char veldep;		/* Velocity&gt;LFO depth (0-99) */
+	unsigned char b_ptch;		/* Bendwheel&Pitch (0-12 semitones) */
+	unsigned char p_ptch;		/* Pressure&Pitch (+/-12 semitones) */
+	unsigned char kxfade;		/* Keygroup crossfade (0=off 1=on) */
+	unsigned char groups;		/* number of keygroups (1-99) */
+	unsigned char tpnum;			/* temporary program number (internal use) */
+	unsigned char temper[12];	/* Key temperament (+/25 cents) C,C#,D,D# etc */
+	unsigned char echout;		/* Echo output level (0=off 1=on) */
+	unsigned char mw_pan;		/* Modwheel pan amount (+/-50) */
+	unsigned char cohere;		/* Sample start coherence (0=off 1=on) */
+	unsigned char desync;		/* LFO De-Sync (0=off 1=on) */
+	unsigned char plaw;			/* Pitch Law (0=linear) */
+	unsigned char vassoq;		/* Voice assign algorithm (0=oldest 1=quietest) */
+	unsigned char sploud;		/* Soft pedal loudness reduction (0-99) */
+	unsigned char spatt;			/* Soft pedal attack stretch (0-99) */
+	unsigned char spfilt;		/* Soft pedal filter close (0-99) */
+	unsigned short ptuno;		/* Tune offset cent:semi (+/-50.00 fraction is binary) */
+	unsigned char k_lrat;		/* Key&LFO rate (+/-50) */
+	unsigned char k_ldep;		/* Key&LFO depth (+/-50) */
+	unsigned char k_ldel;		/* Key&LFO delay (+/-50) */
+	unsigned char voscl;			/* Voice output scale (0=-6dB, 1=0dB, 2=+12dB) */
+	unsigned char vsscl;			/* Stereo output scale (0=0dB, 1=+6dB) */
+	unsigned char legato;		/* Mono legato mode enable (0=Off, 1=On) */
+	unsigned char b_ptchd;		/* Range of decrease of Pitch by bendwheel (0..12 semitones) */
+	unsigned char b_mode;		/* Bending of held notes (0=normal mode, 1=held) */
+	unsigned char transpose;	/* Shift pitch of incoming MIDI (-50..+50 semitones) */
+	/* Values used to represent Modulation Sources for the following mod's:
+	 *
+	 * 0: No Source
+	 * 1: Modwheel
+	 * 2: Bend
+	 * 3: Pressure
+	 * 4: External
+	 * 5: Note-on velocity
+	 * 6: Key
+	 * 7: LFO1
+	 * 8: LFO2
+	 * 9: Env1
+	 * 10: Env2
+	 * 11: !Modwheel (Instantaneous value of modwheel at note-on)
+	 * 12: !Bend (Instantaneous value of bendwheel at note-on)
+	 * 13: !External (Instantaneous value of MIDI controller at note-on)
+	 * 14: Env3
+	 */ 
+	unsigned char modspan1;		/* First source of assignable modulation of pan position */
+	unsigned char modspan2;		/* Second source of assignable modulation of pan */
+	unsigned char modspan3;		/* Third source of assignable modulation of pan */
+	unsigned char modsamp1;		/* First source of assignable modulation of loudness */
+	unsigned char modsamp2;		/* Second source of assignable modulation of loudness */
+	unsigned char modslfot;		/* Source of assignable modulation of LFO1 speed */
+	unsigned char modslfol;		/* Source of assignable modulation of LFO1 depth */
+	unsigned char modslfod;		/* Source of assignable modulation of LFO1 delay */
+	unsigned char modsfilt1;	/* First source of assignable modulation of filter frequency */
+	unsigned char modsfilt2;	/* Second source of assignable modulation of filter frequency */
+	unsigned char modsfilt3;	/* Third source of assignable modulation of filter frequency */
+	unsigned char modspitch;	/* Source of assignable modulation of pitch */
+	unsigned char modsamp3;		/* Third source of assignable modulation of loudness */
+	unsigned char modvpan1;		/* Amount of control of pan by assignable source 1 (-50..50) */
+	unsigned char modvpan2;		/* Amount of control of pan by assignable source 2 (-50..50) */
+	unsigned char modvpan3;		/* Amount of control of pan by assignable source 3 (-50..50) */
+	unsigned char modvamp1;		/* Amount of control of loudness by assignable source 1 (-50..50) */
+	unsigned char modvamp2;		/* Amount of control of loudness by assignable source 2 (-50..50) */
+	unsigned char modvlfor;		/* Amount of control of LFO1 speed (-50..50) */
+	unsigned char modvlfol;		/* Amount of control of LFO1 depth (-50..50) */
+	unsigned char modvlfod;		/* Amount of control of LFO1 delay (-50..50) */
+	unsigned char lfo1wave;		/* LFO1 waveform (0=Triangle, 1=Sawtooth, 2=Square) */
+	unsigned char lfo2wave;		/* LFO2 waveform (0=Triangle, 1=Sawtooth, 2=Square) */
+	unsigned char modslflt2_1;	/* First source of assignable modulation of filter 2 frequency (only used on S3200) */
+	unsigned char modslflt2_2;	/* Second source of assignable modulation of filter 2 frequency (only used on S3200) */
+	unsigned char modslflt2_3;	/* Third source of assignable modulation of filter 2 frequency (only used on S3200) */
+	unsigned char lfo2trig;		/* Retrigger mode for LFO2 */
+	unsigned char _reserved1[7];	/* .. */
+	unsigned char portime;		/* Portamento Time */
+	unsigned char portype;		/* Portamento Type */
+	unsigned char porten;		/* Portamento On/Off */
+	unsigned char pfxchan;		/* Effects Bus Select (0=Off, 1=FX1, 2=FX2, 3=RV3, 4=RV4) */
+	unsigned char pfxslev;		/* Not used */
+} LakaiProgram;
+
+
+/* A LakaiKeygroup structure holds the information for one keygroup:
+ * lower/upper end of range etc.
+ */
+typedef struct
+{
+	unsigned char kgident;		/* 2=Keygroup block identifier */
+	unsigned short nxtkg;		/* Next keygroup block address (internal use) */
+	unsigned char lonote;		/* Keyrange low (24-127 = C0-G8) */
+	unsigned char hinote;		/* Keyrange high (24-127 = C0-G8) */
+	unsigned short kgtuno;		/* Tune offset cent:semi (+/-50.00 fraction is binary */
+	unsigned char filfrq;		/* Basic filter frequency (0-99) */
+	unsigned char k_freq;		/* Key&Filter freq (+/-24 semitones/octave) */
+	unsigned char v_freq;		/*	Velocity&Filter freq (+/-50) */
+	unsigned char p_freq;		/* Pressure&Filter freq (+/-50) */
+	unsigned char e_freq;		/* Envelope&Filter freq (+/-50) */
+	unsigned char attak1;		/* Amplitude attack (0-99) */
+	unsigned char decay1;		/* Amplitude decay (0-99) */
+	unsigned char sustn1;		/* Amplitude sustain level (0-99) */
+	unsigned char relse1;		/* Amplitude release (0-99) */
+	unsigned char v_att1;		/* Velocity&Amp attack (+/-50) */
+	unsigned char v_rel1;		/* Velocity&Amp release (+/-50) */
+	unsigned char o_rel1;		/* Off Vel.&Amp release (+/-50) */
+	unsigned char k_dar1;		/* Key&Decay&Release (+/-50) */
+	unsigned char attak2;		/* Filter attack (0-99) */
+	unsigned char decay2;		/* Filter decay (0-99) */
+	unsigned char sustn2; 		/* Filter sustain level (0-99) */
+	unsigned char relse2;		/* Filter release (0-99) */
+	unsigned char v_att2;		/* Velocity&Filter attack (+/-50) */
+	unsigned char v_rel2;		/* Velocity&Filter release (+/-50) */
+	unsigned char o_rel2;		/* Off Vel.&Filter relase (+/-50 */
+	unsigned char k_dar2;		/* Key&Decay&Release (+/-50) */
+	unsigned char v_env2;		/* Velocity&Filter envelope output (+/-50) */
+	unsigned char e_ptch;		/* Envelope&Pitch (+/-50) */
+	unsigned char vxfade;		/* Velocity zone crossfade (0=off 1=on) */
+	unsigned char vzones;		/* Number of velocity zones in use (not used) */
+	unsigned char lkxf;			/* Calculated left key crossfade factor (internal) */
+	unsigned char rkxf;			/* Calculated right key crossfade factor (internal) */
+	
+	/* Velocity zone 1 */
+	unsigned char sname1[12];	/* Sample name */
+	unsigned char lovel1;		/* Velocity range low (0-127) */
+	unsigned char hivel1;		/* Velocity range high (0-127) */
+	unsigned short vtuno1;		/* Tune offset (+/-50.00 fraction is in binary form) */
+	unsigned char vloud1;		/* Loudness offset (+/-50) */
+	unsigned char vfreq1;		/* Filter frequency offset (+/-50) */
+	unsigned char vpano1;		/* Pan offset (+/-50) */
+	unsigned char zplay1;		/* Loop in release (0=as sample, 1-4 see below) */
+	unsigned char lvxf1;			/* Low velocity crossfade factor (internal use) */
+	unsigned char hvxf1;			/* High velocity crossfade factor (internal use) */
+	unsigned short sbadd1;		/* Calculated sample header block address (internal) */
+
+	/* Velocity zone 2 */
+	unsigned char sname2[12];	/* See velocity zone 1 */
+	unsigned char lovel2;
+	unsigned char hivel2;
+	unsigned short vtuno2;
+	unsigned char vloud2;
+	unsigned char vfreq2;
+	unsigned char vpano2;
+	unsigned char zplay2;
+	unsigned char lvxf2;
+	unsigned char hvxf2;
+	unsigned short sbadd2;
+
+	/* Velocity zone 3 */
+	unsigned char sname3[12];	/* See velocity zone 1 */
+	unsigned char lovel3;
+	unsigned char hivel3;
+	unsigned short vtuno3;
+	unsigned char vloud3;
+	unsigned char vfreq3;
+	unsigned char vpano3;
+	unsigned char zplay3;
+	unsigned char lvxf3;
+	unsigned char hvxf3;
+	unsigned short sbadd3;
+	
+	/* Velocity zone 4 */
+	unsigned char sname4[12];	/* See velocity zone 1 */
+	unsigned char lovel4;
+	unsigned char hivel4;
+	unsigned short vtuno4;
+	unsigned char vloud4;
+	unsigned char vfreq4;
+	unsigned char vpano4;
+	unsigned char zplay4;
+	unsigned char lvxf4;
+	unsigned char hvxf4;
+	unsigned short sbadd4;
+	
+	unsigned char kbeat;			/* Fixed rate detune (byte) */
+	unsigned char ahold;			/* Attack hold until loop */
+	unsigned char cp1;			/* Constant pitch for each velocity zone (0=track 1=const) */
+	unsigned char cp2;
+	unsigned char cp3;
+	unsigned char cp4;
+	unsigned char vzout1;		/* Output number offset for each velocity zone (0-7) */
+	unsigned char vzout2;
+	unsigned char vzout3;
+	unsigned char vzout4;
+	unsigned short vss1;			/* Velocity&Sample start (+/-9999) */
+	unsigned short vss2;
+	unsigned short vss3;
+	unsigned short vss4;
+	unsigned char kv_lo;			/* Velocity&Loudness offset (+/-50) */
+	/* ZPLAY:- type of sample playback, values:
+	 * 0 = as defined by sample header
+	 * 1 = normal looping
+	 * 2 = loop until release
+	 * 3 = no looping
+	 * 4 = play to sample end
+	 */
+
+	/* TODO: Need to proof-read this! */
+	unsigned char filq;
+	unsigned char l_ptch;
+	unsigned char modvfilt1;
+	unsigned char modvfilt2;
+	unsigned char modvfilt3;
+	unsigned char modvpitch;
+	unsigned char modvamp3;
+	unsigned char env2l1;
+	unsigned char env2r2;
+	unsigned char env2l2;
+	unsigned char env2l4;
+	unsigned char kgmute;
+	unsigned char pfxchan;
+	unsigned char pfxslev;
+	unsigned char res1[5];
+	unsigned char lsi2_on;
+	unsigned char flt2gain;
+	unsigned char flt2mode;
+	unsigned char flt2q;
+	unsigned char tonefreq;
+	unsigned char toneslop;
+	unsigned char modvflt2_1;
+	unsigned char modvflt2_2;
+	unsigned char modvflt2_3;
+	unsigned char fil2fr;
+	unsigned char k_frq2;
+	unsigned char env3r1;
+	unsigned char env3l1;
+	unsigned char env3r2;
+	unsigned char env3l2;
+	unsigned char env3r3;
+	unsigned char env3l3;
+	unsigned char env3r4;
+	unsigned char env3l4;
+	unsigned char v_att3;
+	unsigned char v_rel3;
+	unsigned char o_rel3;
+	unsigned char k_dar3;
+	unsigned char v_env3;
+
+} LakaiKeygroup;
+
+
+/* A LakaiSampleHeader structure contains information about the associated
+ * sample, such as length, loop points
+ */
+typedef struct
+{
+	unsigned char shident;			/* 3=sample header block identifier */
+	unsigned char sbandw;			/* Bandwidth (0=10kHz 1=20kHz) */
+	unsigned char spitch;			/* Original pitch (24-127 = C0-G8) */
+	unsigned char shname[12];		/* Name (same position as program) */
+	unsigned char ssrvld;			/* Sample rate ssrate valid (80H=yes) */
+	unsigned char sloops;			/* Number of loops (internal use) */
+	unsigned char saloop;			/* First active loop (internal use) */
+	unsigned char padbyte;			/* Spare byte */
+	unsigned char sptype;			/* Playback type (see below) */
+	unsigned short stuno;			/* Tune offset cent:semi (+/-50.00) */
+	unsigned long slocat;			/* Data absolute start address */
+	unsigned long slngth;			/* Data length (number of samples) */
+	unsigned long sstart;		/* Play relative start address */
+	unsigned long smpend;		/* Play relative end address */
+	unsigned long loopat1;		/* Relative loop point (bits 0-5 are treated as 1) */
+	unsigned long llngth1;		/* Loop length (binary) fraction:INT.LOW:INT.HIGH */
+	unsigned short ldwell1;			/* Dwell time (0=no loop 1-9998=mSec 9999=hold) */
+
+	/* TODO: All short XXX[2] should be long XXX instead */
+	unsigned short loopat2[2];		/* See loop 1 */
+	unsigned short llngth2[3];
+	unsigned short ldwell2;
+
+	unsigned short loopat3[2];		/* See loop 1 */
+	unsigned short llngth3[3];
+	unsigned short ldwell3;
+
+	unsigned short loopat4[2];		/* See loop 1 */
+	unsigned short llngth4[3];
+	unsigned short ldwell4;
+
+	unsigned short loopat5[2];		/* See loop 1 */
+	unsigned short llngth5[3];
+	unsigned short ldwell5;
+
+	unsigned short loopat6[2];		/* See loop 1 */
+	unsigned short llngth6[3];
+	unsigned short ldwell6;
+
+	unsigned short loopat7[2];		/* See loop 1 */
+	unsigned short llngth7[3];
+	unsigned short ldwell7;
+
+	unsigned short loopat8[2];		/* See loop 1 */
+	unsigned short llngth8[3];
+	unsigned short ldwell8;
+	
+	unsigned char sspare[2];		/* Spare bytes used internally */
+	unsigned short sspair;			/* Address of stereo partner (internal use) */
+	unsigned short ssrate;			/* Sample rate in Hz */
+	unsigned char shlto;				/* Hold loop tune offset (+/-50 cents) */
+	/* Type of playback values:-
+	 * 0 = normal looping
+	 * 1 = Loop until release
+	 * 2 = No looping
+	 * 3 = Play to sample end
+	 */
+} LakaiSampleHeader;
+
+/* TODO: Add here: Drum trigger unit block */
+typedef struct
+{
+	unsigned char d1oper;
+	unsigned char d1exch;
+	unsigned char d1thru;
+	unsigned char drname[12];
+
+	unsigned char dchan1;
+	unsigned char dnote1;
+	unsigned char dsens1;
+	unsigned char dtrig1;
+	unsigned char dvcrv1;
+	unsigned char dcatp1;
+	unsigned char drcvr1;
+	unsigned short dontm1;
+	
+	unsigned char dchan2;
+	unsigned char dnote2;
+	unsigned char dsens2;
+	unsigned char dtrig2;
+	unsigned char dvcrv2;
+	unsigned char dcatp2;
+	unsigned char drcvr2;
+	unsigned short dontm2;
+	
+	unsigned char dchan3;
+	unsigned char dnote3;
+	unsigned char dsens3;
+	unsigned char dtrig3;
+	unsigned char dvcrv3;
+	unsigned char dcatp3;
+	unsigned char drcvr3;
+	unsigned short dontm3;
+	
+	unsigned char dchan4;
+	unsigned char dnote4;
+	unsigned char dsens4;
+	unsigned char dtrig4;
+	unsigned char dvcrv4;
+	unsigned char dcatp4;
+	unsigned char drcvr4;
+	unsigned short dontm4;
+	
+	unsigned char dchan5;
+	unsigned char dnote5;
+	unsigned char dsens5;
+	unsigned char dtrig5;
+	unsigned char dvcrv5;
+	unsigned char dcatp5;
+	unsigned char drcvr5;
+	unsigned short dontm5;
+	
+	unsigned char dchan6;
+	unsigned char dnote6;
+	unsigned char dsens6;
+	unsigned char dtrig6;
+	unsigned char dvcrv6;
+	unsigned char dcatp6;
+	unsigned char drcvr6;
+	unsigned short dontm6;
+	
+	unsigned char dchan7;
+	unsigned char dnote7;
+	unsigned char dsens7;
+	unsigned char dtrig7;
+	unsigned char dvcrv7;
+	unsigned char dcatp7;
+	unsigned char drcvr7;
+	unsigned short dontm7;
+	
+	unsigned char dchan8;
+	unsigned char dnote8;
+	unsigned char dsens8;
+	unsigned char dtrig8;
+	unsigned char dvcrv8;
+	unsigned char dcatp8;
+	unsigned char drcvr8;
+	unsigned short dontm8;
+	
+	/* unit 2 to follow here */
+} LakaiDrumTrigger;
+
+/* A LakaiMiscData structure contains some miscellaneous settings like
+ * MIDI channel, OMNI setting etc.
+ */
+typedef struct
+{
+	unsigned char bmchan;		/* Basic MIDI channel (0-15) for MIDI program select */
+	unsigned char bmomni;		/* Basic channel Omni (0=off 1=on) */
+	unsigned char pselen;		/* MIDI program select enable (0=off 1=on) */
+	unsigned char selpnm;		/*	Selected program number (0-127) */
+	unsigned char omnovr;		/* MIDI play commands Omni override (0=off 1=on) */
+	unsigned char exchan;		/* MIDI exclusive channel (0-127) */
+} LakaiMiscData;
+
+/* TODO: still missing here: Multi & effect structure */
+
+/*
+ * lakai_init()
+ *
+ * Performs some library initializations. To be called by each application
+ * before it can start using the other liblakai functions.
+ *
+ * INPUTS:
+ *  -
+ * OUTPUTS:
+ * -
+ * RETURNS:
+ * - (should always suceed)
+ */
+
+void lakai_init( void );
+
+/*
+ * open the given SCSI generic file (typically something like "/dev/sgx") for
+ * I/O. This requires the "sg" driver to be available either as a module or
+ * built into the kernel.
+ *
+ * INPUTS:
+ * 	char *devname: pointer to name of the device special file to be opened
+ *
+ * OUTPUTS:
+ * 	-
+ * RETURNS:
+ * 	LHANDLE: handle to be used in further calls to lakai functions
+ * 	If lakai_open fails, returns -1.
+ *
+ */
+LHANDLE lakai_open( char *devname );
+
+
+/*
+ * close the device special file that was previously opened with lakai_open().
+ * No further actions are performed - the sampler device is NOT implictly put
+ * back into NON-MIDI-OVER-SCSI mode, for instance. The user app has to do this
+ * by itself first.
+ * After a call to lakai_close() the LHANDLE given back earlier should not be
+ * used anymore.
+ *
+ * INPUTS:
+ * 	LHANDLE handle: handle to a previously opened device special file
+ *
+ * OUTPUTS:
+ * 	-
+ * 
+ * RETURNS:
+ * 	0 if lakai device was successfully closed, -1 on error.
+ *
+ */
+int lakai_close( LHANDLE handle );
+
+
+#define LAKAI_MODE_NORMAL		0x00
+#define LAKAI_MODE_SCSI_MIDI	0x01
+#define LAKAI_MODE_SCSI_BULK	0x02
+
+/*
+ * sets the working mode of the sampler to one of these:
+ * "standard" (MIDI over serial), MIDI over SCSI, or SCSI bulk
+ * 
+ * "standard" MIDI means release the lock of the sampler.
+ * This should always be called when an application has finished
+ * exchanging data with the sampler (especially when the app quits).
+ * 
+ * "MIDI over SCSI" is used for getting the basic low-volume data
+ * like status report, resident program/sample list, sample headers etc.
+ * 
+ * "SCSI bulk" is the mode used to transfer large chunk of data
+ * (typically sample data)
+ *
+ * INPUTS: 
+ * 	handle: LHANDLE to opened LAKAI device
+ * 	mode: desired sampler mode, see LAKAI_MODE_* definitions above
+ *
+ *  RETURNS: 
+ *  	0 if mode change was successful, -1 on error
+ *  
+ *  NOTE: 
+ *  	The user could possibly change the setting himself through the panel
+ *  	of the sampler (setting "MIDI via SCSI on/off" in the Global section).
+ *  	However, he had better not interfere with the app here, only in cases
+ *  	where the app is obviously "stuck" and cannot reset the state.
+ */
+int lakai_setmode(LHANDLE handle, int mode);
+
+/*
+ * Convert a string of characters from ASCII to Akai representation
+ * Non-convertible characters in src get mapped to Space (' ') char in dest.
+ *
+ * INPUTS:
+ * 	char *src: pointer to array that holds the original string
+ * 	char *dest: pointer to array that holds the converted string
+ * 	int len: # of characters to be converted
+ *
+ * 	The user has to make sure "dest" can hold "len" bytes.
+ * 
+ * OUTPUTS:
+ * 	Converted string is in "dest" buffer.
+ *
+ * RETURNS:
+ * 	-
+ */
+void lakai_asciitoakai(char *src, char *dest, int len);
+
+
+/*
+ * Convert a string of characters from Akai representation to ASCII 
+ * Non-convertible characters in src get mapped to Space (' ') char in dest.
+ *
+ * INPUTS:
+ * 	char *src: pointer to array that holds the original string
+ * 	char *dest: pointer to array that holds the converted string
+ * 	int len: # of characters to be converted
+ *
+ * 	The user has to make sure "dest" can hold "len" bytes.
+ * 
+ * OUTPUTS:
+ * 	Converted string is in "dest".
+ * 	
+ * RETURNS:
+ * 	-
+ */
+void lakai_akaitoascii(char *src, char *dst, int len);
+
+
+#if 0
+
+/* New thoughts, Apr 22, 2001 (sigh..):
+ * I think I can get all of the "numbers" from the status report (# of program, samples, ...)
+ * So, the application has to get this information first anyway, and if it then requests
+ * the actual program data, it can also allocate the required space for that data itself.
+ * Consequently, it will also have to free that memory by itself etc.
+ * This frees the lib from doing that. Sounds good. Stick with that.
+ *
+ * Another advantage to that solution is that there is far less complexity in the lib
+ * if it has to do memory-allocation housekeeping: Let's say the lib does the allocation
+ * of data structures, it basically tells the client app "here is the data you asked for;
+ * I allocated the memory for you to use it; now go and use it, and when you're finished,
+ * please tell me so I can free the memory then". The client app can keep the data allocated
+ * as long as it wants, requiring some kind of resource management in the lib. But I believe
+ * this is the wrong approach; clasically, the app malloc()s the memory and tells the lib
+ * where the buffer is so that the lib (/kernel) can fill that buffer with data. If the
+ * buffer is too small, that's the client's fault, so "you wanted to shoot yourself in the
+ * foot, please go ahead" from the point of view of the lib. Basta.
+ *
+ * The lib will get the raw (+sysex header etc) data from the device, but hands that
+ * data over to the app in the "pre-chewed" form, that is, after stripping off SysEx stuff.
+ *
+ * When going the other direction, the app wanting to send program data etc to the sampler,
+ * it only has to prepare the data structure, and the lib will take care of providing it
+ * with SysEx headers, fragmentation etc. and send the packets to the sampler.
+ *
+ * 
+ */
+
+#define LAKAI_DEVTYPE_S1000 	0x01
+#define LAKAI_DEVTYPE_S2000	0x02
+/* ... more devtypes here when known.. */
+
+/* 
+ * identify a given device as Akai S1000 (or not)
+ * TODO: Is this really needed? 
+ * This could be integrated into the lakai_open() call..
+ *
+ * INPUTS:
+ * 	LHANDLE to opened LAKAI device
+ * 
+ * RETURNS:
+ * 	the type of the connected device, see LAKAI_DEVTYPE_ definitions above
+ */
+int lakai_identifydevice(LHANDLE handle);
+
+
+/* 
+ * test if the connected unit is ready to receive commands or send
+ * data to the computer.
+ * TODO: Do we really need this?
+ *
+ * INPUTS:
+ * 	LHANDLE to opened LAKAI device
+ *
+ * RETURNS:
+ * 	FALSE (0) or not ready, TRUE else.
+ */
+int lakai_testunitready(LHANDLE handle);
+
+
+/*
+ * gets the number of bytes available to be transferred back
+ * to the host
+ *
+ * INPUTS:
+ * 	handle: LHANDLE to opened LAKAI device
+ *
+ * RETURNS:
+ * 	numbytes: number of waiting bytes
+ */
+
+/* #### TODO: Do I really need this? */
+long lakai_getavailable(LHANDLE handle);
+
+#endif
+
+/*
+ * reads a sample of 'len' bytes of data from the sampler into
+ * the supplied buffer. The user has to take care that the buffer
+ * is sufficiently large. Also, the user has to find out in advance
+ * how large the expected sample is going ot be (in bytes) through
+ * the lakai_get_sample_header() function.
+ *
+ * INPUTS:
+ * 	handle: LHANDLE to opened LAKAI device
+ * 	buffer: buffer to store transferred data into
+ * 	len: number of bytes to be transferred
+ *
+ * RETURNS:
+ * 	number of bytes that have actually been transferred,
+ * 	0 if an error occurred.
+ */
+long lakai_get_sample(LHANDLE handle, int samplenum, char *buffer, long locat, long len);
+
+
+#if 0
+
+/*
+ * writes a block of 'len' bytes of data to the sampler from
+ * the supplied buffer. The user has to take care that the buffer
+ * is sufficiently large and that not too much data gets sent
+ * (does the Sx000 have memory protection? Probably not..)
+ *
+ * INPUTS:
+ * 	handle: LHANDLE to opened LAKAI device
+ * 	buffer: buffer to read data to be transferred from
+ * 	len: number of bytes to be transferred
+ *
+ * RETURNS:
+ * 	number of bytes that have actually been transferred,
+ * 	0 if an error occurred.
+ */
+long lakai_putdatablock(LHANDLE handle, char *buffer, long len);
+
+
+/*
+ * (internal?) low-level functions:
+ *
+ * (RequestErrorSense?) - check if the last operation yielded an error..
+ * (ScanForSampler?) - check /dev/sg0../dev/sg15 for an Akai S1000
+ *
+ * IdentifyDevice(): check model number of SCSI device, ...
+ *
+ * TestUnitReady(): see if the unit is able to answer requests
+ *
+ * SetMidiMode(..): sets to either "normal" (MIDI over serial cable), 
+ * SCSI_MIDI (MIDI over SCSI) or SCSI_BULK (bulk data transfer over SCSI).
+ *
+ * GetNumAvailable(): get the number of bytes waiting to be received from sampler
+ * 
+ * GetDataBlock(): read a block of data from sampler using SCSI_BULK mode
+ *
+ * PutDataBlock(): write a block of data to the sampler using SCSI_BULK mode
+ *
+ * ### for GetDataBlock() and PutDataBlock(): In case of large transfers, might
+ * make sense to give the user feedback (progress indicator). This requires a callback
+ * mechanism, or asynchronous operation (thread?).. todo, later..
+ * 
+ * I will not support bulk transfers in non-bulk mode, that's just too plain slow
+ * (even using MIDI over SCSI, it comes in 120-byte packages, producing far too 
+ * much overhead - I got around 10 - 20 kBytes/sec this way..)
+ * 
+ * Design question: How much work do I want to put into the lib/the client?
+ * That is, who should have to process resident sample/program lists, the lib
+ * or the client?
+ * 1) If it's the lib, the client program can be kept very short&simple; but it
+ * might not be flexible enough this way.
+ * 2) If it's in the client, it has got more work to do, the lib becomes smaller,
+ * and the client is more flexible.
+ *
+ * Right now I think 2) is better suited. That would mean the lib does not do
+ * much more but:
+ * - Init functions, TestUnitReady
+ * - bulk send/receive
+ * - SCSI_MIDI receiving of resident program/sample/etc lists without parsing stuff
+ *
+ * The client just requests this data and has to do things like name translation
+ * itself..really? Doesn't seem to make sense..
+ * 
+ *
+ *
+ *
+ * HIGH-LEVEL FUNCTIONS: (directly related to the corresponding MIDI SysEx messages)
+ * R: data that is only read from sampler
+ * RW: data that can be both read and written)
+ *
+ * All of these functions should get their data in MIDI-over-SCSI mode, so they need
+ * to make sure the sampler is in that mode before talking to it.
+ */
+
+#endif
+
+/*
+ * get the current status from the S1000; includes information such as current
+ * number of used programs, samples...
+ *
+ * INPUTS:
+ * 	LHANDLE handle: handle to previously opened device special file
+ * 	LAKAI_STATUS: pointer to LAKAI_STATUS structure to fill in from sampler
+ *
+ * RETURNS:
+ */
+int lakai_get_status_report(LHANDLE handle, LakaiStatus *ls);
+
+
+/*
+ * Retrieves the current name list of resident program.
+ *
+ * INPUTS:
+ * 	LHANDLE handle: handle to previously opened Lakai device
+ * 	LakaiProgramList *lp: pointer to struct which will be filled in with
+ * 	program names data
+ *
+ * OUTPUTS:
+ * 	lp->prognames[] is filled with the resident program name list
+ * 	
+ * RETURNS:
+ * 	int: number of program names that have been received from sampler
+ */
+int lakai_get_program_list(LHANDLE handle, LakaiProgramList *lp);
+
+
+/*
+ * free a program list that was previously allocated & retrieved through
+ * lakai_get_programlist().
+ *
+ * INPUTS:
+ * 	LakaiProgramList *lp: pointer to LakaiProgramList struct
+ *
+ * OUTPUTS:
+ * 	All program name memory allocated in the LakaiProgramList struct will
+ * 	be freed.
+ *
+ * RETURNS:
+ * 	-
+ */
+void lakai_free_program_list(LakaiProgramList *lp);
+	
+
+/*
+ * Retrieves the current name list of resident samples.
+ *
+ * INPUTS:
+ * 	LHANDLE handle: handle to previously opened Lakai device
+ * 	LakaiSampleList *ls: pointer to struct which will be filled in with
+ * 	sample names data
+ *
+ * OUTPUTS:
+ * 	lp->sampnames[] is filled with the resident sample name list
+ * 	
+ * RETURNS:
+ * 	int: number of sample names that have been received from sampler
+ */
+int lakai_get_sample_list(LHANDLE handle, LakaiSampleList *ls);
+	
+
+/*
+ * free a sample name list that was previously allocated & retrieved through
+ * lakai_get_samplelist().
+ *
+ * INPUTS:
+ * 	LakaiSampleList *lp: pointer to LakaiSampleList struct
+ *
+ * OUTPUTS:
+ * 	All sample name memory allocated in the LakaiSampleList struct will
+ * 	be freed.
+ *
+ * RETURNS:
+ * 	-
+ */
+
+void lakai_free_sample_list(LakaiSampleList *ls);
+
+
+
+/*
+ * gets the program common data for one program. This is a fixed data
+ * structure (might differ between old and more recent sampler models - S1000
+ * seems to have a length of 150 bytes, while S2000/2800 etc have 192 bytes here.
+ * However, if I send an S1000 "RPDATA" command, I should only look at the first 150
+ * bytes.
+ * If the requested program does not exist, returns an error.
+ *
+ * INPUTS:
+ * 	LHANDLE handle: handle to previously opened Lakai device
+ * 	int prognum: number of program whose common data to retrieve
+ *
+ * OUTPUTS:
+ * 	stores the program common data for this program into lpr
+ *
+ * RETURNS:
+ * 	-1 if an error occurred (program does not exist etc),
+ * 	number of bytes transferred otherwise (150/192)
+ */
+int lakai_get_program(LHANDLE handle, int prognum, unsigned char *data);
+
+
+
+/*
+ * gets the keygroup data for a keygroup in a program. This is a fixed data structure
+ * (might differ between old and more recent sampler models?).
+ * If the keygroup does not exist, returns an error.
+ *
+ * INPUTS:
+ * 	LHANDLE handle: handle to previously opened device special file
+ * 	int prognum: number of program in whose keygroups we're interested
+ * 	int keygroupnum: number of keygroup whose data to retrieve
+ *
+ * OUTPUTS:
+ * 	stores the keygroup data for this keygroup into lk
+ *
+ * RETURNS:
+ * 	-1 if an error occurred (keygroup does not exist etc)
+ * 	number of bytes transferred otherwise
+ */
+int lakai_get_keygroup(LHANDLE handle, int prognum, int keygroupnum, unsigned char *data);
+
+
+/*
+ * gets the sample header data for one sample header. This is a fixed
+ * data structure (might differ between old and more recent sampler models?).
+ * If the sample header does not exist, returns an error.
+ *
+ * INPUTS:
+ * 	LHANDLE handle: handle to previously opened device special file
+ * 	int headernum: number of sample header whose common data to retrieve
+ *
+ * OUTPUTS:
+ * 	stores the sample header data for this sampler header into lsh
+ *
+ * RETURNS:
+ * 	-1 if an error occurred (sample header does not exist etc)
+ * 	number of bytes transferred otherwise
+ */
+int lakai_get_sample_header(LHANDLE handle, int samplenum, unsigned char *data);
+
+
+/*
+ * sends a sample header structure of 'len' bytes from the supplied buffer
+ * to the sampler. The user has to take care that the buffer
+ * is prefilled with data. 
+ *
+ * INPUTS:
+ * 	handle: LHANDLE to opened LAKAI device
+ * 	samplenum: Number of the sample to overwrite/create. If the number is
+ * 	higher than the highest number of a resident sample header, a new
+ * 	sample entry is being created.
+ * 	len: Length of sample header data that follows
+ * 	buffer: buffer that holds the sample header data to be transferred
+ *
+ * RETURNS:
+ * 	number of bytes that have actually been transferred,
+ * 	0 if an error occurred.
+ */
+int lakai_put_sample_header(LHANDLE handle, int samplenum, unsigned char *data, int len);
+
+
+/*
+ * Deletes the program "prognum". If program does not exist, returns an error.
+ * Otherwise, that program and all its keygroups will be deleted.
+ * Of course this does not delete any sample header or sample data.
+ *
+ * INPUTS:
+ * 	LHANDLE handle: handle to previously opened device special file
+ * 	int prognum: number of program to be deleted
+ *
+ * OUTPUTS:
+ *    -
+ *
+ * RETURNS:
+ * 	-1 if an error occurred (program does not exist etc)
+ * 	0 (or program number?) otherwise
+ */
+int lakai_delete_program(LHANDLE handle, int prognum);
+
+
+/*
+ * Deletes the keygroup "kgrpnum" in the program "prognum". If the keygroup 
+ * in this program (or that program itself) does not exist, returns an error.
+ * Otherwise, the keygroup will be deleted.
+ * Of course this does not delete any sample header or sample data.
+ *
+ * INPUTS:
+ * 	LHANDLE handle: handle to previously opened device special file
+ * 	int prognum: number of program 
+ * 	int kgrpnum: Number of keygroup to be deleted
+ *
+ * OUTPUTS:
+ *    -
+ *
+ * RETURNS:
+ * 	-1 if an error occurred (program does not exist etc)
+ * 	0 (?) otherwise
+ */
+int lakai_delete_keygroup(LHANDLE handle, int prognum, int kgrpnum);
+
+
+/*
+ * Deletes the sample "samplenum". If that sample does not exist, 
+ * returns an error.  Otherwise, the sample and its sample header data
+ * will be deleted.
+ * Programs using this sample will be unaffected, but this may create
+ * unpleasant situations (to be checked..)
+ *
+ * INPUTS:
+ * 	LHANDLE handle: handle to previously opened device special file
+ * 	int samplenum: number of sample to delete
+ *
+ * OUTPUTS:
+ *    -
+ *
+ * RETURNS:
+ * 	-1 if an error occurred (sample does not exist etc)
+ * 	0 (?) otherwise
+ */
+int lakai_delete_sample(LHANDLE handle, int samplenum);
+
+/*
+ * Retrieves the "miscellaneous" data from the sampler. Contents of this
+ * data field seems to depend heavily on the sampler model used.
+ *
+ * INPUTS:
+ * 	LHANDLE handle: handle to previously opened device special file
+ * 	data: pointer to a unsigned char buffer that can hold at least TODO xxx
+ * 	bytes.
+ *
+ * OUTPUTS:
+ *    The buffer is filled with the misc data
+ *
+ * RETURNS:
+ * 	int res: Number of bytes transferred by the call
+ *
+ */
+int lakai_get_miscdata(LHANDLE handle, unsigned char *data);
+
+/*
+ * Replaces an existing program or creates a new program in the sampler.
+ * From the SysEx docs: "If the program number is above the highest existing 
+ * program number, a new program will be created (if sufficient blocks are 
+ * free - one for the program common block and one for each keygroup as 
+ * specified by the parameter GROUPS). The created program will have dummy 
+ * keygroups with unspecified data; the appropriate number of keygroup data
+ * messages should be given immediately. If the program name in data is
+ * the same as that of any existing program, that program will be deleted
+ * first. If the program number is of an existing program, the existing
+ * data will be replaced but the parameter GROUPS must be correct. This 
+ * allows complete freedem to change parameters - the use of a duplicate
+ * program name should be avoided. If either error situation occurs, an error
+ * message will be given, otherwise an OK message will be given".
+ *
+ * gets the program common data for one program. This is a fixed data
+ * structure (might differ between old and more recent sampler models - S1000
+ * seems to have a length of 150 bytes, while S2000/2800 etc have 192 bytes here.
+ * However, if I send an S1000 "RPDATA" command, I should only look at the first 150
+ * bytes.
+ * If the requested program does not exist, returns an error.
+ *
+ * INPUTS:
+ * 	LHANDLE handle: handle to previously opened Lakai device
+ * 	int prognum: number of program to overwrite/create
+ * 	int len: Length of the data block following in "data"
+ * 	unsigned char *data: Pointer to program data
+ *
+ * OUTPUTS:
+ * 	-
+ *
+ * RETURNS:
+ * 	-1 if an error occurred (no mem, GROUPS does not match etc),
+ * 	0 otherwise
+ */
+int lakai_put_program(LHANDLE handle, int prognum, unsigned char *data, int len);
+
+
+/*
+ * Replaces an existing keygroup in a program, or creates a new keygroup.
+ * From The SysEx docs: "In the case of transmitting to the S1000, if
+ * the keygroup number is above the highest existing keygroup number, a new
+ * keygroup will be created if a block is free, otherwise the existing 
+ * keygroup will be replaced. The use of program number 255 is a special 
+ * case where the keygroup data will be installed in the program previously 
+ * created. This avoids the need to read the program list to find out what 
+ * number was allocated to that program. If there are no free blocks for a 
+ * new keygroup, an error message will be given".
+ * 
+ * INPUTS:
+ * 	LHANDLE handle: handle to previously opened Lakai device
+ * 	int prognum: number of program iin which to overwrite/create a keygroup
+ * 	int kgrpnum: number of keygroup to create/overwrite
+ * 	unsigned char *data: Pointer to keygroup data
+ *
+ * OUTPUTS:
+ * 	-
+ *
+ * RETURNS:
+ * 	-1 if an error occurred (no mem, GROUPS does not match etc),
+ * 	0 otherwise
+ */
+int lakai_put_keygroup(LHANDLE handle, int prognum, int kgrpnum, unsigned char *data, int len);
+
+
+/*
+ * sends a sample of 'len' bytes of data from the supplied buffer
+ * to the sampler. The user has to take care that the buffer
+ * is prefilled with data. Also, the user has to create a new sample
+ * entry first before sending data into it.
+ *
+ * INPUTS:
+ * 	handle: LHANDLE to opened LAKAI device
+ * 	buffer: buffer that holds the data to be transferred
+ * 	len: number of bytes to be transferred
+ *
+ * RETURNS:
+ * 	number of bytes that have actually been transferred,
+ * 	0 if an error occurred.
+ */
+long lakai_put_sample(LHANDLE handle, int samplenum, char *buffer, long locat, long len);
+
+
+#if 0
+/*
+ * S1000/900 (original sampler):
+ * (F) = returns fixed number of bytes, (V) returns variable number of bytes
+ * lakai_get_status_report()  (F)
+ * lakai_get_program_names()  (V)
+ * lakai_get_sample_names()   (V)
+ * lakai_get_program()        (V...depends on # of keygroups in program..?)
+ * lakai_get_keygroup()       (F)
+ * lakai_get_sample_header()  (F)
+ * lakai_get_drum_settings()  (F)  (later..)
+ * lakai_get_misc_data()      (F)
+ *
+ * Note: RSPACK (Request Sample Data Packet(s)) and ASPACK (Accept Sample Data
+ * Packet(s)) are not implemented because we use the faster way of directly
+ * transferring blocks of memory from/to the sampler, bypassing the MIDI protocol.
+ * 
+ * lakai_put_program()
+ * lakai_put_keygroup()
+ * lakai_put_sample_header()
+ * lakai_put_drum_settings()   (later..)
+ * lakai_put_misc_data()
+ *
+ * lakai_delete_program()   (also deletes the associated keygroups)
+ * lakai_delete_keygroup(programnum, keygroupnum)
+ * lakai_delete_sample()   (also deletes the associated sampler header data)
+ * lakai_set_exclusive()   (do I really need this?)
+ *
+ * S2000/S3000XL/S3200XL:
+ * lakai_get_multi()
+ * lakai_put_multi()
+ *
+ * S2800/S3000/S3200:
+ * ???
+ *
+ * S5000/S6000:
+ * ?? (need more info)
+ *
+ * yet unsorted:
+ * FX/reverb bytes (R/W)
+ * CueList bytes (R/W)
+ * TakeList entries (R/W)
+ * VolumeList entry (R/W)
+ * Harddisk Directory entry (R/W)
+ */
+
+
+ /*
+  * design question: when returning a MIDI message like from lakai_get_sample_names(),
+  * should I strip off the SysEx header/EOX myself or pass this all over to the app?
+  * Right now I'm for passing it all over to the app..
+  */
+
+
+/*
+ * More new thoughts, 31.10.2002 :-)
+ *
+ * open function: can handle up to 16 simultaneous connections. Returns as "handle"
+ * an index into an array like this:
+ * #define MAX_LHANDLES 16
+ * typedef struct
+ * {
+ *   int fd;
+ *   ...possibly more maintenance data goes here..
+ * } Lakai_handle;
+ * Lakai_handle lhandles[MAX_LHANDLES];
+ *
+ *
+ * Error handling: How does libsndfile do this?
+ * Like this:
+ * "sf_error_str () returns the current error message to the caller in the
+ * string buffer provided."
+ *
+ * int      sf_error_str   (SNDFILE *sndfile, char* str, size_t len) ;
+ *
+ * The error string list looks like this:
+ * typedef struct
+ *	{  int   error ;
+ *		char  *str ;
+ *	} ErrorStruct ;
+ *
+ *	static
+ *	ErrorStruct SndfileErrors [] =
+ *	{
+ *		{  SFE_NO_ERROR, 	"No Error." },
+ *		{  SFE_BAD_FILE,	"File does not exist or is not a regular file (possibly a pipe?)." },
+ *		{  SFE_BAD_FILE_READ    , "File exists but no data could be read." },
+ * ...
+ * }
+ *
+ *
+ *
+ * Anyway, if a function runs into an error, it will usually give a return
+ * code of NULL or 0 and set a "lakai_err" variable (either a static one..
+ * no, not thread-safe) or a call-by-ref variable of the caller.
+ * The call can then do with this whatever it wants, like ignore, print
+ * error string to stdout, show an error window or whatever..
+ * Example (as seen from user perspective):
+ * ret = lakai_xxxx(params, ..., &err);
+ * if (!ret)
+ *   fprintf(stderr, "Error while reading: %s\n", lakai_errstr(err));
+ *   exit(5);
+ *
+ *  -----------------------------------------------
+ *  Another q: Do I need a mostly centralized "do the actual scsi command" function?
+ *  If some kind of set-up of data structures is repeated in several locations over and
+ *  over, this makes sense.
+ *  What parameter would this function take then?
+ *
+ */
+
+#endif

Added: lakai/branches/upstream/current/lakbak.c
===================================================================
--- lakai/branches/upstream/current/lakbak.c	2004-10-28 08:35:57 UTC (rev 14)
+++ lakai/branches/upstream/current/lakbak.c	2004-10-28 09:32:22 UTC (rev 15)
@@ -0,0 +1,208 @@
+
+/*
+ * lback.c - perform a complete backup of all samplesand programs of the S2000
+ *
+ * Copyright (c) 2002-2004 Frank Neumann <franky@users.sourceforge.net>
+ *
+ */
+
+#include <stdio.h>
+
+#include "lakai.h"
+
+int main(int argc, char *argv[])
+{
+	int i, j, idx, numprogs, numsamps, numkeygroups, res;
+	FILE *fp1, *lfp;
+	unsigned char *buf;
+	unsigned long slocat, slngth, shdrlength;
+	LHANDLE hd1;
+	LakaiStatus lst;
+	LakaiProgramList lp;
+	LakaiSampleList ls;
+	LakaiProgram lpr;
+	LakaiKeygroup lkg;
+	unsigned char shdrdata[1024];  /* TODO: How much really? */
+	unsigned char progdata[1024];  /* ..".. */
+	unsigned char kgrpdata[1024];  /* ..".. */ 
+	unsigned char miscdata[1024];  /* ..".. */ 
+	unsigned char fname[32];
+	
+	if (argc != 3 )
+	{
+		fprintf(stderr, "Usage: %s <devname> <listfile>\n", argv[0]);
+		exit(1);
+	}
+
+	fprintf(stderr, "\n= Phase 0: Initializing =\n");
+	lakai_init();
+
+	hd1 = lakai_open(argv[1]);
+	if (hd1 < 0)
+	{
+		fprintf(stderr, "Open failed.\n");
+		exit(5);
+	}
+
+	/* list file: this one contains the file names of samples and programs,
+	 * IN THE ORDER IN WHICH THEY HAVE TO BE RESTORED (this is important)
+	 */
+	lfp = fopen(argv[2], "w");
+	if (!lfp)
+	{
+		fprintf(stderr, "Unable to open list file '%s' for writing\n", argv[2]);
+		lakai_close(hd1);
+		exit(5);
+	}
+
+	fprintf(lfp, "# Lakai list file. This file was auto-generated by the Lakai lakbak\n");
+	fprintf(lfp, "# program. If you modify it, better know what you are doing.\n");
+	fprintf(lfp, "#\n");
+
+	/* TEST: get misc data.. */
+	res = lakai_get_miscdata(hd1, miscdata);
+//	fprintf(stderr, "Got %d bytes of misc data\n", res);
+	/* TODO: Write out misc data to a file? */
+	
+
+#if 1
+	fprintf(stderr, "\n= Phase 1: Restoring sample data =\n");
+//	fprintf(stderr, "############ GET SAMPLE NAMES: #############\n");
+	numsamps = lakai_get_sample_list(hd1, &ls);
+	if (numsamps > 0)
+	{
+		fprintf(lfp, "# Samples (and sample header) files: %d samples\n", numsamps);
+		fprintf(lfp, "#\n");
+		fprintf(stderr, "Found %d sample names\n", numsamps);
+#if 0
+		for (i = 0; i < numsamps; i++)
+			fprintf(stderr, "%03d: %s\n", i, ls.samplenames[i]);
+#endif
+//	fprintf(stderr, "Beginning restore...\n");
+
+		for (i = 0; i < numsamps; i++)
+		{
+//			fprintf(stderr, "%03d: %s\n", i, ls.samplenames[i]);
+//			fprintf(stderr, "\r- Getting sample header data...\n");
+			shdrlength = lakai_get_sample_header(hd1, i, shdrdata);
+//			fprintf(stderr, "Got %d bytes of sample header data\n", shdrlength);
+			fprintf(stderr, "Retrieving sample #%d...", i);
+			slocat = (shdrdata[25] * 16777216 + 
+						 shdrdata[24] * 65536 +
+						 shdrdata[23] * 256 + 
+						 shdrdata[22]) * 2;
+			slngth = (shdrdata[29] * 16777216 + 
+						 shdrdata[28] * 65536 +
+						 shdrdata[27] * 256 + 
+						 shdrdata[26]) * 2;
+
+//			fprintf(stderr, "\r- Sample start address: %ld\n", slocat);
+//			fprintf(stderr, "\r- Sample length: %ld\n", slngth);
+
+//			fprintf(stderr, "\r- Getting sample data...\n");
+			buf = (unsigned char *)malloc(slngth);
+			if (!buf)
+			{
+				fprintf(stderr, "Unable to malloc %d bytes sample buffer\n", slngth);
+				exit(5);
+			}
+			memset(buf, 0xea, slngth);
+			res = lakai_get_sample(hd1, i, buf, 0, slngth);
+
+			/* Put together filename, replace " " by "_" */
+			for (idx = 0; idx < strlen(ls.samplenames[i]); idx++)
+			{
+				if (ls.samplenames[i][idx] == ' ')
+					fname[idx] = '_';
+				else
+					fname[idx] = ls.samplenames[i][idx];
+			}
+			fname[idx] = '\0';
+			strcat (fname, ".s");
+				
+//	printf("Fname: '%s'\n", ls.samplenames[i]);
+			fp1 = fopen(fname, "w");
+			fwrite(shdrdata, shdrlength, 1, fp1);
+			fwrite(buf, slngth, 1, fp1);
+			fclose(fp1);
+			free(buf);
+			fprintf(lfp, "SAMP %s\n", fname);
+			fprintf(stderr, "done (file '%s', %d bytes).\n", fname, slngth);
+		}
+	}
+	else /* numsamps == 0 */
+	{
+		fprintf(stderr, "-- No resident sample names found. --\n");
+		fprintf(lfp, "# Samples (and sample header) files: NONE\n");
+		fprintf(lfp, "#\n");
+	}
+
+	lakai_free_sample_list(&ls);
+
+	fprintf(lfp, "#\n");
+#endif
+
+	fprintf(stderr, "\n= Phase 2: Restoring program data =\n");
+	/* restore all programs */
+	numprogs = lakai_get_program_list(hd1, &lp);
+	if (numprogs > 0)
+	{
+		fprintf(lfp, "# Program (and keygroup) files: %d programs\n", numprogs);
+		fprintf(lfp, "#\n");
+		fprintf(stderr, "Found %d program names\n", numprogs);
+		for (i = 0; i < numprogs; i++)
+		{
+			fprintf(stderr, "Retrieving program #%d...", i);
+			res = lakai_get_program(hd1, i, progdata);
+			
+			for (idx = 0; idx < strlen(lp.prognames[i]); idx++)
+			{
+				if (lp.prognames[i][idx] == ' ')
+					fname[idx] = '_';
+				else
+					fname[idx] = lp.prognames[i][idx];
+			}
+			fname[idx] = '\0';
+			strcat (fname, ".p");
+
+			/* retrieve the # of keygroups in this program */
+			numkeygroups = progdata[42];
+//			fprintf(stderr, "%03d: %s, %d keygroups\n", i, lp.prognames[i], numkeygroups);
+				
+			fp1 = fopen(fname, "w");
+			fwrite(progdata, res, 1, fp1);
+
+			for (j = 0; j < numkeygroups; j++)
+			{
+				res = lakai_get_keygroup(hd1, i, j, kgrpdata);
+//				fprintf(stderr, "got %d bytes for keygroup %d\n", res, j);
+				fwrite(kgrpdata, res, 1, fp1);
+			}
+			fclose(fp1);
+			fprintf(lfp, "PROG %s\n", fname);
+			fprintf(stderr, "done (file '%s', %d keygroups).\n", fname, numkeygroups);
+		}
+	}
+	else  /* numprogs == 0 */
+	{
+		fprintf(stderr, "-- No resident program names found. --\n");
+		fprintf(lfp, "# Program (and keygroup) files: NONE\n");
+		fprintf(lfp, "#\n");
+	}
+
+	lakai_free_program_list(&lp);
+
+	fprintf(stderr, "\n= Phase 3: Closing down =\n");
+	
+	/* This is a little asymmetric, but the user has to switch back to "normal" mode
+	 * whenever he wants to be able to access the sampler through standard MIDI
+	 */
+	lakai_setmode(hd1, LAKAI_MODE_NORMAL);
+
+	lakai_close(hd1);
+	fclose(lfp);
+	fprintf(stderr, "= Done. =\n");
+	
+	exit(0);
+}
+

Added: lakai/branches/upstream/current/lakclear.c
===================================================================
--- lakai/branches/upstream/current/lakclear.c	2004-10-28 08:35:57 UTC (rev 14)
+++ lakai/branches/upstream/current/lakclear.c	2004-10-28 09:32:22 UTC (rev 15)
@@ -0,0 +1,82 @@
+
+/*
+ * delall.c - delete all samples/programs currently in the sampler's RAM 
+ *
+ * Copyright (c) Frank Neumann 2003
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lakai.h"
+
+int main(int argc, char *argv[])
+{
+	int i, numprogs, numsamps, res;
+	LHANDLE hd1;
+	LakaiProgramList lp;
+	LakaiSampleList ls;
+	
+	if (argc !=2 )
+	{
+		fprintf(stderr, "Usage: %s <devname>\n", argv[0]);
+		exit(1);
+	}
+
+	lakai_init();
+
+	hd1 = lakai_open(argv[1]);
+	if (hd1 >= 0)
+	{
+//		fprintf(stderr, "Open suceeded, handle: %d\n", hd1);
+	}
+	else
+	{
+		fprintf(stderr, "Open failed.\n");
+		lakai_close(hd1);
+		exit(5);
+	}
+
+//	fprintf(stderr, "############ GET SAMPLE NAMES: #############\n");
+	numsamps = lakai_get_sample_list(hd1, &ls);
+	if (numsamps > 0)
+	{
+		fprintf(stderr, "Deleting all samples...\n");
+		for (i = numsamps-1; i >= 0; i--)
+		{
+			fprintf(stderr, "Delete sample #%d\n", i);
+			res = lakai_delete_sample(hd1, i);
+		}
+		fprintf(stderr, "Done.\n");
+	}
+	else
+		fprintf(stderr, "-- No resident sample names found. --\n");
+
+	lakai_free_sample_list(&ls);
+	
+	/* delete all programs (and thus their keygroups) */
+	numprogs = lakai_get_program_list(hd1, &lp);
+	if (numprogs > 0)
+	{
+		fprintf(stderr, "Deleting all programs...\n");
+		for (i = numprogs-1; i >= 0; i--)
+		{
+			fprintf(stderr, "Delete program #%d\n", i);
+			res = lakai_delete_program(hd1, i);
+		}
+		fprintf(stderr, "Done.\n");
+	}
+	else
+		fprintf(stderr, "-- No resident program names found. --\n");
+
+	lakai_free_program_list(&lp);
+
+	fprintf(stderr, "All done.\n");
+	
+	lakai_setmode(hd1, LAKAI_MODE_NORMAL);
+	lakai_close(hd1);
+	
+	exit(0);
+}
+

Added: lakai/branches/upstream/current/lakres.c
===================================================================
--- lakai/branches/upstream/current/lakres.c	2004-10-28 08:35:57 UTC (rev 14)
+++ lakai/branches/upstream/current/lakres.c	2004-10-28 09:32:22 UTC (rev 15)
@@ -0,0 +1,236 @@
+
+/*
+ * ssamp.c - send one sample header + sample to the sampler
+ *
+ * Copyright (c) 2002-2004 Frank Neumann <franky@users.sourceforge.net>
+ *
+ */
+
+/*** INCLUDES ***/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "lakai.h"
+
+/*** DEFINES ***/
+/* This is fixed for data coming from an S2000; S1000 would have smaller values here */
+#define HEADERLEN 192
+#define MAXLINE 256
+#define MAX_SAMPLENUM 255
+#define MAX_PROGNUM   254
+
+/*** PROTOTYPES ***/
+int do_send_sample(LHANDLE hd1, char *fname, int sample_idx);
+int do_send_program(LHANDLE hd1, char *fname, int prog_idx);
+
+
+
+/*** BEGIN OF MAIN ***/
+int main(int argc, char *argv[])
+{
+	int sample_idx, prog_idx;
+	FILE *lfp;
+	unsigned char linebuf[MAXLINE], fname[256];
+	LHANDLE hd1;
+
+	
+	if (argc != 3 )
+	{
+		fprintf(stderr, "Usage: %s <devname> <listfile>\n", argv[0]);
+		exit(1);
+	}
+
+	lakai_init();
+
+	hd1 = lakai_open(argv[1]);
+	if (hd1 < 0)
+	{
+		fprintf(stderr, "Open failed.\n");
+		exit(5);
+	}
+
+	lfp = fopen(argv[2], "r");
+	if (!lfp)
+	{
+		fprintf(stderr, "Unable to open list file '%s' for reading\n", argv[2]);
+		lakai_close(hd1);
+		exit(5);
+	}
+
+	sample_idx = 0;
+	prog_idx = 0;
+
+	while(fgets(linebuf, MAXLINE, lfp) != NULL)
+	{
+//		fprintf(stderr, "Line: '%s'\n", linebuf);
+
+		/* empty lines: ignored */
+		if (linebuf[0] == 0x0a || linebuf[0] == 0x0d)
+			;
+		/* comment lines: ignored */
+		else if (linebuf[0] == '#')
+			;
+		else if (strncmp(linebuf, "SAMP ", 5) == 0)
+		{
+			sscanf(linebuf+5, "%s", fname);
+			do_send_sample(hd1, fname, sample_idx++);
+		}
+		else if (strncmp(linebuf, "PROG ", 5) == 0)
+		{
+			sscanf(linebuf+5, "%s", fname);
+			do_send_program(hd1, fname, prog_idx++);
+		}
+		else
+			fprintf(stderr, "ignored unknown listfile line: %s", linebuf);
+	} 
+	
+	/* delete the very first program (the one that "always exists") */
+	lakai_delete_program(hd1, 0);
+		
+	lakai_setmode(hd1, LAKAI_MODE_NORMAL);
+	lakai_close(hd1);
+	fclose(lfp);
+	exit(0);
+}
+
+
+/*
+ * do_send_sample(): Send a single sample to sampler
+ *
+ * INPUTS:
+ * 	LHANDLE hd1: handle to lakai device
+ * 	char *fname: filename of file to send to sampler
+ * OUTPUTS:
+ * 	int: 1 if send was ok, 0 otherwise.
+ */
+int do_send_sample(LHANDLE hd1, char *fname, int sample_idx)
+{
+	FILE *fp1;
+	int flen, res;
+	unsigned char *buf;
+	unsigned long slocat;
+	unsigned char hdrbuf[1024];
+
+//	fprintf(stderr, "Sending sample from file '%s'\n", fname);
+
+	fp1 = fopen(fname, "r");
+	if (!fp1)
+	{
+		fprintf(stderr, "do_send_sample(): Could not open program file '%s'\n", fname);
+		return 0;
+	}
+	fseek(fp1, 0, SEEK_END);
+	flen = ftell(fp1),
+	fseek(fp1, 0, SEEK_SET);
+	buf = malloc(flen);
+	if (!buf)
+	{
+		fprintf(stderr, "do_send_sample(): Unable to malloc sample buf\n");
+		fclose(fp1);
+		return 0;
+	}
+
+	fprintf(stderr, "Uploading sample #%d from file '%s', %d bytes...", sample_idx, fname, flen);
+	fread(buf, flen, 1, fp1);
+	fclose(fp1);
+	
+	/* send sample header ... */
+//	fprintf(stderr, "Creating new sample header...");
+	/* 255 is a the highest possible  sample number  so we can be somewhat sure we 
+	 * create a new sample this way */
+	lakai_put_sample_header(hd1, MAX_SAMPLENUM, buf, 192);
+//	fprintf(stderr, "Done.\n");
+
+	/* determine where the sampler allocated the memory for this sample */
+//	fprintf(stderr, "Requesting sample header data...");
+	res = lakai_get_sample_header(hd1, sample_idx, hdrbuf);
+//	fprintf(stderr, "Done.\n");
+//	fprintf(stderr, "Location of this sample in mem: $%x\n", lsh.slocat);
+
+	slocat = hdrbuf[25] * 16777216 + hdrbuf[24] * 65536 + hdrbuf[23] * 256 + hdrbuf[22];
+//	fprintf(stderr, "Sample mem allocated at $%p\n", (void *)slocat);
+
+	/* It appears like there can be no "holes" between sample
+	 * numbers, and they are numbered sequentially, starting with 0, in order of their
+	 * creation. So, to bind sample numbers to sample names, the user has to keep track
+	 * of what sample headers were uploaded when - or he just has to get the whole
+	 * list again through lakai_get_sample_list().
+	 */
+
+	/* send sample data.. */
+//	fprintf(stderr, "Uploading PCM data..\n");
+	lakai_put_sample(hd1, sample_idx, buf+192, 0, flen-192);
+//	lakai_put_sample(hd1, sample_idx, buf+192, slocat, flen-192);
+	fprintf(stderr, "done.\n");
+
+	free(buf);
+	return 1;
+}
+
+
+/*
+ * do_send_program(): Sends a single program (and its keygroups) to sampler
+ *
+ * INPUTS:
+ * 	LHANDLE hd1: handle to lakai device
+ * 	char *fname: filename of file to send to sampler
+ * OUTPUTS:
+ * 	int: 1 if send was ok, 0 otherwise.
+ */
+int do_send_program(LHANDLE hd1, char *fname, int prog_idx)
+{
+	FILE *fp1;
+	int i, flen, numkeygroups;
+	unsigned char *buf;
+	
+	fp1 = fopen(fname, "r");
+	if (!fp1)
+	{
+		fprintf(stderr, "do_send_program(): Could not open program file '%s'\n", fname);
+		return 0;
+	}
+	fseek(fp1, 0, SEEK_END);
+	flen = ftell(fp1),
+	fseek(fp1, 0, SEEK_SET);
+	buf = malloc(flen);
+	if (!buf)
+	{
+		fprintf(stderr, "do_send_program(): Unable to malloc program buf\n");
+		fclose(fp1);
+		return 0;
+	}
+
+	fread(buf, flen, 1, fp1);
+	fclose(fp1);
+
+	numkeygroups = buf[42];
+	fprintf(stderr, "Uploading program #%d from file '%s', %d keygroup(s)...\n", prog_idx, fname, 
+			numkeygroups);
+
+	/* TODO: Test: set to 0 first.. */
+	buf[42] = 1;
+	
+	/* first, send the program with "empty" keygroups" */
+	lakai_put_program(hd1, MAX_PROGNUM, buf, 192);
+
+//	fprintf(stderr, "%d keygroups in this program; sending now..", numkeygroups);
+		
+	/* then, send the keygroups for this program */
+	for (i = 0; i < numkeygroups; i++)
+	{
+		/* 255 is the indicator for "create keygroup in previously sent program" */
+		fprintf(stderr, "\tUploading keygroup %d..", i);
+		/* TODO: Testing... */
+		lakai_put_keygroup(hd1, prog_idx+1, i, buf+(i+1)*192, 192);
+//		lakai_put_keygroup(hd1, 255, i, buf+(i+1)*192, 192);
+		fprintf(stderr, "done.\n");
+	}
+	
+//	fprintf(stderr, "done.\n");
+	free(buf);
+	
+	return 1;
+}
+