[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>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;
+}
+