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

lakai.c

/*
 * 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 */
}


Generated by  Doxygen 1.6.0   Back to index