/* 
 * sound/softoss.c
 *
 * Software based MIDI synthsesizer driver.
 */
/*
 * Copyright (C) by Hannu Savolainen 1993-1997
 *
 * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
 * Version 2 (June 1991). See the "COPYING" file distributed with this software
 * for more info.
 */
#include <linux/config.h>


/*
 * When POLLED_MODE is defined, the resampling loop is run using a timer
 * callback routine. Normally the resampling loop is executed inside
 * audio buffer interrupt handler which doesn't work with single mode DMA.
 */
#define SOFTSYN_MAIN
#undef  POLLED_MODE
#define HANDLE_LFO

#define ENVELOPE_SCALE		8
#define NO_SAMPLE		0xffff

#include "sound_config.h"

#ifdef CONFIG_SOFTOSS
#include "softoss.h"
#include <sys/ultrasound.h>

int             softsynth_disabled = 0;

static volatile int intr_pending = 0;

#ifdef POLLED_MODE

static struct timer_list poll_timer =
{NULL, NULL, 0, 0, softsyn_poll};

#else
#endif

#ifdef HANDLE_LFO
/*
 * LFO table. Playback at 128 Hz gives 1 Hz LFO frequency.
 */
static int      tremolo_table[128] =
{
  0, 39, 158, 355, 630, 982, 1411, 1915,
  2494, 3146, 3869, 4662, 5522, 6448, 7438, 8489,
  9598, 10762, 11980, 13248, 14563, 15922, 17321, 18758,
  20228, 21729, 23256, 24806, 26375, 27960, 29556, 31160,
  32768, 34376, 35980, 37576, 39161, 40730, 42280, 43807,
  45308, 46778, 48215, 49614, 50973, 52288, 53556, 54774,
  55938, 57047, 58098, 59088, 60014, 60874, 61667, 62390,
  63042, 63621, 64125, 64554, 64906, 65181, 65378, 65497,
  65536, 65497, 65378, 65181, 64906, 64554, 64125, 63621,
  63042, 62390, 61667, 60874, 60014, 59087, 58098, 57047,
  55938, 54774, 53556, 52288, 50973, 49614, 48215, 46778,
  45308, 43807, 42280, 40730, 39161, 37576, 35980, 34376,
  32768, 31160, 29556, 27960, 26375, 24806, 23256, 21729,
  20228, 18758, 17321, 15922, 14563, 13248, 11980, 10762,
  9598, 8489, 7438, 6448, 5522, 4662, 3869, 3146,
  2494, 1915, 1411, 982, 630, 355, 158, 39
};

static int      vibrato_table[128] =
{
  0, 1608, 3212, 4808, 6393, 7962, 9512, 11039,
  12540, 14010, 15447, 16846, 18205, 19520, 20788, 22006,
  23170, 24279, 25330, 26320, 27246, 28106, 28899, 29622,
  30274, 30853, 31357, 31786, 32138, 32413, 32610, 32729,
  32768, 32729, 32610, 32413, 32138, 31786, 31357, 30853,
  30274, 29622, 28899, 28106, 27246, 26320, 25330, 24279,
  23170, 22006, 20788, 19520, 18205, 16846, 15447, 14010,
  12540, 11039, 9512, 7962, 6393, 4808, 3212, 1608,
  0, -1608, -3212, -4808, -6393, -7962, -9512, -11039,
  -12540, -14010, -15447, -16846, -18205, -19520, -20788, -22006,
  -23170, -24279, -25330, -26320, -27246, -28106, -28899, -29622,
  -30274, -30853, -31357, -31786, -32138, -32413, -32610, -32729,
  -32768, -32729, -32610, -32413, -32138, -31786, -31357, -30853,
  -30274, -29622, -28899, -28106, -27246, -26320, -25330, -24279,
  -23170, -22006, -20788, -19520, -18205, -16846, -15447, -14010,
  -12540, -11039, -9512, -7962, -6393, -4808, -3212, -1608
};

#endif

static unsigned long last_resample_jiffies;
static unsigned long resample_counter;

extern int     *sound_osp;

static volatile int is_running = 0;
static int      softsynth_loaded = 0;

static struct synth_info softsyn_info =
{"SoftOSS", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 0, 16, 0, MAX_PATCH};

static struct softsyn_devc sdev_info =
{0};
softsyn_devc   *devc = &sdev_info;

static struct voice_alloc_info *voice_alloc;

static int      softsyn_open (int synthdev, int mode);
static void     init_voice (softsyn_devc * devc, int voice);
static void     compute_step (int voice);

static volatile int tmr_running = 0;
static int      voice_limit = 24;

static void
set_max_voices (int nr)
{
  int             i;

  if (nr < 4)
    nr = 4;

  if (nr > voice_limit)
    nr = voice_limit;

  voice_alloc->max_voice = devc->maxvoice = nr;
  devc->afterscale = 5;

  for (i = 31; i > 0; i--)
    if (nr & (1 << i))
      {
	devc->afterscale = i + 1;
	return;
      }
}

static void
update_vibrato (int voice)
{
  voice_info     *v = &softoss_voices[voice];

#ifdef HANDLE_LFO
  int             x;

  x = vibrato_table[v->vibrato_phase >> 8];
  v->vibrato_phase = (v->vibrato_phase + v->vibrato_step) & 0x7fff;

  x = (x * v->vibrato_depth) >> 15;
  v->vibrato_level = (x * 600) >> 8;

  compute_step (voice);
#else
  v->vibrato_level = 0;
#endif
}

#ifdef HANDLE_LFO
static void
update_tremolo (int voice)
{
  voice_info     *v = &softoss_voices[voice];
  int             x;

  x = tremolo_table[v->tremolo_phase >> 8];
  v->tremolo_phase = (v->tremolo_phase + v->tremolo_step) & 0x7fff;

  v->tremolo_level = (x * v->tremolo_depth) >> 20;
}
#endif

static void
start_vibrato (int voice)
{
  voice_info     *v = &softoss_voices[voice];
  int             rate;

  if (!v->vibrato_depth)
    return;

  rate = v->vibrato_rate * 6 * 128;
  v->vibrato_step = (rate * devc->control_rate) / devc->speed;

  devc->vibratomap |= (1 << voice);	/* Enable vibrato */
}

static void
start_tremolo (int voice)
{
  voice_info     *v = &softoss_voices[voice];
  int             rate;

  if (!v->tremolo_depth)
    return;

  rate = v->tremolo_rate * 6 * 128;
  v->tremolo_step = (rate * devc->control_rate) / devc->speed;

  devc->tremolomap |= (1 << voice);	/* Enable tremolo */
}

static void
update_volume (int voice)
{
  voice_info     *v = &softoss_voices[voice];
  unsigned int    vol;

/*
 * Compute plain volume
 */

  vol = (v->velocity * v->expression_vol * v->main_vol) >> 12;

#ifdef HANDLE_LFO
/*
 * Handle LFO
 */

  if (devc->tremolomap & (1 << voice))
    {
      int             t;

      t = 32768 - v->tremolo_level;
      vol = (vol * t) >> 15;
      update_tremolo (voice);
    }
#endif
/*
 * Envelope
 */
  if (v->mode & WAVE_ENVELOPES && !v->percussive_voice)
    vol = (vol * (v->envelope_vol >> 16)) >> 19;
  else
    vol >>= 4;

/*
 * Handle panning
 */

  if (v->panning < 0)		/* Pan left */
    v->rightvol = (vol * (128 + v->panning)) / 128;
  else
    v->rightvol = vol;

  if (v->panning > 0)		/* Pan right */
    v->leftvol = (vol * (128 - v->panning)) / 128;
  else
    v->leftvol = vol;
}

static void
step_envelope (int voice, int do_release, int velocity)
{
  voice_info     *v = &softoss_voices[voice];
  int             r, rate, time, dif;
  unsigned int    vol;
  unsigned long   flags;

  save_flags (flags);
  cli ();

  if (!voice_active[voice] || v->sample == NULL)
    {
      restore_flags (flags);
      return;
    }

  if (!do_release)
    if (v->mode & WAVE_SUSTAIN_ON && v->envelope_phase == 2)
      {				/* Stop envelope until note off */
	v->envelope_volstep = 0;
	v->envelope_time = 0x7fffffff;
	if (v->mode & WAVE_VIBRATO)
	  start_vibrato (voice);
	if (v->mode & WAVE_TREMOLO)
	  start_tremolo (voice);
	restore_flags (flags);
	return;
      }

  if (do_release)
    v->envelope_phase = 3;
  else
    v->envelope_phase++;

  if (v->envelope_phase >= 5)	/* Finished */
    {
      init_voice (devc, voice);
      restore_flags (flags);
      return;
    }

  vol = v->envelope_target = v->sample->env_offset[v->envelope_phase] << 22;


  rate = v->sample->env_rate[v->envelope_phase];
  r = 3 - ((rate >> 6) & 0x3);
  r *= 3;
  r = (int) (rate & 0x3f) << r;
  rate = (((r * 44100) / devc->speed) * devc->control_rate) << 8;

  if (rate < (1 << 20))		/* Avoid infinitely "releasing" voices */
    rate = 1 << 20;

  dif = (v->envelope_vol - vol);
  if (dif < 0)
    dif *= -1;
  if (dif < rate * 2)		/* Too close */
    {
      step_envelope (voice, 0, 60);
      restore_flags (flags);
      return;
    }

  if (vol > v->envelope_vol)
    {
      v->envelope_volstep = rate;
      time = (vol - v->envelope_vol) / rate;
    }
  else
    {
      v->envelope_volstep = -rate;
      time = (v->envelope_vol - vol) / rate;
    }

  time--;
  if (time <= 0)
    time = 1;

  v->envelope_time = time;


  restore_flags (flags);
}

static void
step_envelope_lfo (int voice)
{
  voice_info     *v = &softoss_voices[voice];

/*
 * Update pitch (vibrato) LFO 
 */

  if (devc->vibratomap & (1 << voice))
    update_vibrato (voice);

/* 
 * Update envelope
 */

  if (v->mode & WAVE_ENVELOPES)
    {
      v->envelope_vol += v->envelope_volstep;
      /* Overshoot protection */
      if (v->envelope_vol < 0)
	{
	  v->envelope_vol = v->envelope_target;
	  v->envelope_volstep = 0;
	}

      if (v->envelope_time-- <= 0)
	{
	  v->envelope_vol = v->envelope_target;
	  step_envelope (voice, 0, 60);
	}
    }
}

static void
compute_step (int voice)
{
  voice_info     *v = &softoss_voices[voice];

  /*
   * Since the pitch bender may have been set before playing the note, we
   * have to calculate the bending now.
   */

  v->current_freq = compute_finetune (v->orig_freq,
				      v->bender,
				      v->bender_range,
				      v->vibrato_level);
  v->step = (((v->current_freq << 9) + (devc->speed >> 1)) / devc->speed);

  if (v->mode & WAVE_LOOP_BACK)
    v->step *= -1;		/* Reversed playback */
}

static void
init_voice (softsyn_devc * devc, int voice)
{
  voice_info     *v = &softoss_voices[voice];
  unsigned long   flags;

  save_flags (flags);
  cli ();
  voice_active[voice] = 0;
  devc->vibratomap &= ~(1 << voice);
  devc->tremolomap &= ~(1 << voice);
  v->mode = 0;
  v->wave = NULL;
  v->sample = NULL;
  v->ptr = 0;
  v->step = 0;
  v->startloop = 0;
  v->startbackloop = 0;
  v->endloop = 0;
  v->looplen = 0;
  v->bender = 0;
  v->bender_range = 200;
  v->panning = 0;
  v->main_vol = 127;
  v->expression_vol = 127;
  v->patch_vol = 127;
  v->percussive_voice = 0;
  v->sustain_mode = 0;
  v->envelope_phase = 1;
  v->envelope_vol = 1 << 24;
  v->envelope_volstep = 256;
  v->envelope_time = 0;
  v->vibrato_phase = 0;
  v->vibrato_step = 0;
  v->vibrato_level = 0;
  v->vibrato_rate = 0;
  v->vibrato_depth = 0;
  v->tremolo_phase = 0;
  v->tremolo_step = 0;
  v->tremolo_level = 0;
  v->tremolo_rate = 0;
  v->tremolo_depth = 0;
  voice_alloc->map[voice] = 0;
  voice_alloc->alloc_times[voice] = 0;
  restore_flags (flags);
}

static void
reset_samples (softsyn_devc * devc)
{
  int             i;

  for (i = 0; i < MAX_VOICE; i++)
    voice_active[i] = 0;
  for (i = 0; i < devc->maxvoice; i++)
    {
      init_voice (devc, i);
      softoss_voices[i].instr = 0;
    }

  devc->ram_used = 0;

  for (i = 0; i < MAX_PATCH; i++)
    devc->programs[i] = NO_SAMPLE;

  for (i = 0; i < devc->nrsamples; i++)
    {
      vfree (devc->samples[i]);
      vfree (devc->wave[i]);
      devc->samples[i] = NULL;
      devc->wave[i] = NULL;
    }

  devc->nrsamples = 0;
}

static void
init_engine (softsyn_devc * devc)
{
  int             i, fz, srate, sz = devc->channels;

  set_max_voices (devc->default_max_voices);
  voice_alloc->timestamp = 0;

  if (devc->bits == 16)
    sz *= 2;

  fz = devc->fragsize / sz;	/* Samples per fragment */
  devc->samples_per_fragment = fz;

  devc->usecs = 0;
  devc->usecs_per_frag = (1000000 * fz) / devc->speed;

  for (i = 0; i < devc->maxvoice; i++)
    {
      init_voice (devc, i);
      softoss_voices[i].instr = 0;
    }

  devc->engine_state = ES_STOPPED;

/*
 *    Initialize delay
 */

  for (i = 0; i < DELAY_SIZE; i++)
    left_delay[i] = right_delay[i] = 0;
  delayp = 0;
  srate = (devc->speed / 10000);	/* 1 to 4 */
  if (srate <= 0)
    srate = 1;
  devc->delay_size = (DELAY_SIZE * srate) / 4;
  if (devc->delay_size == 0 || devc->delay_size > DELAY_SIZE)
    devc->delay_size = DELAY_SIZE;
}

void
softsyn_control_loop (void)
{
  int             voice;

/*
 *    Recompute envlope, LFO, etc.
 */
  for (voice = 0; voice < devc->maxvoice; voice++)
    if (voice_active[voice])
      {
	update_volume (voice);
	step_envelope_lfo (voice);
      }
    else
      voice_alloc->map[voice] = 0;
}

static void     start_engine (softsyn_devc * devc);

static void
do_resample (int dummy)
{
  struct dma_buffparms *dmap = audio_devs[devc->audiodev]->dmap_out;
  struct voice_info *vinfo;
  unsigned long   flags, jif;

  int             voice, loops;
  short          *buf;

  if (softsynth_disabled)
    return;

  save_flags (flags);
  cli ();

  if (is_running)
    {
      printk ("SoftOSS: Playback overrun\n");
      restore_flags (flags);
      return;
    }

  jif = jiffies;
  if (jif == last_resample_jiffies)
    {
      if (resample_counter++ > 50)
	{
	  for (voice = 0; voice < devc->maxvoice; voice++)
	    init_voice (devc, voice);
	  voice_limit--;
	  resample_counter = 0;
	  printk ("SoftOSS: CPU overload. Limiting # of voices to %d\n", voice_limit);

	  if (voice_limit < 10)
	    {
	      voice_limit = 10;
	      devc->speed = (devc->speed * 2) / 3;

	      printk ("SoftOSS: Dropping sampling rate and stopping the device.\n");
	      softsynth_disabled = 1;
	    }
	}
    }
  else
    {
      last_resample_jiffies = jif;
      resample_counter = 0;
    }

  /* is_running = 1; */

  if (dmap->qlen > devc->max_playahead)
    {
      printk ("SoftOSS: audio buffers full\n");
      is_running = 0;
      restore_flags (flags);
      return;
    }

/*
 * First verify that all active voices are valid (do this just once per block).
 */
  for (voice = 0; voice < devc->maxvoice; voice++)
    if (voice_active[voice])
      {
	int             ptr;

	vinfo = &softoss_voices[voice];
	ptr = vinfo->ptr >> 9;

	if (vinfo->wave == NULL ||
	    ptr < 0 ||
	    ptr > vinfo->sample->len)
	  init_voice (devc, voice);
	else if (!(vinfo->mode & WAVE_LOOPING) &&
		 (vinfo->ptr + vinfo->step) > vinfo->endloop)
	  voice_active[voice] = 0;
      }

/*
 *    Start the resampling process
 */

  loops = devc->samples_per_fragment;
  buf = (short *) (dmap->raw_buf + (dmap->qtail * dmap->fragment_size));

  softsynth_resample_loop (buf, loops);		/* In Xsoftsynth_rs.c */

  dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
  dmap->qlen++;
  dmap->user_counter += dmap->fragment_size;

  devc->usecs += devc->usecs_per_frag;

  if (tmr_running)
    {
      sound_timer_interrupt ();
    }

/*
 *    Execute timer
 */

  if (!tmr_running)
    if (devc->usecs >= devc->next_event_usecs)
      {
	devc->next_event_usecs = ~0;
	sequencer_timer (0);
      }
  is_running = 0;
  restore_flags (flags);
}

static void
delayed_resample (int dummy)
{
  struct dma_buffparms *dmap = audio_devs[devc->audiodev]->dmap_out;
  int             n = 0;

  if (is_running)
    return;

  while (devc->engine_state != ES_STOPPED &&
	 dmap->qlen < devc->max_playahead && n++ < 2)
    do_resample (0);
  intr_pending = 0;
}

#ifdef POLLED_MODE
static void
softsyn_poll (unsigned long dummy)
{
  delayed_resample (0);

  if (devc->engine_state != ES_STOPPED)

    {
      poll_timer.expires = (1) + jiffies;
      add_timer (&poll_timer);
    };
}
#else
static void
softsyn_callback (int dev, int parm)
{
  delayed_resample (0);
}
#endif

static void
start_engine (softsyn_devc * devc)
{
  struct dma_buffparms *dmap;

  if (!devc->audio_opened)
    if (softsyn_open (devc->synthdev, 0) < 0)
      return;

  if (devc->audiodev >= num_audiodevs)
    return;

  dmap = audio_devs[devc->audiodev]->dmap_out;

  devc->usecs = 0;
  devc->next_event_usecs = ~0;
  devc->control_rate = 64;
  devc->control_counter = 0;

  if (devc->engine_state == ES_STOPPED)
    {
      int             trig, n = 0;

      trig = 0;
      dma_ioctl (devc->audiodev, SNDCTL_DSP_SETTRIGGER, (caddr_t) & trig);
#ifdef POLLED_MODE
      ;

      {
	poll_timer.expires = (1) + jiffies;
	add_timer (&poll_timer);
      };			/* Start polling */
#else
      dmap->audio_callback = softsyn_callback;
      dmap->qhead = dmap->qtail = dmap->qlen = 0;
#endif

      while (dmap->qlen < devc->max_playahead && n++ < 2)
	do_resample (0);

      devc->engine_state = ES_STARTED;
      last_resample_jiffies = jiffies;
      resample_counter = 0;

      trig = PCM_ENABLE_OUTPUT;
      if (dma_ioctl (devc->audiodev, SNDCTL_DSP_SETTRIGGER,
		     (caddr_t) & trig) < 0)
	{
	  printk ("SoftOSS: Trigger failed\n");
	}


    }
}

static void
stop_engine (softsyn_devc * devc)
{
}

static void
request_engine (softsyn_devc * devc, int ticks)
{
  if (ticks < 0)		/* Relative time */
    devc->next_event_usecs = devc->usecs - ticks * (1000000 / HZ);
  else
    devc->next_event_usecs = ticks * (1000000 / HZ);
}

/*
 * Softsync hook serves mode1 (timing) calls made by sequencer.c
 */
static int
softsynth_hook (int cmd, int parm1, int parm2, unsigned long parm3)
{
  switch (cmd)
    {
    case SSYN_START:
      start_engine (devc);
      break;

    case SSYN_STOP:
      stop_engine (devc);
      break;

    case SSYN_REQUEST:
      request_engine (devc, parm1);
      break;

    case SSYN_GETTIME:
      return devc->usecs / (1000000 / HZ);
      break;

    default:
      printk ("SoftOSS: Unknown request %d\n", cmd);
    }

  return 0;
}

static int
softsyn_ioctl (int dev,
	       unsigned int cmd, caddr_t arg)
{
  switch (cmd)
    {

    case SNDCTL_SYNTH_INFO:
      softsyn_info.nr_voices = devc->maxvoice;

      memcpy ((&((char *) arg)[0]), (char *) &softsyn_info, sizeof (softsyn_info));
      return 0;
      break;

    case SNDCTL_SEQ_RESETSAMPLES:
      stop_engine (devc);
      reset_samples (devc);
      return 0;
      break;

    case SNDCTL_SYNTH_MEMAVL:
      return devc->ram_size - devc->ram_used;
      break;

    default:
      return -EINVAL;
    }

}

static int
softsyn_kill_note (int devno, int voice, int note, int velocity)
{
  if (voice < 0 || voice > devc->maxvoice)
    return 0;
  voice_alloc->map[voice] = 0xffff;	/* Releasing */

  if (softoss_voices[voice].sustain_mode & 1)	/* Sustain controller on */
    {
      softoss_voices[voice].sustain_mode = 3;	/* Note off pending */
      return 0;
    }

  if (velocity > 127 || softoss_voices[voice].mode & WAVE_FAST_RELEASE)
    {
      init_voice (devc, voice);	/* Mark it inactive */
      return 0;
    }

  if (softoss_voices[voice].mode & WAVE_ENVELOPES)
    step_envelope (voice, 1, velocity);		/* Enter sustain phase */
  else
    init_voice (devc, voice);	/* Mark it inactive */
  return 0;
}

static int
softsyn_set_instr (int dev, int voice, int instr)
{
  if (voice < 0 || voice > devc->maxvoice)
    return 0;

  if (instr < 0 || instr > MAX_PATCH)
    {
      printk ("SoftOSS: Invalid instrument number %d\n", instr);
      return 0;
    }

  softoss_voices[voice].instr = instr;

  return 0;
}

static int
softsyn_start_note (int dev, int voice, int note, int volume)
{
  int             instr = 0;
  int             best_sample, best_delta, delta_freq, selected;
  unsigned long   note_freq, freq, base_note, flags;
  voice_info     *v = &softoss_voices[voice];

  struct patch_info *sample;

  if (voice < 0 || voice > devc->maxvoice)
    return 0;

  if (volume == 0)		/* Actually note off */
    softsyn_kill_note (dev, voice, note, volume);

  save_flags (flags);
  cli ();

  if (note == 255)
    {				/* Just volume update */
      v->velocity = volume;
      if (voice_active[voice])
	update_volume (voice);
      restore_flags (flags);
      return 0;
    }

  voice_active[voice] = 0;	/* Stop the voice for a while */
  devc->vibratomap &= ~(1 << voice);
  devc->tremolomap &= ~(1 << voice);

  instr = v->instr;
  if (instr < 0 || instr > MAX_PATCH || devc->programs[instr] == NO_SAMPLE)
    {
      printk ("SoftOSS: Undefined MIDI instrument %d\n", instr);
      restore_flags (flags);
      return 0;
    }

  instr = devc->programs[instr];

  if (instr < 0 || instr >= devc->nrsamples)
    {
      printk ("SoftOSS: Corrupted MIDI instrument %d (%d)\n", v->instr, instr);
      restore_flags (flags);
      return 0;
    }

  note_freq = note_to_freq (note);

  selected = -1;

  best_sample = instr;
  best_delta = 1000000;

  while (instr != NO_SAMPLE && instr >= 0 && selected == -1)
    {
      delta_freq = note_freq - devc->samples[instr]->base_note;

      if (delta_freq < 0)
	delta_freq = -delta_freq;
      if (delta_freq < best_delta)
	{
	  best_sample = instr;
	  best_delta = delta_freq;
	}
      if (devc->samples[instr]->low_note <= note_freq &&
	  note_freq <= devc->samples[instr]->high_note)
	selected = instr;
      else
	instr = devc->samples[instr]->key;	/* Link to next sample */

      if (instr < 0 || instr >= devc->nrsamples)
	instr = NO_SAMPLE;
    }

  if (selected == -1)
    instr = best_sample;
  else
    instr = selected;

  if (instr < 0 || instr == NO_SAMPLE || instr > devc->nrsamples)
    {
      printk ("SoftOSS: Unresolved MIDI instrument %d\n", v->instr);
      restore_flags (flags);
      return 0;
    }

  sample = devc->samples[instr];
  v->sample = sample;

  if (v->percussive_voice)	/* No key tracking */
    {
      v->orig_freq = sample->base_freq;		/* Fixed pitch */
    }
  else
    {
      base_note = sample->base_note / 100;
      note_freq /= 100;

      freq = sample->base_freq * note_freq / base_note;
      v->orig_freq = freq;
    }

  if (!(sample->mode & WAVE_LOOPING))
    {
      sample->loop_end = sample->len;
    }

  v->wave = devc->wave[instr];

  if (volume < 0)
    volume = 0;
  else if (volume > 127)
    volume = 127;

  v->ptr = 0;
  v->startloop = sample->loop_start * 512;
  v->startbackloop = 0;
  v->endloop = sample->loop_end * 512;
  v->looplen = (sample->loop_end - sample->loop_start) * 512;
  v->leftvol = 64;
  v->rightvol = 64;
  v->patch_vol = sample->volume;
  v->velocity = volume;
  v->mode = sample->mode;
  v->vibrato_phase = 0;
  v->vibrato_step = 0;
  v->vibrato_level = 0;
  v->vibrato_rate = 0;
  v->vibrato_depth = 0;
  v->tremolo_phase = 0;
  v->tremolo_step = 0;
  v->tremolo_level = 0;
  v->tremolo_rate = 0;
  v->tremolo_depth = 0;

  if (!(v->mode & WAVE_LOOPING))
    v->mode &= ~(WAVE_BIDIR_LOOP | WAVE_LOOP_BACK);
  else if (v->mode & WAVE_LOOP_BACK)
    {
      v->ptr = sample->len;
      v->startbackloop = v->startloop;
    }

  if (v->mode & WAVE_VIBRATO)
    {
      v->vibrato_rate = sample->vibrato_rate;
      v->vibrato_depth = sample->vibrato_depth;
    }

  if (v->mode & WAVE_TREMOLO)
    {
      v->tremolo_rate = sample->tremolo_rate;
      v->tremolo_depth = sample->tremolo_depth;
    }

  if (v->mode & WAVE_ENVELOPES)
    {
      v->envelope_phase = -1;
      v->envelope_vol = 0;
      step_envelope (voice, 0, 60);
    }
  update_volume (voice);
  compute_step (voice);

  voice_active[voice] = 1;	/* Mark it active */

  restore_flags (flags);
  return 0;
}

static int
softsyn_open (int synthdev, int mode)
{
  int             err;
  extern int      softoss_dev;
  int             frags = 0x7fff0007;	/* fragment size of 128 bytes */

  if (devc->audio_opened)	/* Already opened */
    return 0;

  softsynth_disabled = 0;
  devc->finfo.mode = OPEN_WRITE;
  devc->finfo.flags = 0;

  if (softoss_dev >= num_audiodevs)
    softoss_dev = num_audiodevs - 1;

  if (softoss_dev < 0)
    softoss_dev = 0;
  if (softoss_dev >= num_audiodevs)
    return -ENXIO;
  devc->audiodev = softoss_dev;

  if (!(audio_devs[devc->audiodev]->format_mask & AFMT_S16_LE))
    {
      printk ("SoftOSS: The audio device doesn't support 16 bits\n");
      return -ENXIO;
    }
  if ((err = audio_open ((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo)) < 0)
    return err;

  devc->speed = audio_devs[devc->audiodev]->d->set_speed (
					       devc->audiodev, devc->speed);
  devc->channels = audio_devs[devc->audiodev]->d->set_channels (
					    devc->audiodev, devc->channels);
  devc->bits = audio_devs[devc->audiodev]->d->set_bits (
						devc->audiodev, devc->bits);


  DDB (printk ("SoftOSS: Using audio dev %d, speed %d, bits %d, channels %d\n", devc->audiodev, devc->speed, devc->bits, devc->channels));

  dma_ioctl (devc->audiodev, SNDCTL_DSP_SETFRAGMENT, (caddr_t) & frags);
  dma_ioctl (devc->audiodev, SNDCTL_DSP_GETBLKSIZE, (caddr_t) & devc->fragsize);

  if (devc->bits != 16 || devc->channels != 2)
    {
      audio_release ((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo);
      printk ("SoftOSS: A 16 bit stereo soundcard is required\n");
      return 0;
    }

  if (devc->max_playahead >= audio_devs[devc->audiodev]->dmap_out->nbufs)
    devc->max_playahead = audio_devs[devc->audiodev]->dmap_out->nbufs;

  DDB (printk ("SoftOSS: Using %d fragments of %d bytes\n", devc->max_playahead, devc->fragsize));

  init_engine (devc);
  devc->audio_opened = 1;
  devc->sequencer_mode = mode;
  return 0;
}

static void
softsyn_close (int synthdev)
{
  devc->engine_state = ES_STOPPED;
#ifdef POLLED_MODE
  del_timer (&poll_timer);;
#endif
  dma_ioctl (devc->audiodev, SNDCTL_DSP_RESET, 0);
  if (devc->audio_opened)
    audio_release ((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo);
  devc->audio_opened = 0;
}

static void
softsyn_hw_control (int dev, unsigned char *event_rec)
{
  int             voice, cmd;
  unsigned short  p1, p2;
  unsigned int    plong;

  cmd = event_rec[2];
  voice = event_rec[3];
  p1 = *(unsigned short *) &event_rec[4];
  p2 = *(unsigned short *) &event_rec[6];
  plong = *(unsigned int *) &event_rec[4];

  switch (cmd)
    {

    case _GUS_NUMVOICES:
      set_max_voices (p1);
      break;


    default:;
    }
}

static int
softsyn_load_patch (int dev, int format, const char *addr,
		    int offs, int count, int pmgr_flag)
{
  struct patch_info *patch = NULL;

  int             i, p, instr;
  long            sizeof_patch;
  int             memlen, adj;
  unsigned short  data;
  short          *wave = NULL;

  sizeof_patch = (long) &patch->data[0] - (long) patch;		/* Header size */

  if (format != GUS_PATCH)
    {
      printk ("SoftOSS: Invalid patch format (key) 0x%x\n", format);
      return -EINVAL;
    }

  if (count < sizeof_patch)
    {
      printk ("SoftOSS: Patch header too short\n");
      return -EINVAL;
    }

  count -= sizeof_patch;

  if (devc->nrsamples >= MAX_SAMPLE)
    {
      printk ("SoftOSS: Sample table full\n");
      return -ENOSPC;
    }

  /*
   * Copy the header from user space but ignore the first bytes which have
   * been transferred already.
   */

  patch = vmalloc (sizeof (*patch));

  if (patch == NULL)
    {
      printk ("SoftOSS: Out of memory\n");
      return -ENOSPC;
    }

  copy_from_user (&((char *) patch)[offs], &(addr)[offs], sizeof_patch - offs);

  if (patch->mode & WAVE_ROM)
    {
      vfree (patch);
      return -EINVAL;
    }

  instr = patch->instr_no;

  if (instr < 0 || instr > MAX_PATCH)
    {
      printk ("SoftOSS: Invalid patch number %d\n", instr);
      vfree (patch);
      return -EINVAL;
    }

  if (count < patch->len)
    {
      printk ("SoftOSS: Patch record too short (%d<%d)\n", count, (int) patch->len);
      patch->len = count;
    }

  if (patch->len <= 0 || patch->len > (devc->ram_size - devc->ram_used))
    {
      printk ("SoftOSS: Invalid sample length %d\n", (int) patch->len);
      vfree (patch);
      return -EINVAL;
    }

  if (patch->mode & WAVE_LOOPING)
    {
      if (patch->loop_start < 0 || patch->loop_start >= patch->len)
	{
	  printk ("SoftOSS: Invalid loop start %d\n", patch->loop_start);
	  vfree (patch);
	  return -EINVAL;
	}

      if (patch->loop_end < patch->loop_start || patch->loop_end > patch->len)
	{
	  printk ("SoftOSS: Invalid loop start or end point (%d, %d)\n", patch->loop_start, patch->loop_end);
	  vfree (patch);
	  return -EINVAL;
	}
    }

/* 
 * Next load the wave data to memory
 */

  memlen = patch->len;
  adj = 1;

  if (!(patch->mode & WAVE_16_BITS))
    memlen *= 2;
  else
    adj = 2;

  wave = vmalloc (memlen);

  if (wave == NULL)
    {
      printk ("SoftOSS: Can't allocate %d bytes of mem for a sample\n", memlen);
      vfree (patch);
      return -ENOSPC;
    }

  p = 0;
  for (i = 0; i < memlen / 2; i++)	/* Handle words */
    {
      unsigned char   tmp;

      data = 0;

      if (patch->mode & WAVE_16_BITS)
	{
	  get_user (*(unsigned char *) &tmp, (unsigned char *) &((addr)[sizeof_patch + p++]));	/* Get lsb */
	  data = tmp;
	  get_user (*(unsigned char *) &tmp, (unsigned char *) &((addr)[sizeof_patch + p++]));	/* Get msb */
	  if (patch->mode & WAVE_UNSIGNED)
	    tmp ^= 0x80;	/* Convert to signed */
	  data |= (tmp << 8);
	}
      else
	{
	  get_user (*(unsigned char *) &tmp, (unsigned char *) &((addr)[sizeof_patch + p++]));
	  if (patch->mode & WAVE_UNSIGNED)
	    tmp ^= 0x80;	/* Convert to signed */
	  data = (tmp << 8);	/* Convert to 16 bits */
	}

      wave[i] = (short) data;
    }

  devc->ram_used += patch->len;
/*
 * Convert pointers to 16 bit indexes
 */
  patch->len /= adj;
  patch->loop_start /= adj;
  patch->loop_end /= adj;

/*
 * Finally link the loaded patch to the chain
 */

  patch->key = devc->programs[instr];
  devc->programs[instr] = devc->nrsamples;
  devc->wave[devc->nrsamples] = (short *) wave;
  devc->samples[devc->nrsamples++] = patch;

  return 0;
}

static void
softsyn_panning (int dev, int voice, int pan)
{
  if (voice < 0 || voice > devc->maxvoice)
    return;

  if (pan < -128)
    pan = -128;
  if (pan > 127)
    pan = 127;

  softoss_voices[voice].panning = pan;
  if (voice_active[voice])
    update_volume (voice);
}

static void
softsyn_volume_method (int dev, int mode)
{
}

static void
softsyn_aftertouch (int dev, int voice, int pressure)
{
  if (voice < 0 || voice > devc->maxvoice)
    return;

  if (voice_active[voice])
    update_volume (voice);
}

static void
softsyn_controller (int dev, int voice, int ctrl_num, int value)
{
  unsigned long   flags;

  if (voice < 0 || voice > devc->maxvoice)
    return;
  save_flags (flags);
  cli ();

  switch (ctrl_num)
    {
    case CTRL_PITCH_BENDER:
      softoss_voices[voice].bender = value;

      if (voice_active[voice])
	compute_step (voice);	/* Update pitch */
      break;


    case CTRL_PITCH_BENDER_RANGE:
      softoss_voices[voice].bender_range = value;
      break;
    case CTL_EXPRESSION:
      value /= 128;
    case CTRL_EXPRESSION:
      softoss_voices[voice].expression_vol = value;
      if (voice_active[voice])
	update_volume (voice);
      break;

    case CTL_PAN:
      softsyn_panning (dev, voice, (value * 2) - 128);
      break;

    case CTL_MAIN_VOLUME:
      value = (value * 100) / 16383;

    case CTRL_MAIN_VOLUME:
      softoss_voices[voice].main_vol = value;
      if (voice_active[voice])
	update_volume (voice);
      break;

    default:
      break;
    }

  restore_flags (flags);
}

static void
softsyn_bender (int dev, int voice, int value)
{
  if (voice < 0 || voice > devc->maxvoice)
    return;

  softoss_voices[voice].bender = value - 8192;
  if (voice_active[voice])
    compute_step (voice);	/* Update pitch */
}

static int
softsyn_alloc_voice (int dev, int chn, int note, struct voice_alloc_info *alloc)
{
  int             i, p, best = -1, best_time = 0x7fffffff;

  p = alloc->ptr;
  /*
     * First look for a completely stopped voice
   */

  for (i = 0; i < alloc->max_voice; i++)
    {
      if (alloc->map[p] == 0)
	{
	  alloc->ptr = p;
	  voice_active[p] = 0;
	  return p;
	}
      if (alloc->alloc_times[p] < best_time)
	{
	  best = p;
	  best_time = alloc->alloc_times[p];
	}
      p = (p + 1) % alloc->max_voice;
    }

  /*
     * Then look for a releasing voice
   */

  for (i = 0; i < alloc->max_voice; i++)
    {
      if (alloc->map[p] == 0xffff)
	{
	  alloc->ptr = p;
	  voice_active[p] = 0;
	  return p;
	}
      p = (p + 1) % alloc->max_voice;
    }

  if (best >= 0)
    p = best;

  alloc->ptr = p;
  voice_active[p] = 0;
  return p;
}

static void
softsyn_setup_voice (int dev, int voice, int chn)
{
  unsigned long   flags;

  struct channel_info *info =
  &synth_devs[dev]->chn_info[chn];

  save_flags (flags);
  cli ();
  /* init_voice(devc, voice); */
  softsyn_set_instr (dev, voice, info->pgm_num);

  softoss_voices[voice].expression_vol =
    info->controllers[CTL_EXPRESSION];	/* Just MSB */
  softoss_voices[voice].main_vol =
    (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128;
  softsyn_panning (dev, voice, (info->controllers[CTL_PAN] * 2) - 128);
  softoss_voices[voice].bender = 0;	/* info->bender_value; */
  softoss_voices[voice].bender_range = info->bender_range;

  if (chn == 9)
    softoss_voices[voice].percussive_voice = 1;
  restore_flags (flags);
}

static void
softsyn_reset (int devno)
{
  int             i;
  unsigned long   flags;

  save_flags (flags);
  cli ();

  for (i = 0; i < devc->maxvoice; i++)
    init_voice (devc, i);
  restore_flags (flags);
}

static struct synth_operations softsyn_operations =
{
  "SoftOSS",
  &softsyn_info,
  0,
  SYNTH_TYPE_SAMPLE,
  0,
  softsyn_open,
  softsyn_close,
  softsyn_ioctl,
  softsyn_kill_note,
  softsyn_start_note,
  softsyn_set_instr,
  softsyn_reset,
  softsyn_hw_control,
  softsyn_load_patch,
  softsyn_aftertouch,
  softsyn_controller,
  softsyn_panning,
  softsyn_volume_method,
  softsyn_bender,
  softsyn_alloc_voice,
  softsyn_setup_voice
};

/*
 * Timer stuff (for /dev/music).
 */

static unsigned int
soft_tmr_start (int dev, unsigned int usecs)
{
  tmr_running = 1;
  start_engine (devc);
  return devc->usecs_per_frag;
}

static void
soft_tmr_disable (int dev)
{
  stop_engine (devc);
  tmr_running = 0;
}

static void
soft_tmr_restart (int dev)
{
  tmr_running = 1;
}

static struct sound_lowlev_timer soft_tmr =
{
  0,
  9999,
  soft_tmr_start,
  soft_tmr_disable,
  soft_tmr_restart
};

int
probe_softsyn (struct address_info *hw_config)
{
  int             i;

  if (softsynth_loaded)
    return 0;

  devc->ram_size = 8 * 1024 * 1024;
  devc->ram_used = 0;
  devc->nrsamples = 0;
  for (i = 0; i < MAX_PATCH; i++)
    {
      devc->programs[i] = NO_SAMPLE;
      devc->wave[i] = NULL;
    }

  devc->maxvoice = DEFAULT_VOICES;

  devc->audiodev = 0;
  devc->audio_opened = 0;
  devc->channels = 2;
  devc->bits = 16;
  devc->max_playahead = 32;

#ifdef SOFTOSS_RATE
  devc->speed = SOFTOSS_RATE;
#else
  devc->speed = 32000;
#endif

#ifdef SOFTOSS_VOICES
  devc->default_max_voices = SOFTOSS_VOICES;
#else
  devc->default_max_voices = 32;
#endif

  softsynth_loaded = 1;
  return 1;
}

void
attach_softsyn_card (struct address_info *hw_config)
{

  voice_alloc = &softsyn_operations.alloc;
  synth_devs[num_synths++] = &softsyn_operations;
  sequencer_init ();
  sound_timer_init (&soft_tmr, "SoftOSS");
  devc->synthdev = num_synths;
  softsynthp = softsynth_hook;

#ifndef POLLED_MODE
#endif
}

void
unload_softsyn (struct address_info *hw_config)
{
  if (!softsynth_loaded)
    return;
#ifndef POLLED_MODE
#endif

  softsynthp = NULL;
  reset_samples (devc);
}

#endif
