#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kernel.h"
//
#include "proto.h"
#include "instrument.h"
#include "externsnd.h"
#include "midi.h"
#include "mcode.h"


// this source file handles the instruments/patches/samples database


// don't change the following #defines
#define MAXSAMPLEBANKS        128
#define LOG2SAMPLESBANKSIZE   7
#define PATCHESPERBANK        128
#define MAXPATCHBANKS         16
#define SAMPLESPERBANK        (1<<LOG2SAMPLESBANKSIZE)
#define SAMPLESBANKMASK       (SAMPLESPERBANK-1)
#define GETSAMPLE(si)   sbanks[(si)>>LOG2SAMPLESBANKSIZE]->samples[(si) & SAMPLESBANKMASK]


typedef struct PATCHBANK {
  PATCH patches[PATCHESPERBANK];
  DRUM drums[PATCHESPERBANK];
} PATCHBANK;

typedef struct SAMPLEBANK {
  SAMPLEDATA samples[SAMPLESPERBANK];
} SAMPLEBANK;

static SAMPLEBANK *sbanks[MAXSAMPLEBANKS];
static PATCHBANK *banks[MAXPATCHBANKS]; // always 16!
static PATCHBANK bank0;
static SAMPLEBANK sbank0;
// exported
SAMPLEINDEX defaultsample;

// default, built in samples
#define WAVELENGTH            32 /* MUST be power of 2 */
#define WAVELENGTHS           16 /* to ensure it is long enough */
static signed short sawtooth[WAVELENGTH*WAVELENGTHS];
static signed short square[WAVELENGTH*WAVELENGTHS];
static signed short sine[WAVELENGTH*WAVELENGTHS];


static void reset_patch_bank(unsigned int banki);
static int delete_patch_bank(unsigned int banki);
static int allocate_patch_bank(unsigned int banki);
static void reset_sample_bank(unsigned int sbanki);
static int delete_sample_bank(unsigned int sbanki);
static int allocate_sample_bank(unsigned int sbanki);



void instruments_initialise() {

  int i;

  for (i = 0; i < MAXSAMPLEBANKS; i++)   sbanks[i] = NULL;
  for (i = 0; i < MAXPATCHBANKS; i++)    banks[i] = NULL;
  defaultsample = 0;

  // create default samples
  for (i = 0; i < WAVELENGTH; i++) {
    if (i < WAVELENGTH/2)
      square[i] = -32767;
    else
      square[i] = 32767;
    sawtooth[i] = (i-WAVELENGTH/2)*(32768/WAVELENGTH);
    sine[i] = sinetable[(i*1024)/WAVELENGTH];
  }
  for (i = WAVELENGTH; i < WAVELENGTH*WAVELENGTHS; i++) {
    square[i] = square[i &(WAVELENGTH-1)];
    sawtooth[i] = sawtooth[i &(WAVELENGTH-1)];
    sine[i] = sine[i &(WAVELENGTH-1)];
  }

  sbanks[0] = &sbank0;
  reset_sample_bank(0);

  sbanks[0]->samples[SAMPLE_SAWTOOTH].sampledata.data = sawtooth;
  sbanks[0]->samples[SAMPLE_SAWTOOTH].length     = WAVELENGTH*WAVELENGTHS;
  strcpy(sbanks[0]->samples[SAMPLE_SAWTOOTH].name, "sawtooth");
  sbanks[0]->samples[SAMPLE_SAWTOOTH].looppoint  = 0;
  sbanks[0]->samples[SAMPLE_SQUARE].sampledata.data = square;
  sbanks[0]->samples[SAMPLE_SQUARE].length       = WAVELENGTH*WAVELENGTHS;
  strcpy(sbanks[0]->samples[SAMPLE_SQUARE].name, "square");
  sbanks[0]->samples[SAMPLE_SQUARE].looppoint    = 0;
  sbanks[0]->samples[SAMPLE_SINE].sampledata.data = sine;
  sbanks[0]->samples[SAMPLE_SINE].length         = WAVELENGTH*WAVELENGTHS;
  strcpy(sbanks[0]->samples[SAMPLE_SINE].name, "sine");
  sbanks[0]->samples[SAMPLE_SINE].looppoint      = 0;

  banks[0] = &bank0;
  reset_patch_bank(0);
}


void instruments_kill() {

  unsigned int i;

  for (i = 0; i < MAXSAMPLEBANKS; i++)  delete_sample_bank(i);
  for (i = 1; i < MAXPATCHBANKS; i++)   delete_patch_bank(i);
}

// ------------------------------------------------------------------------
// create/manipulate/remove samples and patches

SAMPLEINDEX instruments_create_sample_part(char *name, SAMPLEINDEX base, unsigned int start, unsigned int end, unsigned int looppoint) {

  SAMPLEINDEX si;
  SAMPLEDATA *sbase, *snew;
  int bps;

  if (base == ILLEGAL_SAMPLE)                     return ILLEGAL_SAMPLE;
  if (!sbanks[base>>LOG2SAMPLESBANKSIZE])         return ILLEGAL_SAMPLE;
  sbase = &GETSAMPLE(base);
  if ((start >= end) || (end > sbase->length) || (!sbase->sampledata.data))
    return ILLEGAL_SAMPLE;
  // cannot create a part from another part
  if (sbase->flags.part)                          return ILLEGAL_SAMPLE;

  si = instruments_new_sample(name, end-start, 1, looppoint, sbase->format);
  if (si == ILLEGAL_SAMPLE)  return ILLEGAL_SAMPLE;

  snew = &GETSAMPLE(si);

  snew->flags.part = 1;
  snew->format = sbase->format;
  bps = 1;
  if (snew->format == SOURCE_16BIT)  bps = 2;
  snew->sampledata.data = (signed short *)((int)sbase->sampledata.data + bps*start);
  snew->basesample = base;
  sbase->usage++;

  return si;
}


SAMPLEINDEX instruments_create_sample(char *name, unsigned int length, unsigned int looppoint, unsigned char format) {
// return sampleindex or ILLEGAL_SAMPLE
  return instruments_new_sample(name, length, 0, looppoint, format);
}


SAMPLEINDEX instruments_install_sample(char *name, signed short *data, unsigned int length, unsigned int looppoint, unsigned char format) {
// return sampleindex or ILLEGAL_SAMPLE
  SAMPLEINDEX si;

  si = instruments_new_sample(name, length, 1, looppoint, format);
  if (si != ILLEGAL_SAMPLE)   GETSAMPLE(si).sampledata.data = data;
  return si;
}


SAMPLEINDEX instruments_new_sample(char *name, unsigned int length, int external, unsigned int looppoint, unsigned char format) {
// return sampleindex or ILLEGAL_SAMPLE

  SAMPLEINDEX si, i;
  unsigned int sbanki;
  SAMPLEDATA *sample;

  si = ILLEGAL_SAMPLE;
  // find an empty slot in a samplebank
  for (sbanki = 0; (sbanki < MAXSAMPLEBANKS) && (si == ILLEGAL_SAMPLE); sbanki++) {
    if (sbanks[sbanki])
      for (i = 0; (i < SAMPLESPERBANK) && (si == ILLEGAL_SAMPLE); i++) {
        if (!sbanks[sbanki]->samples[i].sampledata.data)
          si = i | (sbanki<<LOG2SAMPLESBANKSIZE);
      }
  }

  if (si == ILLEGAL_SAMPLE) {
    // all allocated samplebanks are used
    for (sbanki = 0; (sbanki < MAXSAMPLEBANKS) && (si == ILLEGAL_SAMPLE); sbanki++)
      if (!sbanks[sbanki])    si = sbanki<<LOG2SAMPLESBANKSIZE;
    if (si == ILLEGAL_SAMPLE)           return ILLEGAL_SAMPLE;
    // allocate new samplebank
    if (allocate_sample_bank(si>>LOG2SAMPLESBANKSIZE))    return ILLEGAL_SAMPLE;
  }

  sample = &GETSAMPLE(si);
  if (!external) {
    int bps;
    bps = 1;
    if (format == SOURCE_16BIT)  bps = 2;
    sample->sampledata.data = malloc(bps*length+(2<<LOG2SAMPLESPERFRAME));
    if (!sample->sampledata.data)  return ILLEGAL_SAMPLE;
    memset(sample->sampledata.data, 0, bps*length+(2<<LOG2SAMPLESPERFRAME));
  } else
    sample->sampledata.data = (signed short *)1;
  sample->length = length;
  strncpy(sample->name, name, 15);
  sample->name[15] = 0;
  if (looppoint != NO_LOOPING)
    if (looppoint >= length)  looppoint = 0;
  sample->looppoint = looppoint;
  sample->usage = 0;
  sample->flags.external = external;
  sample->format = format;
  sample->basesample = ILLEGAL_SAMPLE;
  return si;
}


int instruments_use_sample(SAMPLEINDEX si, int use) {
// return 0 for OK or error code otherwise
  SAMPLEBANK *sbank;
  SAMPLEDATA *sample;

  // must be a legal sampleindex
  if ((si>>LOG2SAMPLESBANKSIZE) >= MAXSAMPLEBANKS)  return 1;
  sbank = sbanks[si>>LOG2SAMPLESBANKSIZE];
  // samplebank must be exist
  if (!sbank)                                      return 2;
  // the sample must exist
  sample = &sbank->samples[si & SAMPLESBANKMASK];
  if (!sample->sampledata.data)                    return 3;

  if (use)
    sample->noteusage++;
  else
    sample->noteusage--;

  if (sample->flags.monophonic)        // don't allow multiple uses of monophonic samples
    if (use)
      if (sample->noteusage)                       return 4;

  return 0;
}


int instruments_remove_sample(SAMPLEINDEX si) {
// return 0 for OK
  SAMPLEBANK *sbank;
  unsigned int used;
  SAMPLEDATA *sample;
  SAMPLEINDEX i;

  // must be a legal sampleindex
  if ((si>>LOG2SAMPLESBANKSIZE) >= MAXSAMPLEBANKS)  return 1;
  sbank = sbanks[si>>LOG2SAMPLESBANKSIZE];
  // samplebank must be exist
  if (!sbank)                                       return 2;
  // the sample must exist
  sample = &sbank->samples[si & SAMPLESBANKMASK];
  if (!sample->sampledata.data)                     return 3;
  // the sample may be used by many patches
  if (sample->usage)                                return 4;
  // can't remove it if it is being played
  if (sample->noteusage)                            return 5;
  if (!sample->flags.external)     free(sample->sampledata.data);

  //if sample is a voice-generator, inform the voice-generator...
  if (sample->format == SOURCE_VOICE_GENERATOR)
    call_voice_generator(4, sample->sampledata.voice, NULL, NULL);

  sample->sampledata.data = NULL;

  if (sample->flags.part) {
    // decrement usage of base-sample
    if (sbanks[sample->basesample>>LOG2SAMPLESBANKSIZE])
     if (GETSAMPLE(sample->basesample).sampledata.data)
       GETSAMPLE(sample->basesample).usage--;
  }
  // release the samplebank if it is empty
  used = 0;
  for (i = 0; i < SAMPLESPERBANK; i++)
    if (sbank->samples[i].sampledata.data)  used++;
  if (used == 0) {
    free(sbank);
    sbanks[si>>LOG2SAMPLESBANKSIZE] = NULL;
  }

  return 0;
}


int instruments_unmap(unsigned int banki, unsigned int patchi, unsigned int octave) {

  PATCH *patch;

  if (banki >= MAXPATCHBANKS)                     return 1;
  if (!banks[banki])                              return 2;
  if (patchi >= PATCHESPERBANK)                   return 3;
  if ((octave >= OCTAVES) && (octave != ALL_OCTAVES))  return 4;

  patch = &banks[banki]->patches[patchi];
  if (octave == ALL_OCTAVES) {
    for (octave = 0; octave < OCTAVES; octave++) {
      SAMPLEINDEX si;

      si = patch->samples[octave];
      if (si != ILLEGAL_SAMPLE)   GETSAMPLE(si).usage--;
      patch->samples[octave] = ILLEGAL_SAMPLE;
    }
  } else {
    SAMPLEINDEX si;

    si = patch->samples[octave];
    if (si != ILLEGAL_SAMPLE)   GETSAMPLE(si).usage--;
    patch->samples[octave] = ILLEGAL_SAMPLE;
  }
  patch->name[0] = '\0';

  return 0;
}


int instruments_unmap_drum(unsigned int banki, unsigned int drumi) {

  DRUM *drum;

  if (banki >= MAXPATCHBANKS)                     return 1;
  if (!banks[banki])                              return 2;
  if (drumi >= PATCHESPERBANK)                    return 3;

  drum = &banks[banki]->drums[drumi];

  if (drum->sample != ILLEGAL_SAMPLE)  GETSAMPLE(drum->sample).usage--;
  drum->sample = ILLEGAL_SAMPLE;
  drum->name[0] = '\0';

  return 0;
}


int instruments_map(SAMPLEINDEX si, unsigned int banki, unsigned int patchi, unsigned int octave, unsigned int frequency) {
// return 0 for OK

  SAMPLEDATA *sample;
  PATCH *patch;
  SAMPLEINDEX oldsi;

  if (banki >= MAXPATCHBANKS)                               return 1;
  if (!banks[banki]) {
    allocate_patch_bank(banki);
    if (!banks[banki])                                      return 2;
  }
  if (!sbanks[si>>LOG2SAMPLESBANKSIZE])                     return 3;
  if (!GETSAMPLE(si).sampledata.data)                       return 4;
  if (patchi >= PATCHESPERBANK)                             return 5;
  if ((octave >= OCTAVES) && (octave != ALL_OCTAVES))       return 6;

  sample = &GETSAMPLE(si);
  patch = &banks[banki]->patches[patchi];

  if (octave == ALL_OCTAVES) {
    for (octave = 0; octave < OCTAVES; octave++) {
      oldsi = patch->samples[octave];
      if (oldsi != ILLEGAL_SAMPLE)  GETSAMPLE(oldsi).usage--;

      patch->samples[octave] = si;
      sample->usage++;
      patch->frequency[octave] = frequency;
    }
  } else {
    oldsi = patch->samples[octave];
    if (oldsi != ILLEGAL_SAMPLE)  GETSAMPLE(oldsi).usage--;

    patch->samples[octave] = si;
    sample->usage++;
    if (octave < 5)
      frequency >>= 5-octave;
    else
      frequency <<= octave-5;
    patch->frequency[octave] = frequency;
  }

  return 0;
}


int instruments_map_drum(SAMPLEINDEX si, unsigned int banki, unsigned int drumi, unsigned int frequency) {
// return 0 for OK
  SAMPLEDATA *sample;
  DRUM *drum;
  SAMPLEINDEX oldsi;

  if (banki >= MAXPATCHBANKS)                               return 1;
  if (!banks[banki]) {
    allocate_patch_bank(banki);
    if (!banks[banki])                                      return 2;
  }
  if (!sbanks[si>>LOG2SAMPLESBANKSIZE])                     return 3;
  if (!GETSAMPLE(si).sampledata.data)                       return 4;
  if (drumi >= PATCHESPERBANK)                              return 5;

  sample = &GETSAMPLE(si);
  drum = &banks[banki]->drums[drumi];

  oldsi = drum->sample;
  if (oldsi != ILLEGAL_SAMPLE)  GETSAMPLE(oldsi).usage--;

  drum->sample = si;
  sample->usage++;
  drum->frequency = frequency;

  return 0;
}


int instruments_set_envelope(unsigned int banki, unsigned int patchi, AHDSR *envelope, unsigned int maxduration) {

  PATCH *patch;

  if (banki >= MAXPATCHBANKS)                               return 1;
  if (!banks[banki])                                        return 2;
  if (patchi >= PATCHESPERBANK)                             return 3;

  patch = &banks[banki]->patches[patchi];
  patch->envelope.attacktime = envelope->attacktime;
  patch->envelope.holdtime = envelope->holdtime;
  patch->envelope.decaytime = envelope->decaytime;
  if (envelope->decayamount > 256)
    patch->envelope.decayamount = 256;
  else
    patch->envelope.decayamount = envelope->decayamount;
  patch->envelope.sustaintime = envelope->sustaintime;
  patch->maxduration = maxduration;

  return 0;
}


int instruments_set_drum_envelope(unsigned int banki, unsigned int drumi, AHDSR *envelope, unsigned int maxduration) {

  DRUM *drum;

  if (banki >= MAXPATCHBANKS)                               return 1;
  if (!banks[banki])                                        return 2;
  if (drumi >= PATCHESPERBANK)                              return 3;

  drum = &banks[banki]->drums[drumi];
  drum->envelope.attacktime = envelope->attacktime;
  drum->envelope.holdtime = envelope->holdtime;
  drum->envelope.decaytime = envelope->decaytime;
  if (envelope->decayamount > 256)
    drum->envelope.decayamount = 256;
  else
    drum->envelope.decayamount = envelope->decayamount;
  drum->envelope.sustaintime = envelope->sustaintime;
  drum->maxduration = maxduration;
  return 0;
}


SAMPLEINDEX instruments_enumerate_samples(SAMPLEINDEX si, char *name, unsigned int *usage) {

  unsigned int sbanki, i;
  int found;

  si++;
  sbanki = si>>LOG2SAMPLESBANKSIZE;
  if (sbanki >= MAXSAMPLEBANKS)         return ILLEGAL_SAMPLE;
  i = si & SAMPLESBANKMASK;

  found = 0;
  do {
    if (i >= SAMPLESPERBANK) {
      i = 0;
      do {
        sbanki++;
        if (sbanki >= MAXSAMPLEBANKS)     return ILLEGAL_SAMPLE;
      } while (!sbanks[sbanki]);
    }
    if (sbanks[sbanki]) {
      if (sbanks[sbanki]->samples[i].sampledata.data)
        found = 1;
      else
        i++;
    } else
      i++;
  } while (!found);

  if (name)     strcpy(name, sbanks[sbanki]->samples[i].name);
  if (usage)    *usage = sbanks[sbanki]->samples[i].usage;

  return i | (sbanki<<LOG2SAMPLESBANKSIZE);
}


int instruments_get_sample_info2(SAMPLEINDEX si, char *name, unsigned int *usage) {
// return 0 for OK
  unsigned int sbanki, i;

  sbanki = si>>LOG2SAMPLESBANKSIZE;
  i = si & SAMPLESBANKMASK;
  if (sbanki >= MAXSAMPLEBANKS)                     return 1;
  if (!sbanks[sbanki])                              return 2;
  if (!sbanks[sbanki]->samples[i].sampledata.data)  return 3;

  if (name)   strcpy(name, sbanks[sbanki]->samples[i].name);
  if (usage)  *usage = sbanks[sbanki]->samples[i].usage;

  return 0;
}


int instruments_set_sample_name(SAMPLEINDEX si, char *name) {
// return 0 for OK
  unsigned int sbanki, i;

  sbanki = si>>LOG2SAMPLESBANKSIZE;
  i = si & SAMPLESBANKMASK;
  if (sbanki >= MAXSAMPLEBANKS)                     return 1;
  if (!sbanks[sbanki])                              return 2;
  if (!sbanks[sbanki]->samples[i].sampledata.data)  return 3;

  strcpy(sbanks[sbanki]->samples[i].name, name);

  return 0;
}


SAMPLEINDEX instruments_find_named_sample(char *name) {

  char n0;
  SAMPLEINDEX si;
  unsigned int sbanki;

  n0 = name[0];
  for (sbanki = 0; sbanki < MAXSAMPLEBANKS; sbanki++)
    if (sbanks[sbanki])
      for (si = 0; si < SAMPLESPERBANK; si++)
        if (sbanks[sbanki]->samples[si].name[0] == n0)
          if (strcmp(sbanks[sbanki]->samples[si].name, name) == 0)
            return si + SAMPLESPERBANK*sbanki;

  return ILLEGAL_SAMPLE;
}


void instruments_set_default_sample(SAMPLEINDEX si) {
  defaultsample = si;
}


SAMPLEDATA *instruments_get_sample_ptr(SAMPLEINDEX si) {

  unsigned int sbanki, i;

  sbanki = si>>LOG2SAMPLESBANKSIZE;
  i = si & SAMPLESBANKMASK;
  if (sbanki >= MAXSAMPLEBANKS)                     return NULL;
  if (!sbanks[sbanki])                              return NULL;
  if (!sbanks[sbanki]->samples[i].sampledata.data)  return NULL;

  return sbanks[sbanki]->samples+i;
}

// ------------------------------------------------------------------------
// interface use by midi.c

int instruments_get_sample_info(SAMPLEINDEX si, signed short **data, unsigned int *length, unsigned int *looppoint, unsigned char *format) {
// return 0 for OK
// return data for default sample if specified sample doesn't exist
  SAMPLEDATA *sample;

  if ((si>>LOG2SAMPLESBANKSIZE) >= MAXSAMPLEBANKS)  si = defaultsample;
  if (!sbanks[si>>LOG2SAMPLESBANKSIZE])             si = defaultsample;
  if (si == ILLEGAL_SAMPLE)                       return 1;
  sample = &GETSAMPLE(si);
  if (!sample->sampledata.data) {
    if (defaultsample == ILLEGAL_SAMPLE)          return 1;
    sample = &GETSAMPLE(defaultsample);
  }

  if (data)       *data      = sample->sampledata.data;
  if (length)     *length    = sample->length;
  if (looppoint)  *looppoint = sample->looppoint;
  if (format)     *format    = sample->format;

  return 0;
}


PATCH *instruments_get_patch(unsigned int banki, unsigned int patchi) {

  if (banki >= MAXPATCHBANKS)           return NULL;
  if (!banks[banki])                    return NULL;
  if (patchi >= PATCHESPERBANK)         return NULL;

  return banks[banki]->patches+patchi;
}


DRUM *instruments_get_drum(unsigned int banki, unsigned char notei) {

  if (banki >= MAXPATCHBANKS)           return NULL;
  if (!banks[banki])                    return NULL;
  if (notei >= PATCHESPERBANK)          return NULL;

  return banks[banki]->drums+notei;
}


int instruments_set_patch_name(unsigned int banki, unsigned int patchi, char *name) {

  if (banki >= MAXPATCHBANKS)           return 1;
  if (!banks[banki])                    return 1;
  if (patchi >= PATCHESPERBANK)         return 1;

  strncpy(banks[banki]->patches[patchi].name, name, 15);
  banks[banki]->patches[patchi].name[15] = '\0';
  return 0;
}


int instruments_set_drum_name(unsigned int banki, unsigned int drumi, char *name) {

  if (banki >= MAXPATCHBANKS)           return 1;
  if (!banks[banki])                    return 1;
  if (drumi >= PATCHESPERBANK)          return 1;

  strncpy(banks[banki]->drums[drumi].name, name, 15);
  banks[banki]->drums[drumi].name[15] = '\0';
  return 0;
}


// ------------------------------------------------------------------------

void reset_patch_bank(unsigned int banki) {

  unsigned int i, oct;

  if (banki >= MAXPATCHBANKS)   return;
  if (!banks[banki])            return;

  memset(banks[banki], 0, sizeof(PATCHBANK));

  // reset patches
  for (i = 0; i < PATCHESPERBANK; i++) {
    for (oct = 1; oct < OCTAVES; oct++) {
      banks[banki]->patches[i].samples[oct] = SAMPLE_SAWTOOTH;
      banks[banki]->patches[i].frequency[oct] = 1000*WAVELENGTH;
      GETSAMPLE(SAMPLE_SAWTOOTH).usage++;
    }
    banks[banki]->patches[i].envelope.attacktime = 50;
    banks[banki]->patches[i].envelope.holdtime = 10;
    banks[banki]->patches[i].envelope.decaytime = 30;
    banks[banki]->patches[i].envelope.decayamount = 224; // 256=0dB, -32=-6dB
    banks[banki]->patches[i].envelope.sustaintime = SUSTAIN_INFINITE;
    banks[banki]->patches[i].maxduration = 0;
    banks[banki]->patches[i].name[0] = '\0';
  }

  // drums
  for (i = 0; i < PATCHESPERBANK; i++) {
    banks[banki]->drums[i].sample = SAMPLE_SAWTOOTH;
    GETSAMPLE(SAMPLE_SAWTOOTH).usage++;
    banks[banki]->drums[i].frequency = 8000;
    banks[banki]->drums[i].envelope.attacktime =  50;
    banks[banki]->drums[i].envelope.holdtime =  10;
    banks[banki]->drums[i].envelope.decaytime = 30;
    banks[banki]->drums[i].envelope.decayamount = 192;
    banks[banki]->drums[i].envelope.sustaintime = 600;
    banks[banki]->drums[i].maxduration = 0;
    banks[banki]->drums[i].name[0] = '\0';
  }
}


int delete_patch_bank(unsigned int banki) {
/// return 0 for OK
  int i, oct;

  if ((banki == 0) || (banki >= MAXPATCHBANKS))   return 1;
  if (!banks[banki])                              return 1;

  for (i = 0; i < PATCHESPERBANK; i++) {
    for (oct = 1; oct < OCTAVES; oct++) {
      SAMPLEINDEX si;

      si = banks[banki]->patches[i].samples[oct];
      GETSAMPLE(si).usage--;
    }
  }

  free(banks[banki]);
  banks[banki] = NULL;

  return 0;
}


int allocate_patch_bank(unsigned int banki) {
// return 0 for OK
  PATCHBANK *bank;

  if ((banki == 0) || (banki >= MAXPATCHBANKS))    return 1;

  bank = malloc(sizeof(PATCHBANK));

  if (!bank)  return 1;
  if (banks[banki])   delete_patch_bank(banki);

  banks[banki] = bank;
  reset_patch_bank(banki);

  return 0;
}

// ------------------------------------------------------------------------

void reset_sample_bank(unsigned int sbanki) {

  if (sbanki >= MAXSAMPLEBANKS)         return;
  memset(sbanks[sbanki], 0, sizeof(SAMPLEBANK));
}


int delete_sample_bank(unsigned int sbanki) {
// return 0 for OK
// likely to cause trouble - don't use except when killing the module

  SAMPLEINDEX si, start;

  if (sbanki >= MAXSAMPLEBANKS)         return 1;
  if (!sbanks[sbanki])                  return 1;

  start = 0;
  if (sbanki == 0)   start = 3;  // skip the 3 built-in samples
  for (si = start; si < SAMPLESPERBANK; si++)
    if (sbanks[sbanki]->samples[si].sampledata.data) {
      if (!sbanks[sbanki]->samples[si].flags.external)
        free(sbanks[sbanki]->samples[si].sampledata.data);
      sbanks[sbanki]->samples[si].sampledata.data = NULL;
    }
  if (sbanki == 0)                      return 0;

  free(sbanks[sbanki]);
  sbanks[sbanki] = NULL;

  return 0;
}


int allocate_sample_bank(unsigned int sbanki) {
// return 0 for OK

  SAMPLEBANK *sbank;

  if ((sbanki == 0) || (sbanki > MAXSAMPLEBANKS))  return 1;

  sbank = malloc(sizeof(SAMPLEBANK));
  if (!sbank)                 return 1;
  if (sbanks[sbanki])         delete_sample_bank(sbanki);

  sbanks[sbanki] = sbank;
  reset_sample_bank(sbanki);

  return 0;
}

// ------------------------------------------------------------------------

void build_from_harmonics(signed short *sample, int length, int wavelength, int *harmonics, int wavetype) {


#define FRAC        8
  if (wavetype == SAMPLE_SINE) {
    int harmonic;

    for (harmonic = 0; harmonic < 16; harmonic++) {
      int step, pos, volume, i;

      step = (harmonic+1)<<FRAC;
      step = (step*1024+wavelength/2)/wavelength;
      pos = 0;
      volume = harmonics[harmonic];
      for (i = 0; i < length; i++) {
        int amplitude;

        amplitude = sample[i] + (volume*sinetable[pos>>FRAC])/256;
        if (amplitude < -32767)
          amplitude = -32767;
        else if (amplitude > 32767)
          amplitude = 32767;
        sample[i] = amplitude;
        pos += step;
        if (pos >= (1024<<FRAC))   pos -= 1024<<FRAC;
      }
    }
  }
}
