/* 
 * 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.
 *
 *
 * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
 */
#include <linux/config.h>
#include <linux/module.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"
#include "soundmodule.h"

#if defined(CONFIG_SOFTOSS) || defined(MODULE)
#include "softoss.h"
#include <linux/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;	/* used in softoss_rs.c */

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(KERN_WARNING "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(KERN_WARNING "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(KERN_WARNING "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(KERN_WARNING "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 = jiffies+1;
		  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;
	int trig, n;
	mm_segment_t fs;

	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) 
	{
		n = trig = 0;
		fs = get_fs();
		set_fs(get_ds());
		dma_ioctl(devc->audiodev, SNDCTL_DSP_SETTRIGGER, (caddr_t)&trig);
#ifdef POLLED_MODE
		poll_timer.expires = jiffies+1;
		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(KERN_ERR "SoftOSS: Trigger failed\n");
		set_fs(fs);
	}
}

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(KERN_WARNING "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;
			if (copy_to_user(arg, &softsyn_info, sizeof(softsyn_info)))
				return -EFAULT;
			return 0;

		case SNDCTL_SEQ_RESETSAMPLES:
			stop_engine(devc);
			reset_samples(devc);
			return 0;
		  
		case SNDCTL_SYNTH_MEMAVL:
			return devc->ram_size - devc->ram_used;

		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(KERN_ERR "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(KERN_WARNING "SoftOSS: Undefined MIDI instrument %d\n", instr);
		restore_flags(flags);
		return 0;
	}
	instr = devc->programs[instr];

	if (instr < 0 || instr >= devc->nrsamples)
	{
		printk(KERN_WARNING "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(KERN_WARNING "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 */
	mm_segment_t fs;

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

	softsynth_disabled = 0;
	devc->finfo.f_mode = FMODE_WRITE;
	devc->finfo.f_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(KERN_ERR "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));
	fs = get_fs();
	set_fs(get_ds());
	dma_ioctl(devc->audiodev, SNDCTL_DSP_SETFRAGMENT, (caddr_t) & frags);
	dma_ioctl(devc->audiodev, SNDCTL_DSP_GETBLKSIZE, (caddr_t) & devc->fragsize);
	set_fs(fs);

	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 -EINVAL;
	}
	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)
{
	mm_segment_t fs;

	devc->engine_state = ES_STOPPED;
#ifdef POLLED_MODE
	del_timer(&poll_timer);
#endif
	fs = get_fs();
	set_fs(get_ds());
	dma_ioctl(devc->audiodev, SNDCTL_DSP_RESET, 0);
	set_fs(fs);
	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(KERN_ERR "SoftOSS: Invalid patch format (key) 0x%x\n", format);*/
		return -EINVAL;
	}
	if (count < sizeof_patch)
	{
/*		printk(KERN_ERR "SoftOSS: Patch header too short\n");*/
		return -EINVAL;
	}
	count -= sizeof_patch;

	if (devc->nrsamples >= MAX_SAMPLE)
	{
/*		  printk(KERN_ERR "SoftOSS: Sample table full\n");*/
		  return -ENOBUFS;
	}
	
	/*
	 * Copy the header from user space but ignore the first bytes which have
	 * been transferred already.
	 */

	patch = vmalloc(sizeof(*patch));

	if (patch == NULL)
	{
/*		printk(KERN_ERR "SoftOSS: Out of memory\n");*/
		return -ENOMEM;
	}
	if(copy_from_user(&((char *) patch)[offs], &(addr)[offs], sizeof_patch - offs))
		return -EFAULT;

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

	if (instr < 0 || instr > MAX_PATCH)
	{
/*		printk(KERN_ERR "SoftOSS: Invalid patch number %d\n", instr);*/
		vfree(patch);
		return -EINVAL;
	}
	if (count < patch->len)
	{
/*		printk(KERN_ERR "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(KERN_ERR "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(KERN_ERR "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(KERN_ERR "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(KERN_ERR "SoftOSS: Can't allocate %d bytes of mem for a sample\n", memlen);*/
		vfree(patch);
		return -ENOMEM;
	}
	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 CONFIG_SOFTOSS_RATE
	devc->speed = CONFIG_SOFTOSS_RATE;
#else
	devc->speed = 32000;
#endif

#ifdef CONFIG_SOFTOSS_VOICES
	devc->default_max_voices = CONFIG_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[devc->synthdev = num_synths++] = &softsyn_operations;
	sequencer_init();
	sound_timer_init(&soft_tmr, "SoftOSS");
	devc->timerdev = num_sound_timers;
	softsynthp = softsynth_hook;

#ifndef POLLED_MODE
#endif
}

void unload_softsyn(struct address_info *hw_config)
{
	if (!softsynth_loaded)
		return;
	softsynthp = NULL;
	softsynth_loaded = 0;
	reset_samples(devc);
}

#ifdef MODULE

static struct address_info config;

int init_module(void)
{
	printk(KERN_INFO "SoftOSS driver Copyright (C) by Hannu Savolainen 1993-1997\n");
	if (!probe_softsyn(&config))
		return -ENODEV;
	attach_softsyn_card(&config);
	SOUND_LOCK;
	return 0;
}

void cleanup_module(void)
{
	unload_softsyn(&config);
	sound_unload_synthdev(devc->synthdev);
	sound_unload_timerdev(devc->timerdev);
	SOUND_LOCK_END;
}
#endif
#endif
