Logo Search packages:      
Sourcecode: lakai version File versions  Download package

lakai.h

/*
 * lakai.h - liblakai header file
 *
 * Copyright (c) 2002-2004 Frank Neumann <franky@users.sourceforge.net>
 */

/* AKAI's extension of the SCSI opcodes (see /usr/include/scsi/scsi.h). */
/* These IDs are unallocated in that file..reserved for customer use?   */
#define S2000_MODE    0x09
#define S2000_SEND    0x0c
#define S2000_STATUS  0x0d
#define S2000_RECEIVE 0x0e

/* S1000 SysEx "command" bytes, used both for "send" and "receive" direction */
/* This is typically the fourth byte of a SysEx message */
#define LC_RSTAT   0x00  /* request status report */
#define LC_STAT    0x01  /* status report */
#define LC_RPLIST  0x02  /* request list of resident program names */
#define LC_PLIST   0x03  /* list of resident program names */
#define LC_RSLIST  0x04  /* request list of resident sample names */
#define LC_SLIST   0x05  /* list of resident sample names */
#define LC_RPDATA  0x06  /* request program common data */
#define LC_PDATA   0x07  /* program common data */
#define LC_RKDATA  0x08  /* request keygroup data */
#define LC_KDATA   0x09  /* keygroup data */
#define LC_RSDATA  0x0a  /* request sample header data */
#define LC_SDATA   0x0b  /* sample header data */
#define LC_RSPACK  0x0c  /* request sample data packet(s) */
#define LC_ASPACK  0x0d  /* accept sample data packet(s) */
#define LC_RDDATA  0x0e  /* request drum input settings */
#define LC_DDATA   0x0f  /* drum input settings */
#define LC_RMDATA  0x10  /* request miscellaneous data */
#define LC_MDATA   0x11  /* miscellaneous data */
#define LC_DELP    0x12  /* delete program and its keygroups */
#define LC_DELK    0x13  /* delete keygroup */
#define LC_DELS    0x14  /* delete sample header and data */
#define LC_SETEX   0x15  /* set Sx000 exclusive channel */
#define LC_REPLY   0x16  /* Sx000 command reply (error or ok) */
#define LC_CASPACK 0x1d  /* corrected ASPACK */

/* new operation codes for S3000 (/S2000?) */
#define LC_S3RPDATA  0x27 /* request for program header bytes */
#define LC_S3PDATA   0x28 /* program header bytes */
#define LC_S3RKDATA  0x29 /* request keygroup header bytes */
#define LC_S3KDATA   0x2a /* keygroup header bytes */
#define LC_S3RSDATA  0x2b /* request sample header bytes */
#define LC_S3SDATA   0x2c /* sample header bytes */
#define LC_S3RFDATA  0x2d /* request fx/reverb bytes */
#define LC_S3FDATA   0x2e /* fx/reverb bytes */
#define LC_S3RCLDATA 0x2f /* request Cue-list data */
#define LC_S3CLDATA  0x30 /* Cue-list data */
#define LC_S3RTLDATA 0x31 /* request for take list bytes */
#define LC_S3TLDATA  0x32 /* take list bytes */
#define LC_S3RMDATA  0x33 /* request miscellaneous bytes */
#define LC_S3MDATA   0x34 /* miscellaneous bytes */
#define LC_S3RVLI    0x35 /* request volume list item */
#define LC_S3VLI     0x36 /* volume list item (only used in response to requ) */
#define LC_S3RHDENT  0x37 /* request harddisk directory entry */
#define LC_S3HDENT   0x38 /* harddisk directory entry (only in response to req) */


/*
 * The access to all elements inside the sampler should be handled through
 * this library.
 *
 * In doing so, two different internal communication methods are used
 * (which are invisible to the user of the API, though):
 *
 * - When requesting basic, low-volume data (like program lists, sample
 *   header data etc), the protocol used is "MIDI-over-SCSI".
 *
 * - When requesting large blocks of data (typically the actual sample
 *   data), the protocol used is "raw SCSI".
 *
 * An application program will first have to use the "low-volume"
 * inquiry functions to get at all the basic information it needs
 * (what samples are in the sampler right now, where are they located,
 * how large are they), and after that can use the "high-volume"
 * functions to transfer samples to/from the sampler.
 *
 * In all of these transfers, the application has to allocate the
 * required memory structures; as the library does not know most of
 * the information until it's parsed, there would probably be too much
 * code replication because the application will have to parse the data
 * anyway (and right now I can't think of good ways to put this into
 * utility functions in the library).
 *
 */


/* An LHANDLE is your key to accessing a LAKAI device */
typedef int LHANDLE;

/* TODO: Decide if the data that gets passed back to the user program is the
 * original raw data or if it has been pre-processed for easier use.
 * I think the latter should not be too complicated. It's mostly stripping off
 * the SysEx header/EOX and filling in the data fields of the respective
 * structures.
 */

/* a lakai_status structure contains such information as OS version,
 * number of programs&samples in memory, free memory etc
 */
typedef struct
{
      int osversion;                /* OS version of currently booted Akai OS */
      int nummaxblocks;       /* maximum number of sample/program/keygroup blocks */
      int numfreeblocks;      /* number of currently free blocks */
      int nummaxsamples;      /* maximum number of sample words */
      int numfreesamples;     /* number of currently available sample words */
      int eoxchannel;         /* current MIDI channel for exclusive data */
} LakaiStatus;

/* A LakaiProgramList holds a list of currently resident program names in
 * the sampler.
 */
typedef struct
{
      int numprogs;
      char **prognames;
} LakaiProgramList;

/* A LakaiSampleList holds a list of currently resident sample names in
 * the sampler.
 */
typedef struct
{
      int numsamples;
      char **samplenames;
} LakaiSampleList;


/* A LakaiProgram structure holds all information that make up one
 * program - tuning, filter settings, keygroups etc.
 */
typedef struct
{
      /* TODO: Incomplete.. */
      unsigned char prident;        /* 1=Program header block identifier */
      /* a pad byte is inserted here by the compiler */
      unsigned short kgrp1;         /* 1st keygroup block address (internal use) */
      unsigned char prname[12];     /* Name */
      unsigned char prgnum;         /* MIDI program number (0-127) */
      unsigned char pmchan;         /* MIDI channel (0-15, FFh=OMNI) */
      unsigned char polyph;         /* Polyphony (1-16) */
      unsigned char priort;         /* Priority (0=low 1=normal 2=high 3=hold) */
      unsigned char playlo;         /* Play-range low (24-127 = C0-G8) */
      unsigned char playhi;         /* Play-range high (24-127 = C0-G8) */
      unsigned char oshift;         /* Play octave (keyboard) shift(+/-2) */
      unsigned char output;         /* Output number (0-7,FFh=off) */
      unsigned char stereo;         /* Left and right level (0-99) */
      unsigned char panpos;         /* Left/right balance (+/-50) */
      unsigned char prloud;         /* Basic loudness (0-99) */
      unsigned char v_loud;         /* Velocity&Loudness (+/-50) */
      unsigned char k_loud;         /* Key&Loudness (+/-50) */
      unsigned char p_loud;         /* Pressure&Loudness (+/-50) */
      unsigned char panrat;         /* Pan LFO rate (0-99) */
      unsigned char pandep;         /* Pan depth (0-99) */
      unsigned char pandel;         /* Pan LFO delay (0-99) */
      unsigned char k_panp;         /* Key&Pan position (+/-50) */
      unsigned char lforat;         /* LFO speed (0-99) */
      unsigned char lfodep;         /* LFO fixed depth (0-99) */
      unsigned char lfodel;         /* LFO delay (0-99) */
      unsigned char mwldep;         /* Modwheel&LFO depth (0-99) */
      unsigned char prsdep;         /* Pressure&LFO depth (0-99) */
      unsigned char veldep;         /* Velocity&gt;LFO depth (0-99) */
      unsigned char b_ptch;         /* Bendwheel&Pitch (0-12 semitones) */
      unsigned char p_ptch;         /* Pressure&Pitch (+/-12 semitones) */
      unsigned char kxfade;         /* Keygroup crossfade (0=off 1=on) */
      unsigned char groups;         /* number of keygroups (1-99) */
      unsigned char tpnum;                /* temporary program number (internal use) */
      unsigned char temper[12];     /* Key temperament (+/25 cents) C,C#,D,D# etc */
      unsigned char echout;         /* Echo output level (0=off 1=on) */
      unsigned char mw_pan;         /* Modwheel pan amount (+/-50) */
      unsigned char cohere;         /* Sample start coherence (0=off 1=on) */
      unsigned char desync;         /* LFO De-Sync (0=off 1=on) */
      unsigned char plaw;                 /* Pitch Law (0=linear) */
      unsigned char vassoq;         /* Voice assign algorithm (0=oldest 1=quietest) */
      unsigned char sploud;         /* Soft pedal loudness reduction (0-99) */
      unsigned char spatt;                /* Soft pedal attack stretch (0-99) */
      unsigned char spfilt;         /* Soft pedal filter close (0-99) */
      unsigned short ptuno;         /* Tune offset cent:semi (+/-50.00 fraction is binary) */
      unsigned char k_lrat;         /* Key&LFO rate (+/-50) */
      unsigned char k_ldep;         /* Key&LFO depth (+/-50) */
      unsigned char k_ldel;         /* Key&LFO delay (+/-50) */
      unsigned char voscl;                /* Voice output scale (0=-6dB, 1=0dB, 2=+12dB) */
      unsigned char vsscl;                /* Stereo output scale (0=0dB, 1=+6dB) */
      unsigned char legato;         /* Mono legato mode enable (0=Off, 1=On) */
      unsigned char b_ptchd;        /* Range of decrease of Pitch by bendwheel (0..12 semitones) */
      unsigned char b_mode;         /* Bending of held notes (0=normal mode, 1=held) */
      unsigned char transpose;      /* Shift pitch of incoming MIDI (-50..+50 semitones) */
      /* Values used to represent Modulation Sources for the following mod's:
       *
       * 0: No Source
       * 1: Modwheel
       * 2: Bend
       * 3: Pressure
       * 4: External
       * 5: Note-on velocity
       * 6: Key
       * 7: LFO1
       * 8: LFO2
       * 9: Env1
       * 10: Env2
       * 11: !Modwheel (Instantaneous value of modwheel at note-on)
       * 12: !Bend (Instantaneous value of bendwheel at note-on)
       * 13: !External (Instantaneous value of MIDI controller at note-on)
       * 14: Env3
       */ 
      unsigned char modspan1;       /* First source of assignable modulation of pan position */
      unsigned char modspan2;       /* Second source of assignable modulation of pan */
      unsigned char modspan3;       /* Third source of assignable modulation of pan */
      unsigned char modsamp1;       /* First source of assignable modulation of loudness */
      unsigned char modsamp2;       /* Second source of assignable modulation of loudness */
      unsigned char modslfot;       /* Source of assignable modulation of LFO1 speed */
      unsigned char modslfol;       /* Source of assignable modulation of LFO1 depth */
      unsigned char modslfod;       /* Source of assignable modulation of LFO1 delay */
      unsigned char modsfilt1;      /* First source of assignable modulation of filter frequency */
      unsigned char modsfilt2;      /* Second source of assignable modulation of filter frequency */
      unsigned char modsfilt3;      /* Third source of assignable modulation of filter frequency */
      unsigned char modspitch;      /* Source of assignable modulation of pitch */
      unsigned char modsamp3;       /* Third source of assignable modulation of loudness */
      unsigned char modvpan1;       /* Amount of control of pan by assignable source 1 (-50..50) */
      unsigned char modvpan2;       /* Amount of control of pan by assignable source 2 (-50..50) */
      unsigned char modvpan3;       /* Amount of control of pan by assignable source 3 (-50..50) */
      unsigned char modvamp1;       /* Amount of control of loudness by assignable source 1 (-50..50) */
      unsigned char modvamp2;       /* Amount of control of loudness by assignable source 2 (-50..50) */
      unsigned char modvlfor;       /* Amount of control of LFO1 speed (-50..50) */
      unsigned char modvlfol;       /* Amount of control of LFO1 depth (-50..50) */
      unsigned char modvlfod;       /* Amount of control of LFO1 delay (-50..50) */
      unsigned char lfo1wave;       /* LFO1 waveform (0=Triangle, 1=Sawtooth, 2=Square) */
      unsigned char lfo2wave;       /* LFO2 waveform (0=Triangle, 1=Sawtooth, 2=Square) */
      unsigned char modslflt2_1;    /* First source of assignable modulation of filter 2 frequency (only used on S3200) */
      unsigned char modslflt2_2;    /* Second source of assignable modulation of filter 2 frequency (only used on S3200) */
      unsigned char modslflt2_3;    /* Third source of assignable modulation of filter 2 frequency (only used on S3200) */
      unsigned char lfo2trig;       /* Retrigger mode for LFO2 */
      unsigned char _reserved1[7];  /* .. */
      unsigned char portime;        /* Portamento Time */
      unsigned char portype;        /* Portamento Type */
      unsigned char porten;         /* Portamento On/Off */
      unsigned char pfxchan;        /* Effects Bus Select (0=Off, 1=FX1, 2=FX2, 3=RV3, 4=RV4) */
      unsigned char pfxslev;        /* Not used */
} LakaiProgram;


/* A LakaiKeygroup structure holds the information for one keygroup:
 * lower/upper end of range etc.
 */
typedef struct
{
      unsigned char kgident;        /* 2=Keygroup block identifier */
      unsigned short nxtkg;         /* Next keygroup block address (internal use) */
      unsigned char lonote;         /* Keyrange low (24-127 = C0-G8) */
      unsigned char hinote;         /* Keyrange high (24-127 = C0-G8) */
      unsigned short kgtuno;        /* Tune offset cent:semi (+/-50.00 fraction is binary */
      unsigned char filfrq;         /* Basic filter frequency (0-99) */
      unsigned char k_freq;         /* Key&Filter freq (+/-24 semitones/octave) */
      unsigned char v_freq;         /*    Velocity&Filter freq (+/-50) */
      unsigned char p_freq;         /* Pressure&Filter freq (+/-50) */
      unsigned char e_freq;         /* Envelope&Filter freq (+/-50) */
      unsigned char attak1;         /* Amplitude attack (0-99) */
      unsigned char decay1;         /* Amplitude decay (0-99) */
      unsigned char sustn1;         /* Amplitude sustain level (0-99) */
      unsigned char relse1;         /* Amplitude release (0-99) */
      unsigned char v_att1;         /* Velocity&Amp attack (+/-50) */
      unsigned char v_rel1;         /* Velocity&Amp release (+/-50) */
      unsigned char o_rel1;         /* Off Vel.&Amp release (+/-50) */
      unsigned char k_dar1;         /* Key&Decay&Release (+/-50) */
      unsigned char attak2;         /* Filter attack (0-99) */
      unsigned char decay2;         /* Filter decay (0-99) */
      unsigned char sustn2;         /* Filter sustain level (0-99) */
      unsigned char relse2;         /* Filter release (0-99) */
      unsigned char v_att2;         /* Velocity&Filter attack (+/-50) */
      unsigned char v_rel2;         /* Velocity&Filter release (+/-50) */
      unsigned char o_rel2;         /* Off Vel.&Filter relase (+/-50 */
      unsigned char k_dar2;         /* Key&Decay&Release (+/-50) */
      unsigned char v_env2;         /* Velocity&Filter envelope output (+/-50) */
      unsigned char e_ptch;         /* Envelope&Pitch (+/-50) */
      unsigned char vxfade;         /* Velocity zone crossfade (0=off 1=on) */
      unsigned char vzones;         /* Number of velocity zones in use (not used) */
      unsigned char lkxf;                 /* Calculated left key crossfade factor (internal) */
      unsigned char rkxf;                 /* Calculated right key crossfade factor (internal) */
      
      /* Velocity zone 1 */
      unsigned char sname1[12];     /* Sample name */
      unsigned char lovel1;         /* Velocity range low (0-127) */
      unsigned char hivel1;         /* Velocity range high (0-127) */
      unsigned short vtuno1;        /* Tune offset (+/-50.00 fraction is in binary form) */
      unsigned char vloud1;         /* Loudness offset (+/-50) */
      unsigned char vfreq1;         /* Filter frequency offset (+/-50) */
      unsigned char vpano1;         /* Pan offset (+/-50) */
      unsigned char zplay1;         /* Loop in release (0=as sample, 1-4 see below) */
      unsigned char lvxf1;                /* Low velocity crossfade factor (internal use) */
      unsigned char hvxf1;                /* High velocity crossfade factor (internal use) */
      unsigned short sbadd1;        /* Calculated sample header block address (internal) */

      /* Velocity zone 2 */
      unsigned char sname2[12];     /* See velocity zone 1 */
      unsigned char lovel2;
      unsigned char hivel2;
      unsigned short vtuno2;
      unsigned char vloud2;
      unsigned char vfreq2;
      unsigned char vpano2;
      unsigned char zplay2;
      unsigned char lvxf2;
      unsigned char hvxf2;
      unsigned short sbadd2;

      /* Velocity zone 3 */
      unsigned char sname3[12];     /* See velocity zone 1 */
      unsigned char lovel3;
      unsigned char hivel3;
      unsigned short vtuno3;
      unsigned char vloud3;
      unsigned char vfreq3;
      unsigned char vpano3;
      unsigned char zplay3;
      unsigned char lvxf3;
      unsigned char hvxf3;
      unsigned short sbadd3;
      
      /* Velocity zone 4 */
      unsigned char sname4[12];     /* See velocity zone 1 */
      unsigned char lovel4;
      unsigned char hivel4;
      unsigned short vtuno4;
      unsigned char vloud4;
      unsigned char vfreq4;
      unsigned char vpano4;
      unsigned char zplay4;
      unsigned char lvxf4;
      unsigned char hvxf4;
      unsigned short sbadd4;
      
      unsigned char kbeat;                /* Fixed rate detune (byte) */
      unsigned char ahold;                /* Attack hold until loop */
      unsigned char cp1;                  /* Constant pitch for each velocity zone (0=track 1=const) */
      unsigned char cp2;
      unsigned char cp3;
      unsigned char cp4;
      unsigned char vzout1;         /* Output number offset for each velocity zone (0-7) */
      unsigned char vzout2;
      unsigned char vzout3;
      unsigned char vzout4;
      unsigned short vss1;                /* Velocity&Sample start (+/-9999) */
      unsigned short vss2;
      unsigned short vss3;
      unsigned short vss4;
      unsigned char kv_lo;                /* Velocity&Loudness offset (+/-50) */
      /* ZPLAY:- type of sample playback, values:
       * 0 = as defined by sample header
       * 1 = normal looping
       * 2 = loop until release
       * 3 = no looping
       * 4 = play to sample end
       */

      /* TODO: Need to proof-read this! */
      unsigned char filq;
      unsigned char l_ptch;
      unsigned char modvfilt1;
      unsigned char modvfilt2;
      unsigned char modvfilt3;
      unsigned char modvpitch;
      unsigned char modvamp3;
      unsigned char env2l1;
      unsigned char env2r2;
      unsigned char env2l2;
      unsigned char env2l4;
      unsigned char kgmute;
      unsigned char pfxchan;
      unsigned char pfxslev;
      unsigned char res1[5];
      unsigned char lsi2_on;
      unsigned char flt2gain;
      unsigned char flt2mode;
      unsigned char flt2q;
      unsigned char tonefreq;
      unsigned char toneslop;
      unsigned char modvflt2_1;
      unsigned char modvflt2_2;
      unsigned char modvflt2_3;
      unsigned char fil2fr;
      unsigned char k_frq2;
      unsigned char env3r1;
      unsigned char env3l1;
      unsigned char env3r2;
      unsigned char env3l2;
      unsigned char env3r3;
      unsigned char env3l3;
      unsigned char env3r4;
      unsigned char env3l4;
      unsigned char v_att3;
      unsigned char v_rel3;
      unsigned char o_rel3;
      unsigned char k_dar3;
      unsigned char v_env3;

} LakaiKeygroup;


/* A LakaiSampleHeader structure contains information about the associated
 * sample, such as length, loop points
 */
typedef struct
{
      unsigned char shident;              /* 3=sample header block identifier */
      unsigned char sbandw;               /* Bandwidth (0=10kHz 1=20kHz) */
      unsigned char spitch;               /* Original pitch (24-127 = C0-G8) */
      unsigned char shname[12];           /* Name (same position as program) */
      unsigned char ssrvld;               /* Sample rate ssrate valid (80H=yes) */
      unsigned char sloops;               /* Number of loops (internal use) */
      unsigned char saloop;               /* First active loop (internal use) */
      unsigned char padbyte;              /* Spare byte */
      unsigned char sptype;               /* Playback type (see below) */
      unsigned short stuno;               /* Tune offset cent:semi (+/-50.00) */
      unsigned long slocat;               /* Data absolute start address */
      unsigned long slngth;               /* Data length (number of samples) */
      unsigned long sstart;         /* Play relative start address */
      unsigned long smpend;         /* Play relative end address */
      unsigned long loopat1;        /* Relative loop point (bits 0-5 are treated as 1) */
      unsigned long llngth1;        /* Loop length (binary) fraction:INT.LOW:INT.HIGH */
      unsigned short ldwell1;             /* Dwell time (0=no loop 1-9998=mSec 9999=hold) */

      /* TODO: All short XXX[2] should be long XXX instead */
      unsigned short loopat2[2];          /* See loop 1 */
      unsigned short llngth2[3];
      unsigned short ldwell2;

      unsigned short loopat3[2];          /* See loop 1 */
      unsigned short llngth3[3];
      unsigned short ldwell3;

      unsigned short loopat4[2];          /* See loop 1 */
      unsigned short llngth4[3];
      unsigned short ldwell4;

      unsigned short loopat5[2];          /* See loop 1 */
      unsigned short llngth5[3];
      unsigned short ldwell5;

      unsigned short loopat6[2];          /* See loop 1 */
      unsigned short llngth6[3];
      unsigned short ldwell6;

      unsigned short loopat7[2];          /* See loop 1 */
      unsigned short llngth7[3];
      unsigned short ldwell7;

      unsigned short loopat8[2];          /* See loop 1 */
      unsigned short llngth8[3];
      unsigned short ldwell8;
      
      unsigned char sspare[2];            /* Spare bytes used internally */
      unsigned short sspair;              /* Address of stereo partner (internal use) */
      unsigned short ssrate;              /* Sample rate in Hz */
      unsigned char shlto;                      /* Hold loop tune offset (+/-50 cents) */
      /* Type of playback values:-
       * 0 = normal looping
       * 1 = Loop until release
       * 2 = No looping
       * 3 = Play to sample end
       */
} LakaiSampleHeader;

/* TODO: Add here: Drum trigger unit block */
typedef struct
{
      unsigned char d1oper;
      unsigned char d1exch;
      unsigned char d1thru;
      unsigned char drname[12];

      unsigned char dchan1;
      unsigned char dnote1;
      unsigned char dsens1;
      unsigned char dtrig1;
      unsigned char dvcrv1;
      unsigned char dcatp1;
      unsigned char drcvr1;
      unsigned short dontm1;
      
      unsigned char dchan2;
      unsigned char dnote2;
      unsigned char dsens2;
      unsigned char dtrig2;
      unsigned char dvcrv2;
      unsigned char dcatp2;
      unsigned char drcvr2;
      unsigned short dontm2;
      
      unsigned char dchan3;
      unsigned char dnote3;
      unsigned char dsens3;
      unsigned char dtrig3;
      unsigned char dvcrv3;
      unsigned char dcatp3;
      unsigned char drcvr3;
      unsigned short dontm3;
      
      unsigned char dchan4;
      unsigned char dnote4;
      unsigned char dsens4;
      unsigned char dtrig4;
      unsigned char dvcrv4;
      unsigned char dcatp4;
      unsigned char drcvr4;
      unsigned short dontm4;
      
      unsigned char dchan5;
      unsigned char dnote5;
      unsigned char dsens5;
      unsigned char dtrig5;
      unsigned char dvcrv5;
      unsigned char dcatp5;
      unsigned char drcvr5;
      unsigned short dontm5;
      
      unsigned char dchan6;
      unsigned char dnote6;
      unsigned char dsens6;
      unsigned char dtrig6;
      unsigned char dvcrv6;
      unsigned char dcatp6;
      unsigned char drcvr6;
      unsigned short dontm6;
      
      unsigned char dchan7;
      unsigned char dnote7;
      unsigned char dsens7;
      unsigned char dtrig7;
      unsigned char dvcrv7;
      unsigned char dcatp7;
      unsigned char drcvr7;
      unsigned short dontm7;
      
      unsigned char dchan8;
      unsigned char dnote8;
      unsigned char dsens8;
      unsigned char dtrig8;
      unsigned char dvcrv8;
      unsigned char dcatp8;
      unsigned char drcvr8;
      unsigned short dontm8;
      
      /* unit 2 to follow here */
} LakaiDrumTrigger;

/* A LakaiMiscData structure contains some miscellaneous settings like
 * MIDI channel, OMNI setting etc.
 */
typedef struct
{
      unsigned char bmchan;         /* Basic MIDI channel (0-15) for MIDI program select */
      unsigned char bmomni;         /* Basic channel Omni (0=off 1=on) */
      unsigned char pselen;         /* MIDI program select enable (0=off 1=on) */
      unsigned char selpnm;         /*    Selected program number (0-127) */
      unsigned char omnovr;         /* MIDI play commands Omni override (0=off 1=on) */
      unsigned char exchan;         /* MIDI exclusive channel (0-127) */
} LakaiMiscData;

/* TODO: still missing here: Multi & effect structure */

/*
 * lakai_init()
 *
 * Performs some library initializations. To be called by each application
 * before it can start using the other liblakai functions.
 *
 * INPUTS:
 *  -
 * OUTPUTS:
 * -
 * RETURNS:
 * - (should always suceed)
 */

void lakai_init( void );

/*
 * open the given SCSI generic file (typically something like "/dev/sgx") for
 * I/O. This requires the "sg" driver to be available either as a module or
 * built into the kernel.
 *
 * INPUTS:
 *    char *devname: pointer to name of the device special file to be opened
 *
 * OUTPUTS:
 *    -
 * RETURNS:
 *    LHANDLE: handle to be used in further calls to lakai functions
 *    If lakai_open fails, returns -1.
 *
 */
LHANDLE lakai_open( char *devname );


/*
 * close the device special file that was previously opened with lakai_open().
 * No further actions are performed - the sampler device is NOT implictly put
 * back into NON-MIDI-OVER-SCSI mode, for instance. The user app has to do this
 * by itself first.
 * After a call to lakai_close() the LHANDLE given back earlier should not be
 * used anymore.
 *
 * INPUTS:
 *    LHANDLE handle: handle to a previously opened device special file
 *
 * OUTPUTS:
 *    -
 * 
 * RETURNS:
 *    0 if lakai device was successfully closed, -1 on error.
 *
 */
int lakai_close( LHANDLE handle );


#define LAKAI_MODE_NORMAL           0x00
#define LAKAI_MODE_SCSI_MIDI  0x01
#define LAKAI_MODE_SCSI_BULK  0x02

/*
 * sets the working mode of the sampler to one of these:
 * "standard" (MIDI over serial), MIDI over SCSI, or SCSI bulk
 * 
 * "standard" MIDI means release the lock of the sampler.
 * This should always be called when an application has finished
 * exchanging data with the sampler (especially when the app quits).
 * 
 * "MIDI over SCSI" is used for getting the basic low-volume data
 * like status report, resident program/sample list, sample headers etc.
 * 
 * "SCSI bulk" is the mode used to transfer large chunk of data
 * (typically sample data)
 *
 * INPUTS: 
 *    handle: LHANDLE to opened LAKAI device
 *    mode: desired sampler mode, see LAKAI_MODE_* definitions above
 *
 *  RETURNS: 
 *    0 if mode change was successful, -1 on error
 *  
 *  NOTE: 
 *    The user could possibly change the setting himself through the panel
 *    of the sampler (setting "MIDI via SCSI on/off" in the Global section).
 *    However, he had better not interfere with the app here, only in cases
 *    where the app is obviously "stuck" and cannot reset the state.
 */
int lakai_setmode(LHANDLE handle, int mode);

/*
 * Convert a string of characters from ASCII to Akai representation
 * Non-convertible characters in src get mapped to Space (' ') char in dest.
 *
 * INPUTS:
 *    char *src: pointer to array that holds the original string
 *    char *dest: pointer to array that holds the converted string
 *    int len: # of characters to be converted
 *
 *    The user has to make sure "dest" can hold "len" bytes.
 * 
 * OUTPUTS:
 *    Converted string is in "dest" buffer.
 *
 * RETURNS:
 *    -
 */
void lakai_asciitoakai(char *src, char *dest, int len);


/*
 * Convert a string of characters from Akai representation to ASCII 
 * Non-convertible characters in src get mapped to Space (' ') char in dest.
 *
 * INPUTS:
 *    char *src: pointer to array that holds the original string
 *    char *dest: pointer to array that holds the converted string
 *    int len: # of characters to be converted
 *
 *    The user has to make sure "dest" can hold "len" bytes.
 * 
 * OUTPUTS:
 *    Converted string is in "dest".
 *    
 * RETURNS:
 *    -
 */
void lakai_akaitoascii(char *src, char *dst, int len);


#if 0

/* New thoughts, Apr 22, 2001 (sigh..):
 * I think I can get all of the "numbers" from the status report (# of program, samples, ...)
 * So, the application has to get this information first anyway, and if it then requests
 * the actual program data, it can also allocate the required space for that data itself.
 * Consequently, it will also have to free that memory by itself etc.
 * This frees the lib from doing that. Sounds good. Stick with that.
 *
 * Another advantage to that solution is that there is far less complexity in the lib
 * if it has to do memory-allocation housekeeping: Let's say the lib does the allocation
 * of data structures, it basically tells the client app "here is the data you asked for;
 * I allocated the memory for you to use it; now go and use it, and when you're finished,
 * please tell me so I can free the memory then". The client app can keep the data allocated
 * as long as it wants, requiring some kind of resource management in the lib. But I believe
 * this is the wrong approach; clasically, the app malloc()s the memory and tells the lib
 * where the buffer is so that the lib (/kernel) can fill that buffer with data. If the
 * buffer is too small, that's the client's fault, so "you wanted to shoot yourself in the
 * foot, please go ahead" from the point of view of the lib. Basta.
 *
 * The lib will get the raw (+sysex header etc) data from the device, but hands that
 * data over to the app in the "pre-chewed" form, that is, after stripping off SysEx stuff.
 *
 * When going the other direction, the app wanting to send program data etc to the sampler,
 * it only has to prepare the data structure, and the lib will take care of providing it
 * with SysEx headers, fragmentation etc. and send the packets to the sampler.
 *
 * 
 */

#define LAKAI_DEVTYPE_S1000   0x01
#define LAKAI_DEVTYPE_S2000   0x02
/* ... more devtypes here when known.. */

/* 
 * identify a given device as Akai S1000 (or not)
 * TODO: Is this really needed? 
 * This could be integrated into the lakai_open() call..
 *
 * INPUTS:
 *    LHANDLE to opened LAKAI device
 * 
 * RETURNS:
 *    the type of the connected device, see LAKAI_DEVTYPE_ definitions above
 */
int lakai_identifydevice(LHANDLE handle);


/* 
 * test if the connected unit is ready to receive commands or send
 * data to the computer.
 * TODO: Do we really need this?
 *
 * INPUTS:
 *    LHANDLE to opened LAKAI device
 *
 * RETURNS:
 *    FALSE (0) or not ready, TRUE else.
 */
int lakai_testunitready(LHANDLE handle);


/*
 * gets the number of bytes available to be transferred back
 * to the host
 *
 * INPUTS:
 *    handle: LHANDLE to opened LAKAI device
 *
 * RETURNS:
 *    numbytes: number of waiting bytes
 */

/* #### TODO: Do I really need this? */
long lakai_getavailable(LHANDLE handle);

#endif

/*
 * reads a sample of 'len' bytes of data from the sampler into
 * the supplied buffer. The user has to take care that the buffer
 * is sufficiently large. Also, the user has to find out in advance
 * how large the expected sample is going ot be (in bytes) through
 * the lakai_get_sample_header() function.
 *
 * INPUTS:
 *    handle: LHANDLE to opened LAKAI device
 *    buffer: buffer to store transferred data into
 *    len: number of bytes to be transferred
 *
 * RETURNS:
 *    number of bytes that have actually been transferred,
 *    0 if an error occurred.
 */
long lakai_get_sample(LHANDLE handle, int samplenum, char *buffer, long locat, long len);


#if 0

/*
 * writes a block of 'len' bytes of data to the sampler from
 * the supplied buffer. The user has to take care that the buffer
 * is sufficiently large and that not too much data gets sent
 * (does the Sx000 have memory protection? Probably not..)
 *
 * INPUTS:
 *    handle: LHANDLE to opened LAKAI device
 *    buffer: buffer to read data to be transferred from
 *    len: number of bytes to be transferred
 *
 * RETURNS:
 *    number of bytes that have actually been transferred,
 *    0 if an error occurred.
 */
long lakai_putdatablock(LHANDLE handle, char *buffer, long len);


/*
 * (internal?) low-level functions:
 *
 * (RequestErrorSense?) - check if the last operation yielded an error..
 * (ScanForSampler?) - check /dev/sg0../dev/sg15 for an Akai S1000
 *
 * IdentifyDevice(): check model number of SCSI device, ...
 *
 * TestUnitReady(): see if the unit is able to answer requests
 *
 * SetMidiMode(..): sets to either "normal" (MIDI over serial cable), 
 * SCSI_MIDI (MIDI over SCSI) or SCSI_BULK (bulk data transfer over SCSI).
 *
 * GetNumAvailable(): get the number of bytes waiting to be received from sampler
 * 
 * GetDataBlock(): read a block of data from sampler using SCSI_BULK mode
 *
 * PutDataBlock(): write a block of data to the sampler using SCSI_BULK mode
 *
 * ### for GetDataBlock() and PutDataBlock(): In case of large transfers, might
 * make sense to give the user feedback (progress indicator). This requires a callback
 * mechanism, or asynchronous operation (thread?).. todo, later..
 * 
 * I will not support bulk transfers in non-bulk mode, that's just too plain slow
 * (even using MIDI over SCSI, it comes in 120-byte packages, producing far too 
 * much overhead - I got around 10 - 20 kBytes/sec this way..)
 * 
 * Design question: How much work do I want to put into the lib/the client?
 * That is, who should have to process resident sample/program lists, the lib
 * or the client?
 * 1) If it's the lib, the client program can be kept very short&simple; but it
 * might not be flexible enough this way.
 * 2) If it's in the client, it has got more work to do, the lib becomes smaller,
 * and the client is more flexible.
 *
 * Right now I think 2) is better suited. That would mean the lib does not do
 * much more but:
 * - Init functions, TestUnitReady
 * - bulk send/receive
 * - SCSI_MIDI receiving of resident program/sample/etc lists without parsing stuff
 *
 * The client just requests this data and has to do things like name translation
 * itself..really? Doesn't seem to make sense..
 * 
 *
 *
 *
 * HIGH-LEVEL FUNCTIONS: (directly related to the corresponding MIDI SysEx messages)
 * R: data that is only read from sampler
 * RW: data that can be both read and written)
 *
 * All of these functions should get their data in MIDI-over-SCSI mode, so they need
 * to make sure the sampler is in that mode before talking to it.
 */

#endif

/*
 * get the current status from the S1000; includes information such as current
 * number of used programs, samples...
 *
 * INPUTS:
 *    LHANDLE handle: handle to previously opened device special file
 *    LAKAI_STATUS: pointer to LAKAI_STATUS structure to fill in from sampler
 *
 * RETURNS:
 */
int lakai_get_status_report(LHANDLE handle, LakaiStatus *ls);


/*
 * Retrieves the current name list of resident program.
 *
 * INPUTS:
 *    LHANDLE handle: handle to previously opened Lakai device
 *    LakaiProgramList *lp: pointer to struct which will be filled in with
 *    program names data
 *
 * OUTPUTS:
 *    lp->prognames[] is filled with the resident program name list
 *    
 * RETURNS:
 *    int: number of program names that have been received from sampler
 */
int lakai_get_program_list(LHANDLE handle, LakaiProgramList *lp);


/*
 * free a program list that was previously allocated & retrieved through
 * lakai_get_programlist().
 *
 * INPUTS:
 *    LakaiProgramList *lp: pointer to LakaiProgramList struct
 *
 * OUTPUTS:
 *    All program name memory allocated in the LakaiProgramList struct will
 *    be freed.
 *
 * RETURNS:
 *    -
 */
void lakai_free_program_list(LakaiProgramList *lp);
      

/*
 * Retrieves the current name list of resident samples.
 *
 * INPUTS:
 *    LHANDLE handle: handle to previously opened Lakai device
 *    LakaiSampleList *ls: pointer to struct which will be filled in with
 *    sample names data
 *
 * OUTPUTS:
 *    lp->sampnames[] is filled with the resident sample name list
 *    
 * RETURNS:
 *    int: number of sample names that have been received from sampler
 */
int lakai_get_sample_list(LHANDLE handle, LakaiSampleList *ls);
      

/*
 * free a sample name list that was previously allocated & retrieved through
 * lakai_get_samplelist().
 *
 * INPUTS:
 *    LakaiSampleList *lp: pointer to LakaiSampleList struct
 *
 * OUTPUTS:
 *    All sample name memory allocated in the LakaiSampleList struct will
 *    be freed.
 *
 * RETURNS:
 *    -
 */

void lakai_free_sample_list(LakaiSampleList *ls);



/*
 * gets the program common data for one program. This is a fixed data
 * structure (might differ between old and more recent sampler models - S1000
 * seems to have a length of 150 bytes, while S2000/2800 etc have 192 bytes here.
 * However, if I send an S1000 "RPDATA" command, I should only look at the first 150
 * bytes.
 * If the requested program does not exist, returns an error.
 *
 * INPUTS:
 *    LHANDLE handle: handle to previously opened Lakai device
 *    int prognum: number of program whose common data to retrieve
 *
 * OUTPUTS:
 *    stores the program common data for this program into lpr
 *
 * RETURNS:
 *    -1 if an error occurred (program does not exist etc),
 *    number of bytes transferred otherwise (150/192)
 */
int lakai_get_program(LHANDLE handle, int prognum, unsigned char *data);



/*
 * gets the keygroup data for a keygroup in a program. This is a fixed data structure
 * (might differ between old and more recent sampler models?).
 * If the keygroup does not exist, returns an error.
 *
 * INPUTS:
 *    LHANDLE handle: handle to previously opened device special file
 *    int prognum: number of program in whose keygroups we're interested
 *    int keygroupnum: number of keygroup whose data to retrieve
 *
 * OUTPUTS:
 *    stores the keygroup data for this keygroup into lk
 *
 * RETURNS:
 *    -1 if an error occurred (keygroup does not exist etc)
 *    number of bytes transferred otherwise
 */
int lakai_get_keygroup(LHANDLE handle, int prognum, int keygroupnum, unsigned char *data);


/*
 * gets the sample header data for one sample header. This is a fixed
 * data structure (might differ between old and more recent sampler models?).
 * If the sample header does not exist, returns an error.
 *
 * INPUTS:
 *    LHANDLE handle: handle to previously opened device special file
 *    int headernum: number of sample header whose common data to retrieve
 *
 * OUTPUTS:
 *    stores the sample header data for this sampler header into lsh
 *
 * RETURNS:
 *    -1 if an error occurred (sample header does not exist etc)
 *    number of bytes transferred otherwise
 */
int lakai_get_sample_header(LHANDLE handle, int samplenum, unsigned char *data);


/*
 * sends a sample header structure of 'len' bytes from the supplied buffer
 * to the sampler. The user has to take care that the buffer
 * is prefilled with data. 
 *
 * INPUTS:
 *    handle: LHANDLE to opened LAKAI device
 *    samplenum: Number of the sample to overwrite/create. If the number is
 *    higher than the highest number of a resident sample header, a new
 *    sample entry is being created.
 *    len: Length of sample header data that follows
 *    buffer: buffer that holds the sample header data to be transferred
 *
 * RETURNS:
 *    number of bytes that have actually been transferred,
 *    0 if an error occurred.
 */
int lakai_put_sample_header(LHANDLE handle, int samplenum, unsigned char *data, int len);


/*
 * Deletes the program "prognum". If program does not exist, returns an error.
 * Otherwise, that program and all its keygroups will be deleted.
 * Of course this does not delete any sample header or sample data.
 *
 * INPUTS:
 *    LHANDLE handle: handle to previously opened device special file
 *    int prognum: number of program to be deleted
 *
 * OUTPUTS:
 *    -
 *
 * RETURNS:
 *    -1 if an error occurred (program does not exist etc)
 *    0 (or program number?) otherwise
 */
int lakai_delete_program(LHANDLE handle, int prognum);


/*
 * Deletes the keygroup "kgrpnum" in the program "prognum". If the keygroup 
 * in this program (or that program itself) does not exist, returns an error.
 * Otherwise, the keygroup will be deleted.
 * Of course this does not delete any sample header or sample data.
 *
 * INPUTS:
 *    LHANDLE handle: handle to previously opened device special file
 *    int prognum: number of program 
 *    int kgrpnum: Number of keygroup to be deleted
 *
 * OUTPUTS:
 *    -
 *
 * RETURNS:
 *    -1 if an error occurred (program does not exist etc)
 *    0 (?) otherwise
 */
int lakai_delete_keygroup(LHANDLE handle, int prognum, int kgrpnum);


/*
 * Deletes the sample "samplenum". If that sample does not exist, 
 * returns an error.  Otherwise, the sample and its sample header data
 * will be deleted.
 * Programs using this sample will be unaffected, but this may create
 * unpleasant situations (to be checked..)
 *
 * INPUTS:
 *    LHANDLE handle: handle to previously opened device special file
 *    int samplenum: number of sample to delete
 *
 * OUTPUTS:
 *    -
 *
 * RETURNS:
 *    -1 if an error occurred (sample does not exist etc)
 *    0 (?) otherwise
 */
int lakai_delete_sample(LHANDLE handle, int samplenum);

/*
 * Retrieves the "miscellaneous" data from the sampler. Contents of this
 * data field seems to depend heavily on the sampler model used.
 *
 * INPUTS:
 *    LHANDLE handle: handle to previously opened device special file
 *    data: pointer to a unsigned char buffer that can hold at least TODO xxx
 *    bytes.
 *
 * OUTPUTS:
 *    The buffer is filled with the misc data
 *
 * RETURNS:
 *    int res: Number of bytes transferred by the call
 *
 */
int lakai_get_miscdata(LHANDLE handle, unsigned char *data);

/*
 * Replaces an existing program or creates a new program in the sampler.
 * From the SysEx docs: "If the program number is above the highest existing 
 * program number, a new program will be created (if sufficient blocks are 
 * free - one for the program common block and one for each keygroup as 
 * specified by the parameter GROUPS). The created program will have dummy 
 * keygroups with unspecified data; the appropriate number of keygroup data
 * messages should be given immediately. If the program name in data is
 * the same as that of any existing program, that program will be deleted
 * first. If the program number is of an existing program, the existing
 * data will be replaced but the parameter GROUPS must be correct. This 
 * allows complete freedem to change parameters - the use of a duplicate
 * program name should be avoided. If either error situation occurs, an error
 * message will be given, otherwise an OK message will be given".
 *
 * gets the program common data for one program. This is a fixed data
 * structure (might differ between old and more recent sampler models - S1000
 * seems to have a length of 150 bytes, while S2000/2800 etc have 192 bytes here.
 * However, if I send an S1000 "RPDATA" command, I should only look at the first 150
 * bytes.
 * If the requested program does not exist, returns an error.
 *
 * INPUTS:
 *    LHANDLE handle: handle to previously opened Lakai device
 *    int prognum: number of program to overwrite/create
 *    int len: Length of the data block following in "data"
 *    unsigned char *data: Pointer to program data
 *
 * OUTPUTS:
 *    -
 *
 * RETURNS:
 *    -1 if an error occurred (no mem, GROUPS does not match etc),
 *    0 otherwise
 */
int lakai_put_program(LHANDLE handle, int prognum, unsigned char *data, int len);


/*
 * Replaces an existing keygroup in a program, or creates a new keygroup.
 * From The SysEx docs: "In the case of transmitting to the S1000, if
 * the keygroup number is above the highest existing keygroup number, a new
 * keygroup will be created if a block is free, otherwise the existing 
 * keygroup will be replaced. The use of program number 255 is a special 
 * case where the keygroup data will be installed in the program previously 
 * created. This avoids the need to read the program list to find out what 
 * number was allocated to that program. If there are no free blocks for a 
 * new keygroup, an error message will be given".
 * 
 * INPUTS:
 *    LHANDLE handle: handle to previously opened Lakai device
 *    int prognum: number of program iin which to overwrite/create a keygroup
 *    int kgrpnum: number of keygroup to create/overwrite
 *    unsigned char *data: Pointer to keygroup data
 *
 * OUTPUTS:
 *    -
 *
 * RETURNS:
 *    -1 if an error occurred (no mem, GROUPS does not match etc),
 *    0 otherwise
 */
int lakai_put_keygroup(LHANDLE handle, int prognum, int kgrpnum, unsigned char *data, int len);


/*
 * sends a sample of 'len' bytes of data from the supplied buffer
 * to the sampler. The user has to take care that the buffer
 * is prefilled with data. Also, the user has to create a new sample
 * entry first before sending data into it.
 *
 * INPUTS:
 *    handle: LHANDLE to opened LAKAI device
 *    buffer: buffer that holds the data to be transferred
 *    len: number of bytes to be transferred
 *
 * RETURNS:
 *    number of bytes that have actually been transferred,
 *    0 if an error occurred.
 */
long lakai_put_sample(LHANDLE handle, int samplenum, char *buffer, long locat, long len);


#if 0
/*
 * S1000/900 (original sampler):
 * (F) = returns fixed number of bytes, (V) returns variable number of bytes
 * lakai_get_status_report()  (F)
 * lakai_get_program_names()  (V)
 * lakai_get_sample_names()   (V)
 * lakai_get_program()        (V...depends on # of keygroups in program..?)
 * lakai_get_keygroup()       (F)
 * lakai_get_sample_header()  (F)
 * lakai_get_drum_settings()  (F)  (later..)
 * lakai_get_misc_data()      (F)
 *
 * Note: RSPACK (Request Sample Data Packet(s)) and ASPACK (Accept Sample Data
 * Packet(s)) are not implemented because we use the faster way of directly
 * transferring blocks of memory from/to the sampler, bypassing the MIDI protocol.
 * 
 * lakai_put_program()
 * lakai_put_keygroup()
 * lakai_put_sample_header()
 * lakai_put_drum_settings()   (later..)
 * lakai_put_misc_data()
 *
 * lakai_delete_program()   (also deletes the associated keygroups)
 * lakai_delete_keygroup(programnum, keygroupnum)
 * lakai_delete_sample()   (also deletes the associated sampler header data)
 * lakai_set_exclusive()   (do I really need this?)
 *
 * S2000/S3000XL/S3200XL:
 * lakai_get_multi()
 * lakai_put_multi()
 *
 * S2800/S3000/S3200:
 * ???
 *
 * S5000/S6000:
 * ?? (need more info)
 *
 * yet unsorted:
 * FX/reverb bytes (R/W)
 * CueList bytes (R/W)
 * TakeList entries (R/W)
 * VolumeList entry (R/W)
 * Harddisk Directory entry (R/W)
 */


 /*
  * design question: when returning a MIDI message like from lakai_get_sample_names(),
  * should I strip off the SysEx header/EOX myself or pass this all over to the app?
  * Right now I'm for passing it all over to the app..
  */


/*
 * More new thoughts, 31.10.2002 :-)
 *
 * open function: can handle up to 16 simultaneous connections. Returns as "handle"
 * an index into an array like this:
 * #define MAX_LHANDLES 16
 * typedef struct
 * {
 *   int fd;
 *   ...possibly more maintenance data goes here..
 * } Lakai_handle;
 * Lakai_handle lhandles[MAX_LHANDLES];
 *
 *
 * Error handling: How does libsndfile do this?
 * Like this:
 * "sf_error_str () returns the current error message to the caller in the
 * string buffer provided."
 *
 * int      sf_error_str   (SNDFILE *sndfile, char* str, size_t len) ;
 *
 * The error string list looks like this:
 * typedef struct
 *    {  int   error ;
 *          char  *str ;
 *    } ErrorStruct ;
 *
 *    static
 *    ErrorStruct SndfileErrors [] =
 *    {
 *          {  SFE_NO_ERROR,  "No Error." },
 *          {  SFE_BAD_FILE,  "File does not exist or is not a regular file (possibly a pipe?)." },
 *          {  SFE_BAD_FILE_READ    , "File exists but no data could be read." },
 * ...
 * }
 *
 *
 *
 * Anyway, if a function runs into an error, it will usually give a return
 * code of NULL or 0 and set a "lakai_err" variable (either a static one..
 * no, not thread-safe) or a call-by-ref variable of the caller.
 * The call can then do with this whatever it wants, like ignore, print
 * error string to stdout, show an error window or whatever..
 * Example (as seen from user perspective):
 * ret = lakai_xxxx(params, ..., &err);
 * if (!ret)
 *   fprintf(stderr, "Error while reading: %s\n", lakai_errstr(err));
 *   exit(5);
 *
 *  -----------------------------------------------
 *  Another q: Do I need a mostly centralized "do the actual scsi command" function?
 *  If some kind of set-up of data structures is repeated in several locations over and
 *  over, this makes sense.
 *  What parameter would this function take then?
 *
 */

#endif

Generated by  Doxygen 1.6.0   Back to index