patch-2.1.28 linux/drivers/sound/lowlevel/awe_wave.c

Next file: linux/drivers/sound/lowlevel/init.c
Previous file: linux/drivers/sound/lowlevel/awe_voice.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.27/linux/drivers/sound/lowlevel/awe_wave.c linux/drivers/sound/lowlevel/awe_wave.c
@@ -1,58 +1,54 @@
-/*================================================================
- * awe_wave.c -- driver for AWE32 wave table synth
- *	version 0.2.0; Oct. 16, 1996
- *	copyright (c) 1996  by Takashi Iwai
- *================================================================*/
-
-/* if you're using obsolete VoxWare 3.0.x on Linux 1.2.x (or FreeBSD),
- * uncomment the following line
+/*
+ * sound/awe_wave.c
+ *
+ * The low level driver for the AWE32/Sound Blaster 32 wave table synth.
+ *   version 0.3.1b; Jan. 21, 1997
+ *
+ * Copyright (C) 1996,1997 Takashi Iwai
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-/* #define AWE_OBSOLETE_VOXWARE */
 
-#include "lowlevel.h"
-
-#ifdef AWE_OBSOLETE_VOXWARE
-
-#include "sound_config.h"
-#if !defined(EXCLUDE_AWE32)
-#define CONFIG_AWE32_SYNTH
+#define AWEDRV_VERSION		"0.3.1b"
+#ifdef __FreeBSD__
+#  include <i386/isa/sound/awe_config.h>
+#else
+#  include "awe_config.h"
 #endif
 
-#else /* AWE_OBSOLETE_VOXWARE */
-
-#include "../sound_config.h"
-
-#endif /* AWE_OBSOLETE_VOXWARE */
-
-
-/*----------------------------------------------------------------*
- * compile condition
- *----------------------------------------------------------------*/
-
-/* initialize FM passthrough even without extended RAM */
-/*#define AWE_ALWAYS_INIT_FM*/
-
-/* debug on */
-#define AWE_DEBUG_ON
-
-/* verify checksum for uploading samples */
-#define AWE_CHECKSUM_DATA
-#define AWE_CHECKSUM_MEMORY
-
-/* disable interruption during sequencer operation */
-/*#define AWE_NEED_DISABLE_INTR*/
-
 /*----------------------------------------------------------------*/
 
 #ifdef CONFIG_AWE32_SYNTH
 
-#include "awe_hw.h"
-#include "awe_voice.h"
+#ifdef __FreeBSD__
+#  include <i386/isa/sound/awe_hw.h>
+#  include <i386/isa/sound/awe_voice.h>
+#else
+#  include "awe_hw.h"
+#  include <linux/awe_voice.h>
+#endif
 
 #ifdef AWE_OBSOLETE_VOXWARE
-#include "tuning.h"
+#  ifdef __FreeBSD__
+#    define SEQUENCER_C
+#    include <i386/isa/sound/tuning.h>
+#  else
+#    include "tuning.h"
+#  endif
 #else
-#include "../tuning.h"
+#  include "../tuning.h"
 #endif
 
 #ifdef linux
@@ -66,8 +62,8 @@
  * debug message
  *----------------------------------------------------------------*/
 
-#ifdef AWE_DEBUG_ON
 static int debug_mode = 0;
+#ifdef AWE_DEBUG_ON
 #define DEBUG(LVL,XXX)	{if (debug_mode > LVL) { XXX; }}
 #define ERRMSG(XXX)	{if (debug_mode) { XXX; }}
 #define FATALERR(XXX)	XXX
@@ -95,6 +91,7 @@
 
 #define AWE_MAX_PRESETS		256
 #define AWE_DEFAULT_BANK	0
+#define AWE_DRUM_BANK		128
 
 /* preset table index */
 static awe_voice_list *preset_table[AWE_MAX_PRESETS];
@@ -103,35 +100,68 @@
  * voice table
  *----------------------------------------------------------------*/
 
-#define AWE_FX_BYTES	((AWE_FX_END+7)/8)
+/* effects table */
+#define AWE_FX_NBYTES	((AWE_FX_END+7)/8)
+typedef	struct FX_Rec { /* channel effects */
+	unsigned char flags[AWE_FX_NBYTES];
+	short val[AWE_FX_END];
+} FX_Rec;
 
-typedef struct _voice_info {
-	int state;		/* status (on = 1, off = 0) */
-	int note;		/* midi key (0-127) */
-	int velocity;		/* midi velocity (0-127) */
+
+/* channel parameters */
+typedef struct _awe_chan_info {
+	int bank;		/* current tone bank */
+	int instr;		/* current program */
 	int bender;		/* midi pitchbend (-8192 - 8192) */
 	int bender_range;	/* midi bender range (x100) */
 	int panning;		/* panning (0-127) */
 	int main_vol;		/* channel volume (0-127) */
 	int expression_vol;	/* midi expression (0-127) */
+	awe_voice_list *vrec;	/* instrument list */
+	awe_voice_list *def_vrec; /* default instrument list */
+	FX_Rec fx;		/* effects */
+	int sustained;		/* sustain status in MIDI */
+} awe_chan_info;
+
+/* voice parameters */
+typedef struct _voice_info {
+	int state;
+#define AWE_ST_OFF		0	/* no sound */
+#define AWE_ST_ON		1	/* playing */
+#define AWE_ST_STANDBY		2	/* stand by for playing */
+#define AWE_ST_SUSTAINED	3	/* sustained */
+#define AWE_ST_MARK		4	/* marked for allocation */
+
+	int ch;			/* midi channel */
+	int key;		/* internal key for search */
+	int time;		/* allocated time */
+	awe_chan_info	*cinfo;	/* channel info */
+
+	int note;		/* midi key (0-127) */
+	int velocity;		/* midi velocity (0-127) */
+	awe_voice_info *sample;	/* assigned voice */
 
 	/* EMU8000 parameters */
 	int apitch;		/* pitch parameter */
 	int avol;		/* volume parameter */
-
-	/* instrument parameters */
-	int bank;		/* current tone bank */
-	int instr;		/* current program */
-	awe_voice_list *vrec;
-	awe_voice_info *sample;
-
-	/* channel effects */
-	unsigned char fx_flags[AWE_FX_BYTES];
-	short fx[AWE_FX_END];
+	int apan;		/* panning parameter */
 } voice_info;
 
+/* voice information */
 static voice_info voices[AWE_MAX_VOICES];
 
+#define IS_NO_SOUND(v)	(voices[v].state == AWE_ST_OFF || voices[v].state == AWE_ST_STANDBY)
+#define IS_NO_EFFECT(v)	(voices[v].state != AWE_ST_ON)
+#define IS_PLAYING(v)	(!IS_NO_SOUND(v))
+
+
+/* MIDI channel effects information (for hw control) */
+#if AWE_MAX_CHANNELS < AWE_MAX_VOICES
+static awe_chan_info channels[AWE_MAX_VOICES];
+#else
+static awe_chan_info channels[AWE_MAX_CHANNELS];
+#endif
+
 
 /*----------------------------------------------------------------
  * global variables
@@ -155,12 +185,21 @@
 
 static int reverb_mode = 0;		/* reverb mode */
 static int chorus_mode = 0;		/* chorus mode */
-static unsigned short init_atten = 32;  /* 12dB */
+static unsigned short init_atten = AWE_DEFAULT_ATTENUATION; /* 12dB below */
+
+static int awe_present = FALSE;		/* awe device present? */
+static int awe_busy = FALSE;		/* awe device opened? */
+
+#define DEFAULT_DRUM_FLAGS	(1 << 9)
+#define IS_DRUM_CHANNEL(c)	(drum_flags & (1 << (c)))
+static unsigned long drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */
+
+static int awe_channel_mode = 0;		/* channel control mode */
 
-static int awe_present = 0;		/* awe device present? */
-static int awe_busy = 0;		/* awe device opened? */
+static int current_alloc_time = 0;		/* voice allocation time */
 
 static int awe_gus_bank = AWE_DEFAULT_BANK;	/* GUS default bank number */
+static int awe_exclusive_sound = TRUE;		/* exclusive sound on */
 
 
 static struct synth_info awe_info = {
@@ -202,6 +241,7 @@
 /* set voice parameters */
 static void awe_init_voice_info(awe_voice_info *vp);
 static void awe_init_voice_parm(awe_voice_parm *pp);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
 static int freq_to_note(int freq);
 static int calc_rate_offset(int Hz);
 /*static int calc_parm_delay(int msec);*/
@@ -209,26 +249,31 @@
 static int calc_parm_attack(int msec);
 static int calc_parm_decay(int msec);
 static int calc_parm_search(int msec, short *table);
+#endif
 
 /* turn on/off note */
 static void awe_note_on(int voice);
 static void awe_note_off(int voice);
 static void awe_terminate(int voice);
-static void awe_exclusive_off(int voice);
+static void awe_exclusive_off(int voice, int exclass);
 
 /* calculate voice parameters */
-static void awe_set_pitch(int voice);
-static void awe_set_volume(int voice);
+typedef void (*fx_affect_func)(int voice, int forced);
+static void awe_set_pitch(int voice, int forced);
+static void awe_set_voice_pitch(int voice, int forced);
+static void awe_set_volume(int voice, int forced);
+static void awe_set_voice_vol(int voice, int forced);
 static void awe_set_pan(int voice, int forced);
-static void awe_fx_fmmod(int voice);
-static void awe_fx_tremfrq(int voice);
-static void awe_fx_fm2frq2(int voice);
-static void awe_fx_cutoff(int voice);
-static void awe_fx_initpitch(int voice);
+static void awe_fx_fmmod(int voice, int forced);
+static void awe_fx_tremfrq(int voice, int forced);
+static void awe_fx_fm2frq2(int voice, int forced);
+static void awe_fx_cutoff(int voice, int forced);
 static void awe_calc_pitch(int voice);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
 static void awe_calc_pitch_from_freq(int voice, int freq);
+#endif
 static void awe_calc_volume(int voice);
-static void awe_voice_init(int voice, int inst_only);
+static void awe_voice_init(int voice);
 
 /* sequencer interface */
 static int awe_open(int dev, int mode);
@@ -237,6 +282,7 @@
 static int awe_kill_note(int dev, int voice, int note, int velocity);
 static int awe_start_note(int dev, int v, int note_num, int volume);
 static int awe_set_instr(int dev, int voice, int instr_no);
+static int awe_set_instr_2(int dev, int voice, int instr_no);
 static void awe_reset(int dev);
 static void awe_hw_control(int dev, unsigned char *event);
 static int awe_load_patch(int dev, int format, const char *addr,
@@ -245,23 +291,35 @@
 static void awe_controller(int dev, int voice, int ctrl_num, int value);
 static void awe_panning(int dev, int voice, int value);
 static void awe_volume_method(int dev, int mode);
+#ifndef AWE_NO_PATCHMGR
+static int awe_patchmgr(int dev, struct patmgr_info *rec);
+#endif
 static void awe_bender(int dev, int voice, int value);
 static int awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc);
 static void awe_setup_voice(int dev, int voice, int chn);
 
 /* hardware controls */
+#ifdef AWE_HAS_GUS_COMPATIBILITY
 static void awe_hw_gus_control(int dev, int cmd, unsigned char *event);
+#endif
 static void awe_hw_awe_control(int dev, int cmd, unsigned char *event);
+static void awe_voice_change(int voice, fx_affect_func func);
+static void awe_sustain_off(int voice, int forced);
 
 /* voice search */
-static awe_voice_info *awe_search_voice(int voice, int note);
 static awe_voice_list *awe_search_instr(int bank, int preset);
+static int awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_info **vlist);
+static void awe_alloc_multi_voices(int ch, int note, int velocity);
+static void awe_alloc_one_voice(int voice, int note, int velocity);
+static int awe_clear_voice(void);
 
 /* load / remove patches */
 static void awe_check_loaded(void);
 static int awe_load_info(awe_patch_info *patch, const char *addr);
 static int awe_load_data(awe_patch_info *patch, const char *addr);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
 static int awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag);
+#endif
 static int awe_write_wave_data(const char *addr, long offset, int size);
 static awe_voice_list *awe_get_removed_list(awe_voice_list *curp);
 static void awe_remove_samples(void);
@@ -272,6 +330,7 @@
 static void awe_init_dma(void);
 static void awe_init_array(void);
 static void awe_send_array(unsigned short *data);
+static void awe_tweak_voice(int voice);
 static void awe_tweak(void);
 static void awe_init_fm(void);
 static int awe_open_dram_for_write(int offset);
@@ -280,6 +339,7 @@
 static void awe_close_dram(void);
 static void awe_close_dram_for_read(void);
 static void awe_write_dram(unsigned short c);
+static int awe_detect_base(int addr);
 static int awe_detect(void);
 static int awe_check_dram(void);
 static void awe_set_chorus_mode(int mode);
@@ -302,17 +362,6 @@
 #endif /* AWE_OBSOLETE_VOXWARE */
 
 
-#ifdef AWE_NEED_DISABLE_INTR
-#define DECL_INTR_FLAGS(x)	unsigned long x
-#else
-#undef DISABLE_INTR
-#undef RESTORE_INTR
-#define DECL_INTR_FLAGS(x) /**/
-#define DISABLE_INTR(x) /**/
-#define RESTORE_INTR(x) /**/
-#endif
-
-
 /* macros for Linux and FreeBSD compatibility */
 
 #undef OUTW
@@ -324,6 +373,8 @@
 #ifdef linux
 #  define NO_DATA_ERR                 ENODATA
 #  define OUTW(data, addr)            outw(data, addr)
+
+#ifdef AWE_NEW_KERNEL_INTERFACE
 #  define COPY_FROM_USER(target, source, offs, count) \
               copy_from_user( ((caddr_t)(target)),(source)+(offs),(count) )
 #  define GET_BYTE_FROM_USER(target, addr, offs)      \
@@ -332,8 +383,22 @@
 		get_user(target, (unsigned short*)&((addr)[offs]))
 #  define IOCTL_TO_USER(target, offs, source, count)  \
               copy_to_user  ( ((caddr_t)(target)),(source)+(offs),(count) )
+#else /* AWE_NEW_KERNEL_INTERFACE */
+#  define COPY_FROM_USER(target, source, offs, count) \
+              memcpy_fromfs( ((caddr_t)(target)),(source)+(offs),(count) )
+#  define GET_BYTE_FROM_USER(target, addr, offs)      \
+              *((char  *)&(target)) = get_fs_byte( (addr)+(offs) )
+#  define GET_SHORT_FROM_USER(target, addr, offs)     \
+              *((short *)&(target)) = get_fs_word( (addr)+(offs) )
+#  define IOCTL_TO_USER(target, offs, source, count)  \
+              memcpy_tofs  ( ((caddr_t)(target)),(source)+(offs),(count) )
+#endif /* AWE_NEW_KERNEL_INTERFACE */
+
 #  define BZERO(target,len)                           \
               memset( (caddr_t)target, '\0', len )
+#  define MEMCPY(dst,src,len) \
+              memcpy((caddr_t)dst, (caddr_t)src, len)
+
 #elif defined(__FreeBSD__)
 #  define NO_DATA_ERR                 EINVAL
 #  define OUTW(data, addr)            outw(addr, data)
@@ -347,6 +412,8 @@
               memcpy( &((target)[offs]), (source), (count) )
 #  define BZERO(target,len)                           \
               bzero( (caddr_t)target, len )
+#  define MEMCPY(dst,src,len) \
+              bcopy((caddr_t)src, (caddr_t)dst, len)
 #endif
 
 
@@ -356,6 +423,7 @@
 
 static struct synth_operations awe_operations =
 {
+	"EMU8K",
 	&awe_info,
 	0,
 	SYNTH_TYPE_SAMPLE,
@@ -365,7 +433,7 @@
 	awe_ioctl,
 	awe_kill_note,
 	awe_start_note,
-	awe_set_instr,
+	awe_set_instr_2,
 	awe_reset,
 	awe_hw_control,
 	awe_load_patch,
@@ -373,6 +441,9 @@
 	awe_controller,
 	awe_panning,
 	awe_volume_method,
+#ifndef AWE_NO_PATCHMGR
+	awe_patchmgr,
+#endif
 	awe_bender,
 	awe_alloc,
 	awe_setup_voice
@@ -404,7 +475,7 @@
 
 	/* allocate sample tables */
 	PERMANENT_MALLOC(awe_sample_info *, samples,
-			 AWE_MAX_SAMPLES * sizeof(awe_sample_info), mem_start);
+			 AWE_MAX_SAMPLES * AWE_SAMPLE_INFO_SIZE, mem_start);
 	PERMANENT_MALLOC(awe_voice_list *, infos,
 			 AWE_MAX_INFOS * sizeof(awe_voice_list), mem_start);
 	if (samples == NULL || infos == NULL) {
@@ -429,14 +500,21 @@
 	/* intialize AWE32 hardware */
 	awe_initialize();
 
-	printk("<AWE32 SynthCard (%dk)>\n", (int)awe_mem_size/1024);
-	sprintf(awe_info.name, "AWE32 Synth (%dk)", (int)awe_mem_size/1024);
+#ifndef __FreeBSD__
+	printk("AWE32 Sound Driver v%s (DRAM %dk)\n",
+	       AWEDRV_VERSION, (int)awe_mem_size/1024);
+#else
+	DEBUG(0,printk("AWE32 Sound Driver v%s (DRAM %dk)\n",
+                   AWEDRV_VERSION, (int)awe_mem_size/1024));
+#endif
+	sprintf(awe_info.name, "AWE32 Driver v%s (DRAM %dk)",
+		AWEDRV_VERSION, (int)awe_mem_size/1024);
 
 	/* set reverb & chorus modes */
 	awe_set_reverb_mode(reverb_mode);
 	awe_set_chorus_mode(chorus_mode);
 
-	awe_present = 1;
+	awe_present = TRUE;
 
 #ifdef AWE_OBSOLETE_VOXWARE
 	return mem_start;
@@ -491,11 +569,13 @@
  *================================================================*/
 
 /* select a given AWE32 pointer */
-#define awe_set_cmd(cmd)	OUTW(cmd, awe_base + 0x802)
+static int awe_cur_cmd = -1;
+#define awe_set_cmd(cmd) \
+if (awe_cur_cmd != cmd) { OUTW(cmd, awe_base + 0x802); awe_cur_cmd = cmd; }
 #define awe_port(port)		(awe_base - 0x620 + port)
 
 /* write 16bit data */
-static void
+INLINE static void
 awe_poke(unsigned short cmd, unsigned short port, unsigned short data)
 {
 	awe_set_cmd(cmd);
@@ -503,7 +583,7 @@
 }
 
 /* write 32bit data */
-static void
+INLINE static void
 awe_poke_dw(unsigned short cmd, unsigned short port, unsigned long data)
 {
 	awe_set_cmd(cmd);
@@ -512,7 +592,7 @@
 }
 
 /* read 16bit data */
-static unsigned short
+INLINE static unsigned short
 awe_peek(unsigned short cmd, unsigned short port)
 {
 	unsigned short k;
@@ -522,7 +602,7 @@
 }
 
 /* read 32bit data */
-static unsigned long
+INLINE static unsigned long
 awe_peek_dw(unsigned short cmd, unsigned short port)
 {
 	unsigned long k1, k2;
@@ -567,25 +647,25 @@
 static int
 awe_check_port(void)
 {
-	return (check_region(awe_port(Data0), 3) ||
-		check_region(awe_port(Data1), 3) ||
-		check_region(awe_port(Data3), 3));
+	return (check_region(awe_port(Data0), 4) ||
+		check_region(awe_port(Data1), 4) ||
+		check_region(awe_port(Data3), 4));
 }
 
 static void
 awe_request_region(void)
 {
-	request_region(awe_port(Data0), 3, "sound driver (AWE32)");
-	request_region(awe_port(Data1), 3, "sound driver (AWE32)");
-	request_region(awe_port(Data3), 3, "sound driver (AWE32)");
+	request_region(awe_port(Data0), 4, "sound driver (AWE32)");
+	request_region(awe_port(Data1), 4, "sound driver (AWE32)");
+	request_region(awe_port(Data3), 4, "sound driver (AWE32)");
 }
 
 static void
 awe_release_region(void)
 {
-	release_region(awe_port(Data0), 3);
-	release_region(awe_port(Data1), 3);
-	release_region(awe_port(Data3), 3);
+	release_region(awe_port(Data0), 4);
+	release_region(awe_port(Data1), 4);
+	release_region(awe_port(Data3), 4);
 }
 
 #endif /* !AWE_OBSOLETE_VOXWARE */
@@ -597,30 +677,22 @@
 static void
 awe_initialize(void)
 {
-	unsigned short data;
-	DECL_INTR_FLAGS(flags);
-
 	DEBUG(0,printk("AWE32: initializing..\n"));
-	DISABLE_INTR(flags);
-
-	/* check for an error condition */
-	data = awe_peek(AWE_U1);
-	if (!(data & 0x000F) == 0x000C) {
-		FATALERR(printk("AWE32: can't initialize AWE32\n"));
-	}
 
 	/* initialize hardware configuration */
 	awe_poke(AWE_HWCF1, 0x0059);
 	awe_poke(AWE_HWCF2, 0x0020);
 
-	/* disable audio output */
-	awe_poke(AWE_HWCF3, 0x0000);
+	/* disable audio; this seems to reduce a clicking noise a bit.. */
+	awe_poke(AWE_HWCF3, 0);
 
 	/* initialize audio channels */
 	awe_init_audio();
 
-	/* initialize init array */
+	/* initialize DMA */
 	awe_init_dma();
+
+	/* initialize init array */
 	awe_init_array();
 
 	/* check DRAM memory size */
@@ -634,13 +706,6 @@
 
 	/* enable audio */
 	awe_poke(AWE_HWCF3, 0x0004);
-
-	data = awe_peek(AWE_HWCF2);
-	if (~data & 0x40) {
-		FATALERR(printk("AWE32: Unable to initialize AWE32.\n"));
-	}
-
-	RESTORE_INTR(flags);
 }
 
 
@@ -720,6 +785,8 @@
 }	
 
 
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+
 /* convert frequency mHz to abstract cents (= midi key * 100) */
 static int
 freq_to_note(int mHz)
@@ -811,27 +878,27 @@
  * convert envelope time parameter to AWE32 raw parameter
  *----------------------------------------------------------------*/
 
-/* attack & decay/release time table (mHz) */
+/* attack & decay/release time table (msec) */
 static short attack_time_tbl[128] = {
-32767, 5939, 3959, 2969, 2375, 1979, 1696, 1484, 1319, 1187, 1079, 989, 913, 848, 791, 742,
+32767, 11878, 5939, 3959, 2969, 2375, 1979, 1696, 1484, 1319, 1187, 1079, 989, 913, 848, 791, 742,
  698, 659, 625, 593, 565, 539, 516, 494, 475, 456, 439, 424, 409, 395, 383, 371,
  359, 344, 330, 316, 302, 290, 277, 266, 255, 244, 233, 224, 214, 205, 196, 188,
  180, 173, 165, 158, 152, 145, 139, 133, 127, 122, 117, 112, 107, 103, 98, 94,
  90, 86, 83, 79, 76, 73, 69, 67, 64, 61, 58, 56, 54, 51, 49, 47,
  45, 43, 41, 39, 38, 36, 35, 33, 32, 30, 29, 28, 27, 25, 24, 23,
  22, 21, 20, 20, 19, 18, 17, 16, 16, 15, 14, 14, 13, 13, 12, 11,
- 11, 10, 10, 10, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 0,
+ 11, 10, 10, 10, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 0,
 };
 
 static short decay_time_tbl[128] = {
-32767, 3651, 3508, 3371, 3239, 3113, 2991, 2874, 2761, 2653, 2550, 2450, 2354, 2262, 2174, 2089,
- 2007, 1928, 1853, 1781, 1711, 1644, 1580, 1518, 1459, 1401, 1347, 1294, 1243, 1195, 1148, 1103,
- 1060, 1018, 979, 940, 904, 868, 834, 802, 770, 740, 711, 683, 657, 631, 606, 582,
- 560, 538, 517, 496, 477, 458, 440, 423, 407, 391, 375, 361, 347, 333, 320, 307,
- 295, 284, 273, 262, 252, 242, 232, 223, 215, 206, 198, 190, 183, 176, 169, 162,
- 156, 150, 144, 138, 133, 128, 123, 118, 113, 109, 104, 100, 96, 93, 89, 85,
- 82, 79, 76, 73, 70, 67, 64, 62, 60, 57, 55, 53, 51, 49, 47, 45,
- 43, 41, 40, 38, 37, 35, 34, 32, 31, 30, 29, 28, 27, 25, 24, 0,
+32767, 32766, 4589, 4400, 4219, 4045, 3879, 3719, 3566, 3419, 3279, 3144, 3014, 2890, 2771, 2657,
+ 2548, 2443, 2343, 2246, 2154, 2065, 1980, 1899, 1820, 1746, 1674, 1605, 1539, 1475, 1415, 1356,
+ 1301, 1247, 1196, 1146, 1099, 1054, 1011, 969, 929, 891, 854, 819, 785, 753, 722, 692,
+ 664, 636, 610, 585, 561, 538, 516, 494, 474, 455, 436, 418, 401, 384, 368, 353,
+ 339, 325, 311, 298, 286, 274, 263, 252, 242, 232, 222, 213, 204, 196, 188, 180,
+ 173, 166, 159, 152, 146, 140, 134, 129, 123, 118, 113, 109, 104, 100, 96, 92,
+ 88, 84, 81, 77, 74, 71, 68, 65, 63, 60, 58, 55, 53, 51, 49, 47,
+ 45, 43, 41, 39, 38, 36, 35, 33, 32, 30, 29, 28, 27, 26, 25, 24,
 };
 
 /*
@@ -842,6 +909,7 @@
 }
 */
 
+/* delay time = 0x8000 - msec/92 */
 static int
 calc_parm_hold(int msec)
 {
@@ -851,22 +919,25 @@
 	return val;
 }
 
+/* attack time: search from time table */
 static int
 calc_parm_attack(int msec)
 {
 	return calc_parm_search(msec, attack_time_tbl);
 }
 
+/* decay/release time: search from time table */
 static int
 calc_parm_decay(int msec)
 {
 	return calc_parm_search(msec, decay_time_tbl);
 }
 
+/* search an index for specified time from given time table */
 static int
 calc_parm_search(int msec, short *table)
 {
-	int left = 0, right = 127, mid;
+	int left = 1, right = 127, mid;
 	while (left < right) {
 		mid = (left + right) / 2;
 		if (msec < (int)table[mid])
@@ -876,6 +947,7 @@
 	}
 	return left;
 }
+#endif /* AWE_HAS_GUS_COMPATIBILITY */
 
 
 /*================================================================
@@ -883,103 +955,53 @@
  *================================================================*/
 
 /* set an effect value */
-#define FX_SET(v,type,value) \
-(voices[v].fx_flags[(type)/8] |= (1<<((type)%8)),\
- voices[v].fx[type] = (value))
-/* check the effect value is set */
-#define FX_ON(v,type)	(voices[v].fx_flags[(type)/8] & (1<<((type)%8)))
-
-#if 0
-#define FX_BYTE(v,type,value)\
-	(FX_ON(v,type) ? (unsigned char)voices[v].fx[type] :\
-	 (unsigned char)(value))
-#define FX_WORD(v,type,value)\
-	(FX_ON(v,type) ? (unsigned short)voices[v].fx[type] :\
-	 (unsigned short)(value))
+#define FX_SET(rec,type,value) \
+	((rec)->flags[(type)/8] |= (1 << ((type) % 8)), \
+	 (rec)->val[type] = (value))
 
-#else
+/* check the effect value is set */
+#define FX_ON(rec,type)	((rec)->flags[(type)/8] & (1<<((type)%8)))
 
 /* get byte effect value */
-static unsigned char FX_BYTE(int v, int type, unsigned char value)
-{
-	unsigned char tmp;
-	if (FX_ON(v,type))
-		tmp = (unsigned char)voices[v].fx[type];
-	else
-		tmp = value;
-	DEBUG(4,printk("AWE32: [-- byte(%d) = %x]\n", type, tmp));
-	return tmp;
-}
-
+#define FX_BYTE(rec,type,value) \
+	(unsigned char)(FX_ON(rec,type) ? (rec)->val[type] : (value))
 /* get word effect value */
-static unsigned short FX_WORD(int v, int type, unsigned short value)
-{
-	unsigned short tmp;
-	if (FX_ON(v,type))
-		tmp = (unsigned short)voices[v].fx[type];
-	else
-		tmp = value;
-	DEBUG(4,printk("AWE32: [-- word(%d) = %x]\n", type, tmp));
-	return tmp;
-}
-
-#endif
+#define FX_WORD(rec,type,value) \
+	(unsigned short)(FX_ON(rec,type) ? (rec)->val[type] : (value))
 
 /* get word (upper=type1/lower=type2) effect value */
-static unsigned short FX_COMB(int v, int type1, int type2, unsigned short value)
+static unsigned short
+FX_COMB(FX_Rec *rec, int type1, int type2, unsigned short value)
 {
 	unsigned short tmp;
-	if (FX_ON(v, type1))
-		tmp = (unsigned short)(voices[v].fx[type1]) << 8;
+	if (FX_ON(rec, type1))
+		tmp = (unsigned short)(rec->val[type1]) << 8;
 	else
 		tmp = value & 0xff00;
-	if (FX_ON(v, type2))
-		tmp |= (unsigned short)(voices[v].fx[type2]) & 0xff;
+	if (FX_ON(rec, type2))
+		tmp |= (unsigned short)(rec->val[type2]) & 0xff;
 	else
 		tmp |= value & 0xff;
-	DEBUG(4,printk("AWE32: [-- comb(%d/%d) = %x]\n", type1, type2, tmp));
 	return tmp;
 }
 
 /* address offset */
 static long
-FX_OFFSET(int voice, int lo, int hi)
+FX_OFFSET(FX_Rec *rec, int lo, int hi, int mode)
 {
-	awe_voice_info *vp;
-	long addr;
-	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
-		return 0;
-
-	addr = 0;
-	if (FX_ON(voice, hi)) {
-		addr = (short)voices[voice].fx[hi];
+	long addr = 0;
+	if (FX_ON(rec, hi)) {
+		addr = (short)rec->val[hi];
 		addr = addr << 15;
 	}
-	if (FX_ON(voice, lo))
-		addr += (short)voices[voice].fx[lo];
-	if (!(vp->mode & (AWE_SAMPLE_8BITS<<6)))
+	if (FX_ON(rec, lo))
+		addr += (short)rec->val[lo];
+	if (!(mode & AWE_SAMPLE_8BITS))
 		addr /= 2;
 	return addr;
 }
 
 
-typedef void (*fx_affect_func)(int voice);
-static fx_affect_func fx_realtime[] = {
-	/* env1: delay, attack, hold, decay, release, sustain, pitch, cutoff*/
-	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-	/* env2: delay, attack, hold, decay, release, sustain */
-	NULL, NULL, NULL, NULL, NULL, NULL,
-	/* lfo1: delay, freq, volume, pitch, cutoff */
-	NULL, awe_fx_tremfrq, awe_fx_tremfrq, awe_fx_fmmod, awe_fx_fmmod,
-	/* lfo2: delay, freq, pitch */
-	NULL, awe_fx_fm2frq2, awe_fx_fm2frq2,
-	/* global: initpitch, chorus, reverb, cutoff, filterQ */
-	awe_fx_initpitch, NULL, NULL, awe_fx_cutoff, NULL,
-	/* sample: start, loopstart, loopend */
-	NULL, NULL, NULL,
-};
-
-
 /*================================================================
  * turn on/off sample
  *================================================================*/
@@ -989,8 +1011,8 @@
 {
 	unsigned long temp;
 	long addr;
-	unsigned short tmp2;
 	awe_voice_info *vp;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
 
 	/* A voice sample must assigned before calling */
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
@@ -1005,71 +1027,65 @@
 
 	/* modulation & volume envelope */
 	awe_poke(AWE_ENVVAL(voice),
-		 FX_WORD(voice, AWE_FX_ENV1_DELAY, vp->parm.moddelay));
+		 FX_WORD(fx, AWE_FX_ENV1_DELAY, vp->parm.moddelay));
 	awe_poke(AWE_ATKHLD(voice),
-		 FX_COMB(voice, AWE_FX_ENV1_ATTACK, AWE_FX_ENV1_HOLD,
+		 FX_COMB(fx, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK,
 			 vp->parm.modatkhld));
 	awe_poke(AWE_DCYSUS(voice),
-		 FX_COMB(voice, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY,
+		 FX_COMB(fx, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY,
 			  vp->parm.moddcysus));
 	awe_poke(AWE_ENVVOL(voice),
-		 FX_WORD(voice, AWE_FX_ENV2_DELAY, vp->parm.voldelay));
+		 FX_WORD(fx, AWE_FX_ENV2_DELAY, vp->parm.voldelay));
 	awe_poke(AWE_ATKHLDV(voice),
-		 FX_COMB(voice, AWE_FX_ENV2_ATTACK, AWE_FX_ENV2_HOLD,
+		 FX_COMB(fx, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK,
 			 vp->parm.volatkhld));
 	/* decay/sustain parameter for volume envelope must be set at last */
 
 	/* pitch offset */
-	awe_poke(AWE_IP(voice), voices[voice].apitch);
-	DEBUG(3,printk("AWE32: [-- pitch=%x]\n", voices[voice].apitch));
+	awe_set_pitch(voice, TRUE);
 
 	/* cutoff and volume */
-	tmp2 = FX_BYTE(voice, AWE_FX_CUTOFF, vp->parm.cutoff);
-	tmp2 = (tmp2 << 8) | voices[voice].avol;
-	awe_poke(AWE_IFATN(voice), tmp2);
+	awe_set_volume(voice, TRUE);
 
 	/* modulation envelope heights */
 	awe_poke(AWE_PEFE(voice),
-		 FX_COMB(voice, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF,
+		 FX_COMB(fx, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF,
 			 vp->parm.pefe));
 
 	/* lfo1/2 delay */
 	awe_poke(AWE_LFO1VAL(voice),
-		 FX_WORD(voice, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay));
+		 FX_WORD(fx, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay));
 	awe_poke(AWE_LFO2VAL(voice),
-		 FX_WORD(voice, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay));
+		 FX_WORD(fx, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay));
 
 	/* lfo1 pitch & cutoff shift */
-	awe_poke(AWE_FMMOD(voice),
-		 FX_COMB(voice, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF,
-			 vp->parm.fmmod));
+	awe_fx_fmmod(voice, TRUE);
 	/* lfo1 volume & freq */
-	awe_poke(AWE_TREMFRQ(voice),
-		 FX_COMB(voice, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ,
-			 vp->parm.tremfrq));
+	awe_fx_tremfrq(voice, TRUE);
 	/* lfo2 pitch & freq */
-	awe_poke(AWE_FM2FRQ2(voice),
-		 FX_COMB(voice, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ,
-			 vp->parm.fm2frq2));
-
+	awe_fx_fm2frq2(voice, TRUE);
 	/* pan & loop start */
 	 awe_set_pan(voice, 1);
 
 	/* chorus & loop end (chorus 8bit, MSB) */
 	addr = vp->loopend - 1;
-	addr += FX_OFFSET(voice, AWE_FX_LOOP_END,
-			  AWE_FX_COARSE_LOOP_END);
-	temp = FX_BYTE(voice, AWE_FX_CHORUS, vp->parm.chorus);
+	addr += FX_OFFSET(fx, AWE_FX_LOOP_END,
+			  AWE_FX_COARSE_LOOP_END, vp->mode);
+	temp = FX_BYTE(fx, AWE_FX_CHORUS, vp->parm.chorus);
 	temp = (temp <<24) | (unsigned long)addr;
 	awe_poke_dw(AWE_CSL(voice), temp);
+	DEBUG(4,printk("AWE32: [-- loopend=%x/%x]\n",
+		       (int)vp->loopend, (int)addr));
 
 	/* Q & current address (Q 4bit value, MSB) */
 	addr = vp->start - 1;
-	addr += FX_OFFSET(voice, AWE_FX_SAMPLE_START,
-			  AWE_FX_COARSE_SAMPLE_START);
-	temp = FX_BYTE(voice, AWE_FX_FILTERQ, vp->parm.filterQ);
+	addr += FX_OFFSET(fx, AWE_FX_SAMPLE_START,
+			  AWE_FX_COARSE_SAMPLE_START, vp->mode);
+	temp = FX_BYTE(fx, AWE_FX_FILTERQ, vp->parm.filterQ);
 	temp = (temp<<28) | (unsigned long)addr;
 	awe_poke_dw(AWE_CCCA(voice), temp);
+	DEBUG(4,printk("AWE32: [-- startaddr=%x/%x]\n",
+		       (int)vp->start, (int)addr));
 
 	/* reset volume */
 	awe_poke_dw(AWE_VTFT(voice), 0x0000FFFF);
@@ -1077,36 +1093,41 @@
 
 	/* turn on envelope */
 	awe_poke(AWE_DCYSUSV(voice),
-		 FX_COMB(voice, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY,
+		 FX_COMB(fx, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY,
 			  vp->parm.voldcysus));
 	/* set chorus */
-	temp = FX_BYTE(voice, AWE_FX_REVERB, vp->parm.reverb);
+	temp = FX_BYTE(fx, AWE_FX_REVERB, vp->parm.reverb);
 	temp = (awe_peek_dw(AWE_PTRX(voice)) & 0xffff0000) | (temp<<8);
 	awe_poke_dw(AWE_PTRX(voice), temp);
 	awe_poke_dw(AWE_CPF(voice), 0x40000000);
-
-	DEBUG(3,printk("AWE32: [-- start=%x loop=%x]\n",
-		       (int)vp->start, (int)vp->loopstart));
 }
 
+
 /* turn off the voice */
 static void
 awe_note_off(int voice)
 {
 	awe_voice_info *vp;
 	unsigned short tmp;
-	if ((vp = voices[voice].sample) == NULL || !voices[voice].state)
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	if ((vp = voices[voice].sample) == NULL) {
+		awe_voice_init(voice);
 		return;
-	if (FX_ON(voice, AWE_FX_ENV1_RELEASE))
-		tmp = 0x8000 | voices[voice].fx[AWE_FX_ENV1_RELEASE];
+	}
+
+	if (FX_ON(fx, AWE_FX_ENV1_RELEASE))
+		tmp = 0x8000 | fx->val[AWE_FX_ENV1_RELEASE];
 	else
 		tmp = vp->parm.modrelease;
 	awe_poke(AWE_DCYSUS(voice), tmp);
-	if (FX_ON(voice, AWE_FX_ENV2_RELEASE))
-		tmp = 0x8000 | voices[voice].fx[AWE_FX_ENV2_RELEASE];
+	if (FX_ON(fx, AWE_FX_ENV2_RELEASE))
+		tmp = 0x8000 | fx->val[AWE_FX_ENV2_RELEASE];
 	else
 		tmp = vp->parm.volrelease;
 	awe_poke(AWE_DCYSUSV(voice), tmp);
+	voices[voice].state = AWE_ST_OFF;
+
+	awe_voice_init(voice);
 }
 
 /* force to terminate the voice (no releasing echo) */
@@ -1114,29 +1135,27 @@
 awe_terminate(int voice)
 {
 	awe_poke(AWE_DCYSUSV(voice), 0x807F);
+	awe_tweak_voice(voice);
+	awe_voice_init(voice);
 }
 
 
 /* turn off other voices with the same exclusive class (for drums) */
 static void
-awe_exclusive_off(int voice)
+awe_exclusive_off(int voice, int exclass)
 {
-	int i, excls;
+	int i;
 
-	if (voices[voice].sample == NULL) /* no sample */
-		return;
-	excls = voices[voice].sample->exclusiveClass;
-	if (excls == 0)	/* not exclusive */
+	if (exclass == 0) /* not exclusive */
 		return;
 
 	/* turn off voices with the same class */
 	for (i = 0; i < awe_max_voices; i++) {
-		if (i != voice && voices[voice].state &&
+		if (i != voice && IS_PLAYING(voice) &&
 		    voices[i].sample &&
-		    voices[i].sample->exclusiveClass == excls) {
+		    voices[i].sample->exclusiveClass == exclass) {
 			DEBUG(4,printk("AWE32: [exoff(%d)]\n", i));
 			awe_note_off(i);
-			awe_voice_init(i, 1);
 		}
 	}
 }
@@ -1148,26 +1167,47 @@
 
 /* change pitch */
 static void
-awe_set_pitch(int voice)
+awe_set_pitch(int voice, int forced)
 {
-	if (!voices[voice].state) return;
+	if (IS_NO_EFFECT(voice) && !forced) return;
 	awe_poke(AWE_IP(voice), voices[voice].apitch);
+	DEBUG(3,printk("AWE32: [-- pitch=%x]\n", voices[voice].apitch));
+}
+
+/* calculate & change pitch */
+static void
+awe_set_voice_pitch(int voice, int forced)
+{
+	awe_calc_pitch(voice);
+	awe_set_pitch(voice, forced);
 }
 
 /* change volume */
 static void
-awe_set_volume(int voice)
+awe_set_volume(int voice, int forced)
 {
 	awe_voice_info *vp;
 	unsigned short tmp2;
-	if (!voices[voice].state) return;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+
+	if (IS_NO_EFFECT(voice) && !forced) return;
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
 		return;
-	tmp2 = FX_BYTE(voice, AWE_FX_CUTOFF, vp->parm.cutoff);
+
+	tmp2 = FX_BYTE(fx, AWE_FX_CUTOFF, vp->parm.cutoff);
 	tmp2 = (tmp2 << 8) | voices[voice].avol;
 	awe_poke(AWE_IFATN(voice), tmp2);
 }
 
+/* calculate & change volume */
+static void
+awe_set_voice_vol(int voice, int forced)
+{
+	awe_calc_volume(voice);
+	awe_set_volume(voice, forced);
+}
+
+
 /* change pan; this could make a click noise.. */
 static void
 awe_set_pan(int voice, int forced)
@@ -1175,8 +1215,9 @@
 	unsigned long temp;
 	long addr;
 	awe_voice_info *vp;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
 
-	if (!voices[voice].state && !forced) return;
+	if (IS_NO_EFFECT(voice) && !forced) return;
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
 		return;
 
@@ -1187,7 +1228,7 @@
 		int pos = 0;
 		if (vp->pan >= 0) /* 0-127 */
 			pos = (int)vp->pan * 2 - 128;
-		pos += voices[voice].panning; /* -128 - 127 */
+		pos += voices[voice].cinfo->panning; /* -128 - 127 */
 		pos = 127 - pos;
 		if (pos < 0)
 			temp = 0;
@@ -1196,76 +1237,75 @@
 		else
 			temp = pos;
 	}
-	addr = vp->loopstart - 1;
-	addr += FX_OFFSET(voice, AWE_FX_LOOP_START,
-			  AWE_FX_COARSE_LOOP_START);
-	temp = (temp<<24) | (unsigned long)addr;
-	awe_poke_dw(AWE_PSST(voice), temp);
+	if (forced || temp != voices[voice].apan) {
+		addr = vp->loopstart - 1;
+		addr += FX_OFFSET(fx, AWE_FX_LOOP_START,
+				  AWE_FX_COARSE_LOOP_START, vp->mode);
+		temp = (temp<<24) | (unsigned long)addr;
+		awe_poke_dw(AWE_PSST(voice), temp);
+		voices[voice].apan = temp;
+		DEBUG(4,printk("AWE32: [-- loopstart=%x/%x]\n",
+			       (int)vp->loopstart, (int)addr));
+	}
 }
 
 /* effects change during playing */
 static void
-awe_fx_fmmod(int voice)
+awe_fx_fmmod(int voice, int forced)
 {
 	awe_voice_info *vp;
-	if (!voices[voice].state) return;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	if (IS_NO_EFFECT(voice) && !forced) return;
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
 		return;
 	awe_poke(AWE_FMMOD(voice),
-		 FX_COMB(voice, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF,
+		 FX_COMB(fx, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF,
 			 vp->parm.fmmod));
 }
 
+/* set tremolo (lfo1) volume & frequency */
 static void
-awe_fx_tremfrq(int voice)
+awe_fx_tremfrq(int voice, int forced)
 {
 	awe_voice_info *vp;
-	if (!voices[voice].state) return;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	if (IS_NO_EFFECT(voice) && !forced) return;
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
 		return;
 	awe_poke(AWE_TREMFRQ(voice),
-		 FX_COMB(voice, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ,
+		 FX_COMB(fx, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ,
 			 vp->parm.tremfrq));
 }
 
+/* set lfo2 pitch & frequency */
 static void
-awe_fx_fm2frq2(int voice)
+awe_fx_fm2frq2(int voice, int forced)
 {
 	awe_voice_info *vp;
-	if (!voices[voice].state) return;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	if (IS_NO_EFFECT(voice) && !forced) return;
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
 		return;
 	awe_poke(AWE_FM2FRQ2(voice),
-		 FX_COMB(voice, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ,
+		 FX_COMB(fx, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ,
 			 vp->parm.fm2frq2));
 }
 
+/* set total cutoff  & current pitch */
 static void
-awe_fx_cutoff(int voice)
+awe_fx_cutoff(int voice, int forced)
 {
 	unsigned short tmp2;
 	awe_voice_info *vp;
-	if (!voices[voice].state) return;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	if (IS_NO_EFFECT(voice)) return;
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
 		return;
-	tmp2 = FX_BYTE(voice, AWE_FX_CUTOFF, vp->parm.cutoff);
+	tmp2 = FX_BYTE(fx, AWE_FX_CUTOFF, vp->parm.cutoff);
 	tmp2 = (tmp2 << 8) | voices[voice].avol;
 	awe_poke(AWE_IFATN(voice), tmp2);
 }
 
-static void
-awe_fx_initpitch(int voice)
-{
-	if (!voices[voice].state) return;
-	if (FX_ON(voice, AWE_FX_INIT_PITCH)) {
-		DEBUG(3,printk("AWE32: initpitch ok\n"));
-	} else {
-		DEBUG(3,printk("AWE32: BAD initpitch %d\n", AWE_FX_INIT_PITCH));
-	}
-	awe_calc_pitch(voice);
-	awe_poke(AWE_IP(voice), voices[voice].apitch);
-}
-
 
 /*================================================================
  * calculate pitch offset
@@ -1279,6 +1319,7 @@
 {
 	voice_info *vp = &voices[voice];
 	awe_voice_info *ap;
+	awe_chan_info *cp = voices[voice].cinfo;
 	int offset;
 
 	/* search voice information */
@@ -1300,18 +1341,18 @@
 	}
 	offset += ap->tune * 4096 / 1200;
 	DEBUG(4,printk("AWE32: p-> tune+ ofs=%d\n", offset));
-	if (vp->bender != 0) {
-		DEBUG(3,printk("AWE32: p-> bend(%d) %d\n", voice, vp->bender));
+	if (cp->bender != 0) {
+		DEBUG(3,printk("AWE32: p-> bend(%d) %d\n", voice, cp->bender));
 		/* (819200: 1 semitone) ==> (4096: 12 semitones) */
-		offset += vp->bender * vp->bender_range / 2400;
+		offset += cp->bender * cp->bender_range / 2400;
 	}
 	offset = (offset * ap->scaleTuning) / 100;
 	DEBUG(4,printk("AWE32: p-> scale* ofs=%d\n", offset));
 
 	/* add initial pitch correction */
-	if (FX_ON(voice, AWE_FX_INIT_PITCH)) {
-		DEBUG(3,printk("AWE32: fx_pitch(%d) %d\n", voice, vp->fx[AWE_FX_INIT_PITCH]));
-		offset += vp->fx[AWE_FX_INIT_PITCH];
+	if (FX_ON(&cp->fx, AWE_FX_INIT_PITCH)) {
+		DEBUG(3,printk("AWE32: fx_pitch(%d) %d\n", voice, cp->fx.val[AWE_FX_INIT_PITCH]));
+		offset += cp->fx.val[AWE_FX_INIT_PITCH];
 	}
 
 	/* 0xe000: root pitch */
@@ -1324,11 +1365,14 @@
 }
 
 
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+/* calculate MIDI key and semitone from the specified frequency */
 static void
 awe_calc_pitch_from_freq(int voice, int freq)
 {
 	voice_info *vp = &voices[voice];
 	awe_voice_info *ap;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
 	int offset;
 	int note;
 
@@ -1342,14 +1386,16 @@
 	note = freq_to_note(freq);
 	offset = (note - ap->root * 100 + ap->tune) * 4096 / 1200;
 	offset = (offset * ap->scaleTuning) / 100;
-	if (FX_ON(voice, AWE_FX_INIT_PITCH))
-		offset += vp->fx[AWE_FX_INIT_PITCH];
+	if (FX_ON(fx, AWE_FX_INIT_PITCH))
+		offset += fx->val[AWE_FX_INIT_PITCH];
 	vp->apitch = 0xe000 + ap->rate_offset + offset;
 	if (vp->apitch > 0xffff)
 		vp->apitch = 0xffff;
 	if (vp->apitch < 0)
 		vp->apitch = 0;
 }
+#endif /* AWE_HAS_GUS_COMPATIBILITY */
+
 
 /*================================================================
  * calculate volume attenuation
@@ -1375,6 +1421,7 @@
 {
 	voice_info *vp = &voices[voice];
 	awe_voice_info *ap;
+	awe_chan_info *cp = voices[voice].cinfo;
 	int vol;
 
 	/* search voice information */
@@ -1387,13 +1434,8 @@
 			return;
 	}
 	
-	if (vp->velocity < ap->vellow)
-		vp->velocity = ap->vellow;
-	else if (vp->velocity > ap->velhigh)
-		vp->velocity = ap->velhigh;
-
 	/* 0 - 127 */
-	vol = (vp->velocity * vp->main_vol * vp->expression_vol) / (127*127);
+	vol = (vp->velocity * cp->main_vol * cp->expression_vol) / (127*127);
 	vol = vol * ap->amplitude / 127;
 	if (vol < 0) vol = 0;
 	if (vol > 127) vol = 127;
@@ -1412,35 +1454,52 @@
  * synth operation routines
  *================================================================*/
 
+#define AWE_VOICE_KEY(v)	(0x8000 | (v))
+#define AWE_CHAN_KEY(c,n)	(((c) << 8) | ((n) + 1))
+
 /* initialize the voice */
 static void
-awe_voice_init(int voice, int inst_only)
+awe_voice_init(int voice)
 {
-	if (! inst_only) {
-		/* clear voice parameters */
-		voices[voice].note = -1;
-		voices[voice].velocity = 0;
-		voices[voice].panning = 0; /* zero center */
-		voices[voice].bender = 0; /* zero tune skew */
-		voices[voice].bender_range = 200; /* sense * 100 */
-		voices[voice].main_vol = 127;
-		voices[voice].expression_vol = 127;
-		voices[voice].bank = AWE_DEFAULT_BANK;
-		voices[voice].instr = -1;
-		voices[voice].vrec = NULL;
-		voices[voice].sample = NULL;
-	}
+	voices[voice].note = -1;
+	voices[voice].velocity = 0;
+	voices[voice].sample = NULL;
+	voices[voice].state = AWE_ST_OFF;
+	voices[voice].cinfo = &channels[voice];
+	voices[voice].ch = -1;
+	voices[voice].key = AWE_VOICE_KEY(voice);
+	voices[voice].time = current_alloc_time;
 
 	/* clear voice mapping */
-	voices[voice].state = 0;
 	voice_alloc->map[voice] = 0;
 
 	/* emu8000 parameters */
 	voices[voice].apitch = 0;
 	voices[voice].avol = 255;
+	voices[voice].apan = -1;
 
 	/* clear effects */
-	BZERO(voices[voice].fx_flags, sizeof(voices[voice].fx_flags));
+	if (! awe_channel_mode)
+		BZERO(&voices[voice].cinfo->fx, sizeof(FX_Rec));
+}
+
+/* initialize channel info */
+static void awe_channel_init(int ch)
+{
+	channels[ch].panning = 0; /* zero center */
+	channels[ch].bender = 0; /* zero tune skew */
+	channels[ch].bender_range = 200; /* sense * 100 */
+	channels[ch].main_vol = 127;
+	channels[ch].expression_vol = 127;
+	if (awe_channel_mode && IS_DRUM_CHANNEL(ch))
+		channels[ch].bank = AWE_DRUM_BANK;
+	else
+		channels[ch].bank = AWE_DEFAULT_BANK;
+	channels[ch].instr = 0;
+	channels[ch].vrec = NULL;
+	channels[ch].def_vrec = NULL;
+	channels[ch].sustained = 0;
+	BZERO(&channels[ch].fx, sizeof(FX_Rec));
 }
 
 
@@ -1457,14 +1516,19 @@
 	if (awe_busy)
 		return RET_ERROR(EBUSY);
 
-	awe_busy = 1;
+	awe_busy = TRUE;
 	awe_reset(dev);
 
 	/* clear sample position flag */
 	loaded_once = 0;
 
-	/* set GUS bank to default */
+	/* set default mode */
+	init_atten = AWE_DEFAULT_ATTENUATION; /* 12dB below */
 	awe_gus_bank = AWE_DEFAULT_BANK;
+	drum_flags = DEFAULT_DRUM_FLAGS;
+	awe_exclusive_sound = TRUE;
+	awe_channel_mode = 0;
+
 	return 0;
 }
 
@@ -1476,7 +1540,7 @@
 awe_close(int dev)
 {
 	awe_reset(dev);
-	awe_busy = 0;
+	awe_busy = FALSE;
 }
 
 
@@ -1494,7 +1558,7 @@
 
 	case SNDCTL_SEQ_RESETSAMPLES:
 		awe_reset_samples();
-		awe_reset(dev); /* better to reset emu8k chip... */
+		awe_reset(dev);
 		return 0;
 		break;
 
@@ -1520,88 +1584,139 @@
 static int
 awe_kill_note(int dev, int voice, int note, int velocity)
 {
-	awe_voice_info *vp;
-	DECL_INTR_FLAGS(flags);
+	int i, key, besttime;
 
-	DEBUG(2,printk("AWE32: [off(%d)]\n", voice));
-	if (voice < 0 || voice >= awe_max_voices)
-		      return RET_ERROR(EINVAL);
-	if ((vp = voices[voice].sample) == NULL)
-		return 0;
-       
-	if (!(vp->mode & AWE_MODE_NORELEASE)) {
-		DISABLE_INTR(flags);
-		awe_note_off(voice);
-		RESTORE_INTR(flags);
+	DEBUG(2,printk("AWE32: [off(%d) nt=%d vl=%d]\n", voice, note, velocity));
+	if (awe_channel_mode) {
+		if (awe_channel_mode == 2) { /* get channel */
+			int v2 = voice_alloc->map[voice] >> 8;
+			voice_alloc->map[voice] = 0;
+			voice = v2;
+		}
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return RET_ERROR(EINVAL);
+		if (channels[voice].instr > 128)
+			note = channels[voice].instr - 128;
+		key = AWE_CHAN_KEY(voice, note);
+	} else {
+		if (voice < 0 || voice >= awe_max_voices)
+			return RET_ERROR(EINVAL);
+		key = AWE_VOICE_KEY(voice);
 	}
-	awe_voice_init(voice, 1);
-	return 0;
-}
-
 
-/* search the note with the specified key range */
-static awe_voice_info *
-awe_search_voice(int voice, int note)
-{
-	awe_voice_list *rec;
-	int maxc;
-
-	for (rec = voices[voice].vrec, maxc = AWE_MAX_INFOS;
-	     rec && maxc; rec = rec->next_instr, maxc--) {
-		if (rec->v.low <= note && note <= rec->v.high)
-			return &rec->v;
+	besttime = current_alloc_time + 1;
+	for (i = 0; i < awe_max_voices; i++) {
+		if (voices[i].key == key) {
+			if (voices[i].time < besttime)
+				besttime = voices[i].time;
+		}
 	}
-	return NULL;
+	for (i = 0; i < awe_max_voices; i++) {
+		if (voices[i].key == key &&
+		    (!awe_exclusive_sound || voices[i].time == besttime)) {
+			if (voices[i].cinfo->sustained)
+				voices[i].state = AWE_ST_SUSTAINED;
+			else
+				awe_note_off(i);
+		}
+	}
+	return 0;
 }
 
+
 /* start a voice:
  *   if note is 255, identical with aftertouch function.
  *   Otherwise, start a voice with specified not and volume.
  */
 static int
-awe_start_note(int dev, int v, int note_num, int volume)
+awe_start_note(int dev, int voice, int note, int velocity)
 {
-	DECL_INTR_FLAGS(flags);
+	int i, key, state, volonly;
 
-	DEBUG(2,printk("AWE32: [on(%d) nt=%d vl=%d]\n", v, note_num, volume));
-	if (v < 0 || v >= awe_max_voices)
+	DEBUG(2,printk("AWE32: [on(%d) nt=%d vl=%d]\n", voice, note, velocity));
+	volonly = 0;
+	if (awe_channel_mode) {
+		if (awe_channel_mode == 2) /* get channel */
+			voice = voice_alloc->map[voice] >> 8;
+		else if (voice & 0x80) { /* channel volume mode */
+			voice &= ~0x80;
+			volonly = 2;
+		}
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return RET_ERROR(EINVAL);
+		if (channels[voice].instr > 128)
+			note = channels[voice].instr - 128;
+		if (note >= 128) { /* key volume mode */
+			note -= 128;
+			volonly = 1;
+		}
+		key = AWE_CHAN_KEY(voice, note);
+	} else {
+		if (voice < 0 || voice >= awe_max_voices)
 		      return RET_ERROR(EINVAL);
-	/* an instrument must be set before starting a note */
-	if (voices[v].vrec == NULL) {
-		DEBUG(1,printk("AWE32: [-- vrec is null]\n"));
+		if (voices[voice].cinfo->instr > 128)
+			note = voices[voice].cinfo->instr - 128;
+		key = AWE_VOICE_KEY(voice);
+		if (note == 255)
+			volonly = 1;
+	}
+
+	/* dynamic volume change */
+	if (volonly) {
+		for (i = 0; i < awe_max_voices; i++) {
+			if ((volonly == 2 && voices[i].ch == voice) ||
+			    voices[i].key == key) {
+				voices[i].velocity = velocity;
+				if (velocity == 0) /* for GUS compatibility */
+					return awe_kill_note(dev, voice, note, velocity);
+				awe_calc_volume(i);
+				state = voices[i].state;
+				voices[i].state = AWE_ST_ON;
+				if (state == AWE_ST_STANDBY)
+					awe_note_on(i);
+				else
+					awe_set_volume(i, FALSE);
+			}
+		}
 		return 0;
 	}
 
-	if (note_num == 255) {
-		/* dynamic volume change; sample is already assigned */
-		if (! voices[v].state || voices[v].sample == NULL)
-			return 0;
-		/* calculate volume parameter */
-		voices[v].velocity = volume;
-		awe_calc_volume(v);
-		DISABLE_INTR(flags);
-		awe_set_volume(v);
-		RESTORE_INTR(flags);
-		return 0;
-	}
-	/* assign a sample with the corresponding note */
-	if ((voices[v].sample = awe_search_voice(v, note_num)) == NULL) {
-		DEBUG(1,printk("AWE32: [-- sample is null]\n"));
-		return 0;
+	/* stop the sound if still playing */
+	if (awe_exclusive_sound) {
+		for (i = 0; i < awe_max_voices; i++)
+			if (voices[i].key == key &&
+			    voices[i].state != AWE_ST_OFF)
+				awe_note_off(i);
 	}
-	/* calculate pitch & volume parameters */
-	voices[v].note = note_num;
-	voices[v].velocity = volume;
-	awe_calc_pitch(v);
-	awe_calc_volume(v);
 
-	DISABLE_INTR(flags);
+	/* allocate voices */
+	if (awe_channel_mode)
+		awe_alloc_multi_voices(voice, note, velocity);
+	else
+		awe_alloc_one_voice(voice, note, velocity);
+
 	/* turn off other voices (for drums) */
-	awe_exclusive_off(v);
-	/* turn on the voice */
-	awe_note_on(v);
-	voices[v].state = 1;	/* flag up */
-	RESTORE_INTR(flags);
+	for (i = 0; i < awe_max_voices; i++)
+		if (voices[i].key == key && voices[i].sample)
+			awe_exclusive_off(i, voices[i].sample->exclusiveClass);
+
+	if (velocity == 0)
+		state = AWE_ST_STANDBY; /* stand by for playing */
+	else
+		state = AWE_ST_ON;	/* really play */
+
+	/* set up pitch and volume parameters */
+	for (i = 0; i < awe_max_voices; i++)
+		if (voices[i].key == key) {
+			/* calculate pitch & volume parameters */
+			voices[i].state = state;
+			voices[i].note = note;
+			voices[i].velocity = velocity;
+			awe_calc_pitch(i);
+			awe_calc_volume(i);
+			if (state == AWE_ST_ON)
+				awe_note_on(i);
+		}
 
 	return 0;
 }
@@ -1625,28 +1740,58 @@
 
 /* assign the instrument to a voice */
 static int
+awe_set_instr_2(int dev, int voice, int instr_no)
+{
+	if (awe_channel_mode == 2)
+		voice = voice_alloc->map[voice] >> 8;
+	return awe_set_instr(dev, voice, instr_no);
+}
+
+/* assign the instrument to a voice */
+static int
 awe_set_instr(int dev, int voice, int instr_no)
 {
-	awe_voice_list *rec;
+	awe_chan_info *cinfo;
+	int def_bank;
 
-	if (voice < 0 || voice >= awe_max_voices)
-		return RET_ERROR(EINVAL);
+	if (awe_channel_mode) {
+		/* skip the percussion instr in SEQ2 mode */
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return RET_ERROR(EINVAL);
+		cinfo = &channels[voice];
+		if (IS_DRUM_CHANNEL(voice))
+			def_bank = AWE_DRUM_BANK;
+		else
+			def_bank = cinfo->bank;
+	} else {
+		if (voice < 0 || voice >= awe_max_voices)
+			return RET_ERROR(EINVAL);
+		cinfo = voices[voice].cinfo;
+		def_bank = cinfo->bank;
+	}
 
 	if (instr_no < 0 || instr_no >= AWE_MAX_PRESETS)
 		return RET_ERROR(EINVAL);
 
-	if ((rec = awe_search_instr(voices[voice].bank, instr_no)) == NULL) {
-		/* if bank is not defined, use the default bank 0 */
-		if (voices[voice].bank != AWE_DEFAULT_BANK &&
-		    (rec = awe_search_instr(AWE_DEFAULT_BANK, instr_no)) == NULL) {
-			DEBUG(1,printk("AWE32 Warning: can't find instrument %d\n", instr_no));
-			return 0;
-		}
+	cinfo->vrec = NULL;
+	cinfo->def_vrec = NULL;
+	if (instr_no > 128) {
+		cinfo->vrec = awe_search_instr(128, cinfo->bank);
+		if (cinfo->bank != 0)
+			cinfo->def_vrec = awe_search_instr(128, 0);
+	} else {
+		cinfo->vrec = awe_search_instr(def_bank, instr_no);
+		if (def_bank == AWE_DRUM_BANK)
+			cinfo->def_vrec = awe_search_instr(def_bank, 0);
+		else
+			cinfo->def_vrec = awe_search_instr(AWE_DEFAULT_BANK, instr_no);
+	}
+	if (cinfo->vrec == NULL && cinfo->def_vrec == NULL) {
+		DEBUG(1,printk("AWE32 Warning: can't find instrument %d\n", instr_no));
+		return 0;
 	}
 
-	voices[voice].instr = instr_no;
-	voices[voice].vrec = rec;
-	voices[voice].sample = NULL;  /* not set yet */
+	cinfo->instr = instr_no;
 
 	return 0;
 }
@@ -1657,10 +1802,15 @@
 awe_reset(int dev)
 {
 	int i;
+	current_alloc_time = 0;
 	/* don't turn off voice 31 and 32.  they are used also for FM voices */
-	for (i = 0; i < AWE_NORMAL_VOICES; i++) {
+	for (i = 0; i < AWE_NORMAL_VOICES; i++)
 		awe_terminate(i);
-		awe_voice_init(i, 0);
+	for (i = 0; i < AWE_MAX_CHANNELS; i++)
+		awe_channel_init(i);
+	for (i = 0; i < 16; i++) {
+		awe_operations.chn_info[i].controllers[CTL_MAIN_VOLUME] = 127;
+		awe_operations.chn_info[i].controllers[CTL_EXPRESSION] = 127;
 	}
 	awe_init_fm();
 	awe_tweak();
@@ -1676,10 +1826,15 @@
 	int cmd = event[2];
 	if (cmd & _AWE_MODE_FLAG)
 		awe_hw_awe_control(dev, cmd & _AWE_MODE_VALUE_MASK, event);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
 	else
 		awe_hw_gus_control(dev, cmd & _AWE_MODE_VALUE_MASK, event);
+#endif
 }
 
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+
 /* GUS compatible controls */
 static void
 awe_hw_gus_control(int dev, int cmd, unsigned char *event)
@@ -1688,7 +1843,6 @@
 	unsigned short p1;
 	short p2;
 	int plong;
-	DECL_INTR_FLAGS(flags);
 
 	voice = event[3];
 	p1 = *(unsigned short *) &event[4];
@@ -1698,7 +1852,7 @@
 	switch (cmd) {
 	case _GUS_NUMVOICES:
 		if (p1 >= awe_max_voices)
-			printk("AWE32: num_voices: voices out of range %d\n", p1);
+			DEBUG(0,printk("AWE32: num_voices: voices out of range %d\n", p1));
 		break;
 	case _GUS_VOICESAMPLE:
 		if (voice < awe_max_voices)
@@ -1706,19 +1860,13 @@
 		break;
 
 	case _GUS_VOICEON:
-		if (voice < awe_max_voices) {
-			DISABLE_INTR(flags);
+		if (voice < awe_max_voices)
 			awe_note_on(voice);
-			RESTORE_INTR(flags);
-		}
 		break;
 		
 	case _GUS_VOICEOFF:
-		if (voice < awe_max_voices) {
-			DISABLE_INTR(flags);
+		if (voice < awe_max_voices)
 			awe_note_off(voice);
-			RESTORE_INTR(flags);
-		}
 		break;
 		
 	case _GUS_VOICEMODE:
@@ -1726,9 +1874,9 @@
 		break;
 
 	case _GUS_VOICEBALA:
-		/* -128 to 127 */
+		/* 0 to 15 --> -128 to 127 */
 		if (voice < awe_max_voices)
-			awe_panning(dev, voice, (short)p1);
+			awe_panning(dev, voice, ((int)p1 << 4) - 128);
 		break;
 
 	case _GUS_VOICEFREQ:
@@ -1754,13 +1902,35 @@
 
 	case _GUS_VOICE_POS:
 		if (voice < awe_max_voices) {
-			FX_SET(voice, AWE_FX_SAMPLE_START, (short)(plong & 0x7fff));
-			FX_SET(voice, AWE_FX_COARSE_SAMPLE_START, (plong >> 15) & 0xffff);
+			FX_SET(&voices[voice].cinfo->fx, AWE_FX_SAMPLE_START,
+			       (short)(plong & 0x7fff));
+			FX_SET(&voices[voice].cinfo->fx, AWE_FX_COARSE_SAMPLE_START,
+			       (plong >> 15) & 0xffff);
 		}
 		break;
 	}
 }
 
+#endif
+
+
+/* converter function table for realtime paramter change */
+
+static fx_affect_func fx_realtime[] = {
+	/* env1: delay, attack, hold, decay, release, sustain, pitch, cutoff*/
+	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+	/* env2: delay, attack, hold, decay, release, sustain */
+	NULL, NULL, NULL, NULL, NULL, NULL,
+	/* lfo1: delay, freq, volume, pitch, cutoff */
+	NULL, awe_fx_tremfrq, awe_fx_tremfrq, awe_fx_fmmod, awe_fx_fmmod,
+	/* lfo2: delay, freq, pitch */
+	NULL, awe_fx_fm2frq2, awe_fx_fm2frq2,
+	/* global: initpitch, chorus, reverb, cutoff, filterQ */
+	awe_set_voice_pitch, NULL, NULL, awe_fx_cutoff, NULL,
+	/* sample: start, loopstart, loopend */
+	NULL, NULL, NULL,
+};
+
 
 /* AWE32 specific controls */
 static void
@@ -1770,20 +1940,29 @@
 	unsigned short p1;
 	short p2;
 	int chn;
+	awe_chan_info *cinfo;
+	int i;
 
 	chn = event[1];
 	voice = event[3];
 	p1 = *(unsigned short *) &event[4];
 	p2 = *(short *) &event[6];
 	
+	if (awe_channel_mode) {
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return;
+		cinfo = &channels[voice];
+	} else {
+		if (voice < 0 || voice >= awe_max_voices)
+			return;
+		cinfo = voices[voice].cinfo;
+	}
 
-#ifdef AWE_DEBUG_ON
 	switch (cmd) {
 	case _AWE_DEBUG_MODE:
 		debug_mode = p1;
 		printk("AWE32: debug mode = %d\n", debug_mode);
 		break;
-#endif
 	case _AWE_REVERB_MODE:
 		if (p1 <= 7) {
 			reverb_mode = p1;
@@ -1810,32 +1989,36 @@
 		break;
 
 	case _AWE_SEND_EFFECT:
-		if (voice < awe_max_voices && p1 < AWE_FX_END) {
-			FX_SET(voice, p1, p2);
-			DEBUG(0,printk("AWE32: effects (%d) %d %d\n", voice, p1, voices[voice].fx[p1]));
+		if (p1 < AWE_FX_END) {
+			FX_SET(&cinfo->fx, p1, p2);
+			DEBUG(0,printk("AWE32: effects (%d) %d %d\n", voice, p1, cinfo->fx.val[p1]));
+			FX_SET(&cinfo->fx, p1, p2);
 			if (fx_realtime[p1]) {
 				DEBUG(0,printk("AWE32: fx_realtime (%d)\n", voice));
-				fx_realtime[p1](voice);
+				awe_voice_change(voice, fx_realtime[p1]);
 			}
 		}
 		break;
 
-	case _AWE_TERMINATE_CHANNEL:
-		if (voice < awe_max_voices) {
-			DEBUG(0,printk("AWE32: terminate (%d)\n", voice));
-			awe_terminate(voice);
-			awe_voice_init(voice, 1);
-		}
-		break;
-
 	case _AWE_TERMINATE_ALL:
 		DEBUG(0,printk("AWE32: terminate all\n"));
 		awe_reset(0);
 		break;
 
+	case _AWE_TERMINATE_CHANNEL:
+		awe_voice_change(voice, (fx_affect_func)awe_terminate);
+		break;
+
+	case _AWE_NOTEOFF_ALL:
+		for (i = 0; i < awe_max_voices; i++)
+			awe_note_off(i);
+		break;
+
 	case _AWE_INITIAL_VOLUME:
 		DEBUG(0,printk("AWE32: init attenuation %d\n", p1));
 		init_atten = p1;
+		for (i = 0; i < awe_max_voices; i++)
+			awe_set_voice_vol(i, FALSE);
 		break;
 
 	case _AWE_SET_GUS_BANK:
@@ -1843,6 +2026,46 @@
 		awe_gus_bank = p1;
 		break;
 		
+	 /* v0.3 stuffs */
+	case _AWE_CHANNEL_MODE:
+		DEBUG(0,printk("AWE32: channel mode = %d\n", p1));
+		awe_channel_mode = p1;
+		awe_reset(0);
+		break;
+
+	case _AWE_DRUM_CHANNELS:
+		DEBUG(0,printk("AWE32: drum flags = %x\n", p1));
+		drum_flags = p1;
+		break;
+
+	case _AWE_EXCLUSIVE_SOUND:
+		DEBUG(0,printk("AWE32: exclusive mode = %d\n", p1));
+		awe_exclusive_sound = p1;
+		break;
+
+	case _AWE_GET_CURRENT_MODE:
+		{
+			awe_mode_rec tmprec;
+			tmprec.base_addr = awe_base;
+			tmprec.mem_size = awe_mem_size / 2;
+			tmprec.max_voices = awe_max_voices;
+			tmprec.max_infos = AWE_MAX_INFOS;
+			tmprec.max_samples = AWE_MAX_SAMPLES;
+			tmprec.current_sf_id = current_sf_id;
+			tmprec.free_mem = free_mem_ptr;
+			tmprec.free_info = free_info;
+			tmprec.free_sample = free_sample;
+			tmprec.reverb_mode = reverb_mode;
+			tmprec.chorus_mode = chorus_mode;
+			tmprec.init_atten = init_atten;
+			tmprec.channel_mode = awe_channel_mode;
+			tmprec.gus_bank = awe_gus_bank;
+			tmprec.exclusive_sound = awe_exclusive_sound;
+			tmprec.drum_flags = drum_flags;
+			tmprec.debug_mode = debug_mode;
+			IOCTL_TO_USER(*(awe_mode_rec**)&event[4], 0, &tmprec, sizeof(tmprec));
+			break;
+		}
 	default:
 		DEBUG(0,printk("AWE32: hw control cmd=%d voice=%d\n", cmd, voice));
 		break;
@@ -1862,9 +2085,12 @@
 	awe_patch_info patch;
 	int rc = 0;
 
+#ifdef AWE_HAS_GUS_COMPATIBILITY
 	if (format == GUS_PATCH) {
 		return awe_load_guspatch(addr, offs, count, pmgr_flag);
-	} else if (format == SYSEX_PATCH) {
+	} else
+#endif
+	if (format == SYSEX_PATCH) {
 		/* no system exclusive message supported yet */
 		return 0;
 	} else if (format != AWE_PATCH) {
@@ -1872,14 +2098,14 @@
 		return RET_ERROR(EINVAL);
 	}
 	
-	if (count < sizeof(awe_patch_info)) {
+	if (count < AWE_PATCH_INFO_SIZE) {
 		FATALERR(printk("AWE32 Error: Patch header too short\n"));
 		return RET_ERROR(EINVAL);
 	}
 	COPY_FROM_USER(((char*)&patch) + offs, addr, offs, 
-		       sizeof(awe_patch_info) - offs);
+		       AWE_PATCH_INFO_SIZE - offs);
 
-	count -= sizeof(awe_patch_info);
+	count -= AWE_PATCH_INFO_SIZE;
 	if (count < patch.len) {
 		FATALERR(printk("AWE32 Warning: Patch record too short (%d<%d)\n",
 		       count, (int)patch.len));
@@ -1919,12 +2145,12 @@
 	unsigned char bank, instr;
 	int total_size;
 
-	if (patch->len < sizeof(awe_voice_rec)) {
+	if (patch->len < AWE_VOICE_REC_SIZE) {
 		FATALERR(printk("AWE32 Error: invalid patch info length\n"));
 		return RET_ERROR(EINVAL);
 	}
 
-	offset = sizeof(awe_patch_info);
+	offset = AWE_PATCH_INFO_SIZE;
 	GET_BYTE_FROM_USER(bank, addr, offset); offset++;
 	GET_BYTE_FROM_USER(instr, addr, offset); offset++;
 	GET_SHORT_FROM_USER(nvoices, addr, offset); offset+=2;
@@ -1938,7 +2164,7 @@
 		return RET_ERROR(ENOSPC);
 	}
 
-	total_size = sizeof(awe_voice_rec) + sizeof(awe_voice_info) * nvoices;
+	total_size = AWE_VOICE_REC_SIZE + AWE_VOICE_INFO_SIZE * nvoices;
 	if (patch->len < total_size) {
 		ERRMSG(printk("AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n",
 		       (int)patch->len, nvoices));
@@ -1958,8 +2184,8 @@
 		rec->next_bank = NULL;
 
 		/* copy awe_voice_info parameters */
-		COPY_FROM_USER(&rec->v, addr, offset, sizeof(awe_voice_info));
-		offset += sizeof(awe_voice_info);
+		COPY_FROM_USER(&rec->v, addr, offset, AWE_VOICE_INFO_SIZE);
+		offset += AWE_VOICE_INFO_SIZE;
 		rec->v.sf_id = current_sf_id;
 		if (rec->v.mode & AWE_MODE_INIT_PARM)
 			awe_init_voice_parm(&rec->v.parm);
@@ -1988,11 +2214,11 @@
 		return RET_ERROR(ENOSPC);
 	}
 
-	size = (patch->len - sizeof(awe_sample_info)) / 2;
-	offset = sizeof(awe_patch_info);
+	size = (patch->len - AWE_SAMPLE_INFO_SIZE) / 2;
+	offset = AWE_PATCH_INFO_SIZE;
 	COPY_FROM_USER(&samples[free_sample], addr, offset,
-		       sizeof(awe_sample_info));
-	offset += sizeof(awe_sample_info);
+		       AWE_SAMPLE_INFO_SIZE);
+	offset += AWE_SAMPLE_INFO_SIZE;
 	if (size != samples[free_sample].size) {
 		ERRMSG(printk("AWE32 Warning: sample size differed (%d != %d)\n",
 		       (int)samples[free_sample].size, (int)size));
@@ -2015,14 +2241,51 @@
 {
 	if (!loaded_once) {
 		/* it's the first time */
-		last_sample = free_sample;
-		last_info = free_info;
+		if (current_sf_id == 1) {
+			last_sample = free_sample;
+			last_info = free_info;
+		}
 		current_sf_id++;
 		loaded_once = 1;
 	}
 }
 
 
+/*----------------------------------------------------------------*/
+
+static const char *readbuf_addr;
+static long readbuf_offs;
+static int readbuf_flags;
+
+/* initialize read buffer */
+static void
+awe_init_readbuf(const char *addr, long offset, int size, int mode_flags)
+{
+	readbuf_addr = addr;
+	readbuf_offs = offset;
+	readbuf_flags = mode_flags;
+}
+
+/* read directly from user buffer */
+static unsigned short
+awe_read_word(int pos)
+{
+	unsigned short c;
+	/* read from user buffer */
+	if (readbuf_flags & AWE_SAMPLE_8BITS) {
+		unsigned char cc;
+		GET_BYTE_FROM_USER(cc, readbuf_addr, readbuf_offs + pos);
+		c = cc << 8; /* convert 8bit -> 16bit */
+	} else {
+		GET_SHORT_FROM_USER(c, readbuf_addr, readbuf_offs + pos * 2);
+	}
+	if (readbuf_flags & AWE_SAMPLE_UNSIGNED)
+		c ^= 0x8000; /* unsigned -> signed */
+	return c;
+}
+
+
+
 #define BLANK_LOOP_START	8
 #define BLANK_LOOP_END		40
 #define BLANK_LOOP_SIZE		48
@@ -2035,7 +2298,6 @@
 	int i, truesize;
 	int rc;
 	unsigned long csum1, csum2;
-	DECL_INTR_FLAGS(flags);
 
 	/* be sure loop points start < end */
 	if (sp->loopstart > sp->loopend) {
@@ -2066,42 +2328,33 @@
 	sp->loopstart += free_mem_ptr + AWE_DRAM_OFFSET;
 	sp->loopend += free_mem_ptr + AWE_DRAM_OFFSET;
 
-	DISABLE_INTR(flags);
 	if ((rc = awe_open_dram_for_write(free_mem_ptr)) != 0) {
-		RESTORE_INTR(flags);
 		return rc;
 	}
 
+	awe_init_readbuf(addr, offset, size, sp->mode_flags);
 	csum1 = 0;
 	for (i = 0; i < size; i++) {
-		unsigned char cc;
 		unsigned short c;
-		if (sp->mode_flags & AWE_SAMPLE_8BITS) {
-			GET_BYTE_FROM_USER(cc, addr, offset); offset++;
-			c = cc << 8; /* convert 8bit -> 16bit */
-		} else {
-			GET_SHORT_FROM_USER(c, addr, offset); offset += 2;
-		}
-		if (sp->mode_flags & AWE_SAMPLE_UNSIGNED)
-			c ^= 0x8000; /* unsigned -> signed */
+		c = awe_read_word(i);
 		csum1 += c;
 		awe_write_dram(c);
 		if (i == sp->loopend &&
-		    (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP)) {
+		    (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP))) {
 			int looplen = sp->loopend - sp->loopstart;
 			/* copy reverse loop */
 			int k;
 			for (k = 0; k < looplen; k++) {
-				if (sp->mode_flags & AWE_SAMPLE_8BITS) {
-					GET_BYTE_FROM_USER(cc, addr, offset-k);
-					c = cc << 8;
-				} else {
-					GET_SHORT_FROM_USER(c, addr, offset-k*2);
-				}
-				if (sp->mode_flags & AWE_SAMPLE_UNSIGNED)
-					c ^= 0x8000;
+				/* non-buffered data */
+				c = awe_read_word(i - k);
 				awe_write_dram(c);
 			}
+			if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP) {
+				sp->end += looplen;
+			} else {
+				sp->start += looplen;
+				sp->end += looplen;
+			}
 		}
 	}
 
@@ -2117,19 +2370,15 @@
 	}
 
 	awe_close_dram();
-	RESTORE_INTR(flags);
 	if (sp->checksum_flag) {
 #ifdef AWE_CHECKSUM_DATA
 		if (sp->checksum_flag != 2 && csum1 != sp->checksum) {
 			ERRMSG(printk("AWE32: [%d] checksum mismatch on data %x:%x\n",
-			       free_sample,
-			       (int)samples[free_sample].checksum,
-			       (int)csum1));
+			       free_sample, (int)sp->checksum, (int)csum1));
 			return RET_ERROR(NO_DATA_ERR);
 		}
 #endif /* AWE_CHECKSUM_DATA */
 #ifdef AWE_CHECKSUM_MEMORY
-		DISABLE_INTR(flags);
 		if (awe_open_dram_for_read(free_mem_ptr) == 0) {
 			csum2 = 0;
 			for (i = 0; i < size; i++) {
@@ -2138,30 +2387,28 @@
 				csum2 += c;
 			}
 			awe_close_dram_for_read();
-			if (csum2 != samples[free_sample].checksum) {
-				RESTORE_INTR(flags);
+			if (csum1 != csum2) {
 				ERRMSG(printk("AWE32: [%d] checksum mismatch on DRAM %x:%x\n",
-					      free_sample,
-					      (int)samples[free_sample].checksum,
-					      (int)csum2));
+					      free_sample, (int)csum1, (int)csum2));
 				return RET_ERROR(NO_DATA_ERR);
 			}
 		}
-		RESTORE_INTR(flags);
 #endif /* AWE_CHECKSUM_MEMORY */
 	}
 	free_mem_ptr += sp->size;
 
 	/* re-initialize FM passthrough */
-	DISABLE_INTR(flags);
 	awe_init_fm();
 	awe_tweak();
-	RESTORE_INTR(flags);
 
 	return 0;
 }
 
 
+/*----------------------------------------------------------------*/
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+
 /* calculate GUS envelope time:
  * is this correct?  i have no idea..
  */
@@ -2219,7 +2466,7 @@
 	samples[free_sample].start = 0;
 	samples[free_sample].end = patch.len;
 	samples[free_sample].loopstart = patch.loop_start;
-	samples[free_sample].loopend = patch.loop_end;
+	samples[free_sample].loopend = patch.loop_end + 1;
 	samples[free_sample].size = patch.len;
 
 	/* set up mode flags */
@@ -2229,10 +2476,12 @@
 	if (patch.mode & WAVE_UNSIGNED)
 		samples[free_sample].mode_flags |= AWE_SAMPLE_UNSIGNED;
 	samples[free_sample].mode_flags |= AWE_SAMPLE_NO_BLANK;
-	if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP)))
+	if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK)))
 		samples[free_sample].mode_flags |= AWE_SAMPLE_SINGLESHOT;
 	if (patch.mode & WAVE_BIDIR_LOOP)
 		samples[free_sample].mode_flags |= AWE_SAMPLE_BIDIR_LOOP;
+	if (patch.mode & WAVE_LOOP_BACK)
+		samples[free_sample].mode_flags |= AWE_SAMPLE_REVERSE_LOOP;
 
 	DEBUG(0,printk("AWE32: [sample %d mode %x]\n", patch.instr_no,
 		       samples[free_sample].mode_flags));
@@ -2336,8 +2585,11 @@
 	return 0;
 }
 
+#endif  /* AWE_HAS_GUS_COMPATIBILITY */
 
-/* remove samples with current sf_id from instrument list */
+/*----------------------------------------------------------------*/
+
+/* remove samples with different sf_id from instrument list */
 static awe_voice_list *
 awe_get_removed_list(awe_voice_list *curp)
 {
@@ -2347,7 +2599,7 @@
 	prevp = &lastp;
 	for (maxc = AWE_MAX_INFOS;
 	     curp && maxc; curp = curp->next_instr, maxc--) {
-		if (curp->v.sf_id == current_sf_id)
+		if (curp->v.sf_id != 1)
 			*prevp = curp->next_instr;
 		else
 			prevp = &curp->next_instr;
@@ -2364,9 +2616,14 @@
 	int maxc;
 	int i;
 
+	/* no sample is loaded yet */
 	if (last_sample == free_sample && last_info == free_info)
 		return;
 
+	/* only the primary samples are loaded */
+	if (current_sf_id <= 1)
+		return;
+
 	/* remove the records from preset table */
 	for (i = 0; i < AWE_MAX_PRESETS; i++) {
 		prevp = &preset_table[i];
@@ -2388,7 +2645,7 @@
 
 	free_sample = last_sample;
 	free_info = last_info;
-	current_sf_id--;
+	current_sf_id = 1;
 	loaded_once = 0;
 }
 
@@ -2407,7 +2664,7 @@
 			vp->loopstart += samples[i].loopstart;
 			vp->loopend += samples[i].loopend;
 			/* copy mode flags */
-			vp->mode |= (samples[i].mode_flags << 6);
+			vp->mode = samples[i].mode_flags;
 			/* set index */
 			vp->index = i;
 			return i;
@@ -2421,16 +2678,17 @@
 static void
 awe_aftertouch(int dev, int voice, int pressure)
 {
-	DECL_INTR_FLAGS(flags);
-
 	DEBUG(2,printk("AWE32: [after(%d) %d]\n", voice, pressure));
-	if (voice < 0 || voice >= awe_max_voices)
-		return;
-	voices[voice].velocity = pressure;
-	awe_calc_volume(voice);
-	DISABLE_INTR(flags);
-	awe_set_volume(voice);
-	RESTORE_INTR(flags);
+	if (awe_channel_mode == 2) {
+		int note = (voice_alloc->map[voice] & 0xff) - 1;
+		awe_start_note(dev, voice, note + 0x80, pressure);
+
+	} else if (awe_channel_mode == 0) {
+		if (voice < 0 || voice >= awe_max_voices)
+			return;
+		voices[voice].velocity = pressure;
+		awe_set_voice_vol(voice, FALSE);
+	}
 }
 
 
@@ -2438,77 +2696,98 @@
 static void
 awe_controller(int dev, int voice, int ctrl_num, int value)
 {
-	DECL_INTR_FLAGS(flags);
+	awe_chan_info *cinfo;
 
-	if (voice < 0 || voice >= awe_max_voices)
-		return;
+	if (awe_channel_mode) {
+		if (awe_channel_mode == 2) /* get channel */
+			voice = voice_alloc->map[voice] >> 8;
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return;
+		cinfo = &channels[voice];
+	} else {
+		if (voice < 0 || voice >= awe_max_voices)
+			return;
+		cinfo = voices[voice].cinfo;
+	}
 
 	switch (ctrl_num) {
-	case CTL_BANK_SELECT:
+	case CTL_BANK_SELECT: /* SEQ1 control */
 		DEBUG(2,printk("AWE32: [bank(%d) %d]\n", voice, value));
-		voices[voice].bank = value;
+		cinfo->bank = value;
+		awe_set_instr(dev, voice, cinfo->instr);
 		break;
 
-	case CTRL_PITCH_BENDER:
+	case CTRL_PITCH_BENDER: /* SEQ1 V2 contorl */
 		DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, value));
 		/* zero centered */
-		voices[voice].bender = value;
-		awe_calc_pitch(voice);
-		DISABLE_INTR(flags);
-		awe_set_pitch(voice);
-		RESTORE_INTR(flags);
+		cinfo->bender = value;
+		awe_voice_change(voice, awe_set_voice_pitch);
 		break;
 
-	case CTRL_PITCH_BENDER_RANGE:
+	case CTRL_PITCH_BENDER_RANGE: /* SEQ1 V2 control */
 		DEBUG(2,printk("AWE32: [range(%d) %d]\n", voice, value));
-		/* sense x 100 */
-		voices[voice].bender_range = value;
+		/* value = sense x 100 */
+		cinfo->bender_range = value;
 		/* no audible pitch change yet.. */
 		break;
 
-	case CTL_EXPRESSION:
-		value /= 128;
-	case CTRL_EXPRESSION:
+	case CTL_EXPRESSION: /* SEQ1 control */
+		if (!awe_channel_mode)
+			value /= 128;
+	case CTRL_EXPRESSION: /* SEQ1 V2 control */
 		DEBUG(2,printk("AWE32: [expr(%d) %d]\n", voice, value));
 		/* 0 - 127 */
-		voices[voice].expression_vol = value;
-		awe_calc_volume(voice);
-		DISABLE_INTR(flags);
-		awe_set_volume(voice);
-		RESTORE_INTR(flags);
+		cinfo->expression_vol = value;
+		awe_voice_change(voice, awe_set_voice_vol);
 		break;
 
-	case CTL_PAN:
+	case CTL_PAN:	/* SEQ1 control */
 		DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, value));
 		/* (0-127) -> signed 8bit */
-		voices[voice].panning = value * 2 - 128;
-		DISABLE_INTR(flags);
-		awe_set_pan(voice, 0);
-		RESTORE_INTR(flags);
+		cinfo->panning = value * 2 - 128;
+		awe_voice_change(voice, awe_set_pan);
 		break;
 
-	case CTL_MAIN_VOLUME:
-		value = (value * 127) / 16383;
-	case CTRL_MAIN_VOLUME:
+	case CTL_MAIN_VOLUME:	/* SEQ1 control */
+		if (!awe_channel_mode)
+			value = (value * 100) / 16383;
+	case CTRL_MAIN_VOLUME:	/* SEQ1 V2 control */
 		DEBUG(2,printk("AWE32: [mainvol(%d) %d]\n", voice, value));
 		/* 0 - 127 */
-		voices[voice].main_vol = value;
-		awe_calc_volume(voice);
-		DISABLE_INTR(flags);
-		awe_set_volume(voice);
-		RESTORE_INTR(flags);
+		cinfo->main_vol = value;
+		awe_voice_change(voice, awe_set_voice_vol);
 		break;
 
 	case CTL_EXT_EFF_DEPTH: /* reverb effects: 0-127 */
 		DEBUG(2,printk("AWE32: [reverb(%d) %d]\n", voice, value));
-		FX_SET(voice, AWE_FX_REVERB, value * 2);
-		break;		
+		FX_SET(&cinfo->fx, AWE_FX_REVERB, value * 2);
+		break;
 
 	case CTL_CHORUS_DEPTH: /* chorus effects: 0-127 */
 		DEBUG(2,printk("AWE32: [chorus(%d) %d]\n", voice, value));
-		FX_SET(voice, AWE_FX_CHORUS, value * 2);
-		break;		
+		FX_SET(&cinfo->fx, AWE_FX_CHORUS, value * 2);
+		break;
 
+#ifdef AWE_ACCEPT_ALL_SOUNDS_CONTROLL
+	case 120:  /* all sounds off */
+		{int i; for (i = 0; i < AWE_NORMAL_VOICES; i++)
+			awe_terminate(i);
+		}
+		/*awe_reset(0);*/
+		break;
+	case 123:  /* all notes off */
+		{int i;
+		for (i = 0; i < awe_max_voices; i++)
+			awe_note_off(i);
+		}
+		break;
+#endif
+
+	case CTL_SUSTAIN: /* sustain the channel */
+		cinfo->sustained = value;
+		if (value == 0)
+			awe_voice_change(voice, awe_sustain_off);
+		break;
 
 	default:
 		DEBUG(0,printk("AWE32: [control(%d) ctrl=%d val=%d]\n",
@@ -2518,18 +2797,48 @@
 }
 
 
+
+/* change the voice parameters */
+static void awe_voice_change(int voice, fx_affect_func func)
+{
+	int i; 
+	if (! awe_channel_mode) {
+		func(voice, FALSE);
+		return;
+	}
+
+	for (i = 0; i < awe_max_voices; i++)
+		if (voices[i].ch == voice)
+			func(i, FALSE);
+}
+
+
+/* drop sustain */
+static void awe_sustain_off(int voice, int forced)
+{
+	if (voices[voice].state == AWE_ST_SUSTAINED)
+		awe_note_off(voice);
+}
+
+
 /* voice pan change (value = -128 - 127) */
 static void
 awe_panning(int dev, int voice, int value)
 {
-	DECL_INTR_FLAGS(flags);
-	if (voice >= 0 || voice < awe_max_voices) {
-		voices[voice].panning = value;
-		DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, voices[voice].panning));
-		DISABLE_INTR(flags);
-		awe_set_pan(voice, 0);
-		RESTORE_INTR(flags);
+	awe_chan_info *cinfo;
+	if (awe_channel_mode) {
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return;
+		cinfo = &channels[voice];
+	} else {
+		if (voice < 0 || voice >= awe_max_voices)
+			return;
+		cinfo = voices[voice].cinfo;
 	}
+
+	cinfo->panning = value;
+	DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, cinfo->panning));
+	awe_voice_change(voice, awe_set_pan);
 }
 
 
@@ -2542,63 +2851,173 @@
 }
 
 
+#ifndef AWE_NO_PATCHMGR
+/* patch manager */
+static int
+awe_patchmgr(int dev, struct patmgr_info *rec)
+{
+	FATALERR(printk("AWE32 Warning: patch manager control not supported\n"));
+	return 0;
+}
+#endif
+
+
 /* pitch wheel change: 0-16384 */
 static void
 awe_bender(int dev, int voice, int value)
 {
-	DECL_INTR_FLAGS(flags);
-
-	if (voice < 0 || voice >= awe_max_voices)
-		return;
+	awe_chan_info *cinfo;
+	if (awe_channel_mode) {
+		if (awe_channel_mode == 2)
+			voice = voice_alloc->map[voice] >> 8;
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return;
+		cinfo = &channels[voice];
+	} else {
+		if (voice < 0 || voice >= awe_max_voices)
+			return;
+		cinfo = voices[voice].cinfo;
+	}
 	/* convert to zero centered value */
-	voices[voice].bender = value - 8192;
-	DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, voices[voice].bender));
-	awe_calc_pitch(voice);
-	DISABLE_INTR(flags);
-	awe_set_pitch(voice);
-	RESTORE_INTR(flags);
+	cinfo->bender = value - 8192;
+	DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, cinfo->bender));
+	awe_voice_change(voice, awe_set_voice_pitch);
 }
 
-
-/* search an empty voice; used by sequencer2 */
+/* calculate the number of voices for this note & velocity */
 static int
-awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc)
+awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_info **vlist)
 {
-	int i, p, best = -1, best_time = 0x7fffffff;
+	int maxc;
+	int nvoices;
+	unsigned short sf_id;
 
-	p = alloc->ptr;
-	/* First look for a completely stopped voice */
+	sf_id = current_sf_id;
+	nvoices = 0;
+	for (maxc = AWE_MAX_INFOS;
+	     rec && maxc; rec = rec->next_instr, maxc--) {
+		if (rec->v.low <= note && note <= rec->v.high &&
+		    velocity >= rec->v.vellow && velocity <= rec->v.velhigh) {
+			if (nvoices == 0)
+				sf_id = rec->v.sf_id;
+			else if (rec->v.sf_id != sf_id)
+				continue;
+			vlist[nvoices] = &rec->v;
+			nvoices++;
+			if (nvoices >= AWE_MAX_VOICES)
+				break;
+		}
+	}
+	return nvoices;	
+}
 
-	for (i = 0; i < alloc->max_voice; i++) {
-		if (alloc->map[p] == 0) {
-			alloc->ptr = p;
-			return p;
+/* allocate voices corresponding note and velocity; supports multiple insts. */
+static void
+awe_alloc_multi_voices(int ch, int note, int velocity)
+{
+	int i, v, nvoices;
+	awe_voice_info *vlist[AWE_MAX_VOICES];
+
+	if (channels[ch].vrec == NULL && channels[ch].def_vrec == NULL)
+		awe_set_instr(0, ch, channels[ch].instr);
+
+	nvoices = awe_search_multi_voices(channels[ch].vrec, note, velocity, vlist);
+	if (nvoices == 0)
+		nvoices = awe_search_multi_voices(channels[ch].def_vrec, note, velocity, vlist);
+
+	/* allocate the voices */
+	current_alloc_time++;
+	for (i = 0; i < nvoices; i++) {
+		v = awe_clear_voice();
+		voices[v].key = AWE_CHAN_KEY(ch, note);
+		voices[v].ch = ch;
+		voices[v].time = current_alloc_time;
+		voices[v].cinfo = &channels[ch];
+		voices[v].sample = vlist[i];
+		voices[v].state = AWE_ST_MARK;
+	}
+
+	/* clear the mark in allocated voices */
+	for (i = 0; i < awe_max_voices; i++)
+		if (voices[i].state == AWE_ST_MARK)
+			voices[i].state = AWE_ST_OFF;
+}
+
+
+/* search an empty voice; used internally */
+static int
+awe_clear_voice(void)
+{
+	int i, time, best;
+
+	/* looking for the oldest empty voice */
+	best = -1;
+	time = 0x7fffffff;
+	for (i = 0; i < awe_max_voices; i++) {
+		if (voices[i].state == AWE_ST_OFF && voices[i].time < time) {
+			best = i;
+			time = voices[i].time;
 		}
-		if (alloc->alloc_times[p] < best_time) {
-			best = p;
-			best_time = alloc->alloc_times[p];
+	}
+	if (best >= 0)
+		return best;
+
+	/* looking for the oldest sustained voice */
+	time = 0x7fffffff;
+	for (i = 0; i < awe_max_voices; i++) {
+		if (voices[i].state == AWE_ST_SUSTAINED &&
+		    voices[i].time < time) {
+			best = i;
+			time = voices[i].time;
 		}
-		p = (p + 1) % alloc->max_voice;
+	}
+	if (best >= 0) {
+		awe_note_off(best);
+		return best;
 	}
 
-	/* Then look for a releasing voice */
-	for (i = 0; i < alloc->max_voice; i++) {
-		if (alloc->map[p] == 0xffff) {
-			alloc->ptr = p;
-			return p;
+	/* looking for the oldest voice not marked */
+	time = 0x7fffffff;
+	best = 0;
+	for (i = 0; i < awe_max_voices; i++) {
+		if (voices[i].state != AWE_ST_MARK && voices[i].time < time) {
+			best = i;
+			time = voices[i].time;
 		}
-		p = (p + 1) % alloc->max_voice;
 	}
+	/*awe_terminate(best);*/
+	awe_note_off(best);
+	return best;
+}
 
-	if (best >= 0)
-		p = best;
 
-	/* terminate the voice */
-	if (voices[p].state)
-		awe_terminate(p);
+/* allocate a voice corresponding note and velocity; single instrument */
+static void
+awe_alloc_one_voice(int voice, int note, int velocity)
+{
+	int nvoices;
+	awe_voice_info *vlist[AWE_MAX_VOICES];
+
+	if (voices[voice].cinfo->vrec == NULL && voices[voice].cinfo->def_vrec == NULL)
+		awe_set_instr(0, voice, voices[voice].cinfo->instr);
+
+	nvoices = awe_search_multi_voices(voices[voice].cinfo->vrec, note, velocity, vlist);
+	if (nvoices == 0)
+		nvoices = awe_search_multi_voices(voices[voice].cinfo->def_vrec, note, velocity, vlist);
 
-	alloc->ptr = p;
-	return p;
+	if (nvoices > 0) {
+		voices[voice].time = ++current_alloc_time;
+		voices[voice].sample = vlist[0]; /* use the first one */
+	}
+}
+
+
+/* search an empty voice; used by sequencer2 */
+static int
+awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc)
+{
+	awe_channel_mode = 2;
+	return awe_clear_voice();
 }
 
 
@@ -2610,18 +3029,27 @@
 	if (synth_devs[dev] == NULL ||
 	    (info = &synth_devs[dev]->chn_info[chn]) == NULL)
 		return;
+
 	if (voice < 0 || voice >= awe_max_voices)
 		return;
 
 	DEBUG(2,printk("AWE32: [setup(%d) ch=%d]\n", voice, chn));
-	voices[voice].expression_vol = info->controllers[CTL_EXPRESSION];
-	voices[voice].main_vol =
-		(info->controllers[CTL_MAIN_VOLUME] * 100) / 128; /* 0 - 127 */
-	voices[voice].panning =
+	channels[chn].expression_vol = info->controllers[CTL_EXPRESSION];
+	channels[chn].main_vol = info->controllers[CTL_MAIN_VOLUME];
+	channels[chn].panning =
 		info->controllers[CTL_PAN] * 2 - 128; /* signed 8bit */
-	voices[voice].bender = info->bender_value; /* zero center */
-	voices[voice].bank = info->controllers[CTL_BANK_SELECT];
-	awe_set_instr(dev, voice, info->pgm_num);
+	channels[chn].bender = info->bender_value; /* zero center */
+	channels[chn].bank = info->controllers[CTL_BANK_SELECT];
+	channels[chn].sustained = info->controllers[CTL_SUSTAIN];
+	if (info->controllers[CTL_EXT_EFF_DEPTH]) {
+		FX_SET(&channels[chn].fx, AWE_FX_REVERB,
+		       info->controllers[CTL_EXT_EFF_DEPTH] * 2);
+	}
+	if (info->controllers[CTL_CHORUS_DEPTH]) {
+		FX_SET(&channels[chn].fx, AWE_FX_CHORUS,
+		       info->controllers[CTL_CHORUS_DEPTH] * 2);
+	}
+	awe_set_instr(dev, chn, info->pgm_num);
 }
 
 
@@ -2637,9 +3065,10 @@
 
 	/* turn off envelope engines */
 	for (ch = 0; ch < AWE_MAX_VOICES; ch++) {
-		awe_poke(AWE_DCYSUSV(ch), 0x0080);
+		awe_poke(AWE_DCYSUSV(ch), 0x80);
 	}
   
+	/* reset all other parameters to zero */
 	for (ch = 0; ch < AWE_MAX_VOICES; ch++) {
 		awe_poke(AWE_ENVVOL(ch), 0);
 		awe_poke(AWE_ENVVAL(ch), 0);
@@ -2672,14 +3101,14 @@
 static void
 awe_init_dma(void)
 {
-	awe_poke_dw(AWE_SMALR, 0x00000000);
-	awe_poke_dw(AWE_SMARR, 0x00000000);
-	awe_poke_dw(AWE_SMALW, 0x00000000);
-	awe_poke_dw(AWE_SMARW, 0x00000000);
+	awe_poke_dw(AWE_SMALR, 0);
+	awe_poke_dw(AWE_SMARR, 0);
+	awe_poke_dw(AWE_SMALW, 0);
+	awe_poke_dw(AWE_SMARW, 0);
 }
 
 
-/* initialization arrays */
+/* initialization arrays; from ADIP */
 
 static unsigned short init1[128] = {
 	0x03ff, 0x0030,  0x07ff, 0x0130, 0x0bff, 0x0230,  0x0fff, 0x0330,
@@ -2718,6 +3147,7 @@
 	0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730,
 	0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30,
 	0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30,
+
 	0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330,
 	0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730,
 	0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30,
@@ -2806,35 +3236,39 @@
  * set up awe32 channels to some known state.
  */
 
+/* set the envelope & LFO parameters to the default values; see ADIP */
+static void
+awe_tweak_voice(int i)
+{
+	/* set all mod/vol envelope shape to minimum */
+	awe_poke(AWE_ENVVOL(i), 0x8000);
+	awe_poke(AWE_ENVVAL(i), 0x8000);
+	awe_poke(AWE_DCYSUS(i), 0x7F7F);
+	awe_poke(AWE_ATKHLDV(i), 0x7F7F);
+	awe_poke(AWE_ATKHLD(i), 0x7F7F);
+	awe_poke(AWE_PEFE(i), 0);  /* mod envelope height to zero */
+	awe_poke(AWE_LFO1VAL(i), 0x8000); /* no delay for LFO1 */
+	awe_poke(AWE_LFO2VAL(i), 0x8000);
+	awe_poke(AWE_IP(i), 0xE000);	/* no pitch shift */
+	awe_poke(AWE_IFATN(i), 0xFF00);	/* volume to minimum */
+	awe_poke(AWE_FMMOD(i), 0);
+	awe_poke(AWE_TREMFRQ(i), 0);
+	awe_poke(AWE_FM2FRQ2(i), 0);
+}
+
 static void
 awe_tweak(void)
 {
 	int i;
-
-	/* Set the envelope engine parameters to the "default" values for
-	   simply playing back unarticulated audio at 44.1kHz.  Set all
-	   of the channels: */
-
-	for (i = 0; i < AWE_MAX_VOICES; i++) {
-		awe_poke(AWE_ENVVOL(i)   , 0x8000);
-		awe_poke(AWE_ENVVAL(i)   , 0x8000);
-		awe_poke(AWE_DCYSUS(i)   , 0x7F7F);
-		awe_poke(AWE_ATKHLDV(i)  , 0x7F7F);
-		awe_poke(AWE_LFO1VAL(i)  , 0x8000);
-		awe_poke(AWE_ATKHLD(i)   , 0x7F7F);
-		awe_poke(AWE_LFO2VAL(i)  , 0x8000);
-		awe_poke(AWE_IP(i)       , 0xE000);
-		awe_poke(AWE_IFATN(i)    , 0xFF00);
-		awe_poke(AWE_PEFE(i)     , 0x0000);
-		awe_poke(AWE_FMMOD(i)    , 0x0000);
-		awe_poke(AWE_TREMFRQ(i)  , 0x0010);
-		awe_poke(AWE_FM2FRQ2(i)  , 0x0010);
-	}
+	/* reset all channels */
+	for (i = 0; i < awe_max_voices; i++)
+		awe_tweak_voice(i);
 }
 
 
 /*
- *  initializes the FM section of AWE32
+ *  initializes the FM section of AWE32;
+ *   see Vince Vu's unofficial AWE32 programming guide
  */
 
 static void
@@ -2845,39 +3279,32 @@
 	if (awe_mem_size <= 0)
 		return;
 #endif
-	DEBUG(0,printk("AWE32: initializing FM\n"));
+	DEBUG(3,printk("AWE32: initializing FM\n"));
 
 	/* Initialize the last two channels for DRAM refresh and producing
 	   the reverb and chorus effects for Yamaha OPL-3 synthesizer */
 
-	awe_poke(   AWE_DCYSUSV(30)   , 0x0080);
-	awe_poke_dw(AWE_PSST(30)      , 0xFFFFFFE0);
-	awe_poke_dw(AWE_CSL(30)       , 0xFFFFFFE8);
-	awe_poke_dw(AWE_PTRX(30)      , 0x00FFFF00);
-	awe_poke_dw(AWE_CPF(30)       , 0x00000000);
-	awe_poke_dw(AWE_CCCA(30)      , 0x00FFFFE3);
-
-	awe_poke(   AWE_DCYSUSV(31)   , 0x0080);
-	awe_poke_dw(AWE_PSST(31)      , 0x00FFFFE0);
-	awe_poke_dw(AWE_CSL(31)       , 0xFFFFFFE8);
-	awe_poke_dw(AWE_PTRX(31)      , 0x00FFFF00);
-	awe_poke_dw(AWE_CPF(31)       , 0x00000000);
-	awe_poke_dw(AWE_CCCA(31)      , 0x00FFFFE3);
-
-	/* Timing loop */
-
-	/* PTRX is 32 bit long but do not write to the MS byte */
-	awe_poke(AWE_PTRX(30)      , 0x0000);
-
-	while(! (inw(awe_base-0x620+Pointer) & 0x1000));
-	while(   inw(awe_base-0x620+Pointer) & 0x1000);
-
-	/* now write the MS byte of PTRX */
-	OUTW(0x4828, awe_base-0x620+Data0+0x002);
-
-	awe_poke(   AWE_IFATN(28)     , 0x0000);
-	awe_poke_dw(AWE_VTFT(30)      , 0x8000FFFF);
-	awe_poke_dw(AWE_VTFT(31)      , 0x8000FFFF);
+	/* 31: FM left channel, 0xffffe0-0xffffe8 */
+	awe_poke(AWE_DCYSUSV(30), 0x80);
+	awe_poke_dw(AWE_PSST(30), 0xFFFFFFE0); /* full left */
+	awe_poke_dw(AWE_CSL(30), 0x00FFFFE8 |
+		    (DEF_FM_CHORUS_DEPTH << 24));
+	awe_poke_dw(AWE_PTRX(30), (DEF_FM_REVERB_DEPTH << 8));
+	awe_poke_dw(AWE_CPF(30), 0);
+	awe_poke_dw(AWE_CCCA(30), 0x00FFFFE3);
+
+	/* 32: FM right channel, 0xfffff0-0xfffff8 */
+	awe_poke(AWE_DCYSUSV(31), 0x80);
+	awe_poke_dw(AWE_PSST(31), 0x00FFFFF0); /* full right */
+	awe_poke_dw(AWE_CSL(31), 0x00FFFFF8 |
+		    (DEF_FM_CHORUS_DEPTH << 24));
+	awe_poke_dw(AWE_PTRX(31), (DEF_FM_REVERB_DEPTH << 8));
+	awe_poke_dw(AWE_CPF(31), 0);
+	awe_poke_dw(AWE_CCCA(31), 0x00FFFFF3);
+
+	/* skew volume & cutoff */
+	awe_poke_dw(AWE_VTFT(30), 0x8000FFFF);
+	awe_poke_dw(AWE_VTFT(31), 0x8000FFFF);
 
 	/* change maximum channels to 30 */
 	awe_max_voices = AWE_NORMAL_VOICES;
@@ -2933,22 +3360,19 @@
 static void
 awe_open_dram_for_check(void)
 {
-	int k;
-	unsigned long scratch;
-
-	awe_poke(AWE_HWCF2 , 0x0020);
-
-	for (k = 0; k < AWE_NORMAL_VOICES; k++) {
-		awe_poke(AWE_DCYSUSV(k), 0x0080);
-		awe_poke_dw(AWE_VTFT(k), 0x00000000);
-		awe_poke_dw(AWE_CVCF(k), 0x00000000);
-		awe_poke_dw(AWE_PTRX(k), 0x40000000);
-		awe_poke_dw(AWE_CPF(k), 0x40000000);
-		awe_poke_dw(AWE_PSST(k), 0x00000000);
-		awe_poke_dw(AWE_CSL(k), 0x00000000);
-		scratch = (((k&1) << 9) + 0x400);
-		scratch = scratch << 16;
-		awe_poke_dw(AWE_CCCA(k), scratch);
+	int i;
+	for (i = 0; i < AWE_NORMAL_VOICES; i++) {
+		awe_poke(AWE_DCYSUSV(i), 0x80);
+		awe_poke_dw(AWE_VTFT(i), 0);
+		awe_poke_dw(AWE_CVCF(i), 0);
+		awe_poke_dw(AWE_PTRX(i), 0x40000000);
+		awe_poke_dw(AWE_CPF(i), 0x40000000);
+		awe_poke_dw(AWE_PSST(i), 0);
+		awe_poke_dw(AWE_CSL(i), 0);
+		if (i & 1) /* DMA write */
+			awe_poke_dw(AWE_CCCA(i), 0x06000000);
+		else	   /* DMA read */
+			awe_poke_dw(AWE_CCCA(i), 0x04000000);
 	}
 }
 
@@ -3037,13 +3461,6 @@
 static void
 awe_write_dram(unsigned short c)
 {
-	int k;
-	/* wait until FULL bit in SMAxW register be false */
-	for (k = 0; k < 10000; k++) {
-		if (!(awe_peek_dw(AWE_SMALW) & 0x80000000))
-			break;
-		awe_wait(10);
-	}
 	awe_poke(AWE_SMLD, c);
 }
 
@@ -3051,27 +3468,34 @@
  * detect presence of AWE32 and check memory size
  *================================================================*/
 
+/* detect emu8000 chip on the specified address; from VV's guide */
+
+static int
+awe_detect_base(int addr)
+{
+	awe_base = addr;
+	if ((awe_peek(AWE_U1) & 0x000F) != 0x000C)
+		return 0;
+	if ((awe_peek(AWE_HWCF1) & 0x007E) != 0x0058)
+		return 0;
+	if ((awe_peek(AWE_HWCF2) & 0x0003) != 0x0003)
+		return 0;
+        DEBUG(0,printk("AWE32 found at %x\n", awe_base));
+	return 1;
+}
+	
 static int
 awe_detect(void)
 {
+	int base;
 #ifdef AWE_DEFAULT_BASE_ADDR
-	awe_base = AWE_DEFAULT_BASE_ADDR;
-	if (((awe_peek(AWE_U1) & 0x000F) == 0x000C) &&
-	    ((awe_peek(AWE_HWCF1) & 0x007E) == 0x0058) &&
-	    ((awe_peek(AWE_HWCF2) & 0x0003) == 0x0003))
+	if (awe_detect_base(AWE_DEFAULT_BASE_ADDR))
 		return 1;
 #endif
 	if (awe_base == 0) {
-		for (awe_base = 0x620; awe_base <= 0x680; awe_base += 0x20) {
-			if ((awe_peek(AWE_U1) & 0x000F) != 0x000C)
-				continue;
-			if ((awe_peek(AWE_HWCF1) & 0x007E) != 0x0058)
-				continue;
-			if ((awe_peek(AWE_HWCF2) & 0x0003) != 0x0003)
-			        continue;
-		        DEBUG(0,printk("AWE32 found at %x\n", awe_base));
-			return 1;
-		}
+		for (base = 0x620; base <= 0x680; base += 0x20)
+			if (awe_detect_base(base))
+				return 1;
 	}
 	FATALERR(printk("AWE32 not found\n"));
 	awe_base = 0;
@@ -3082,28 +3506,40 @@
 /*================================================================
  * check dram size on AWE board
  *================================================================*/
+
+/* any three numbers you like */
+#define UNIQUE_ID1	0x1234
+#define UNIQUE_ID2	0x4321
+#define UNIQUE_ID3	0xFFFF
+
 static int
 awe_check_dram(void)
 {
 	awe_open_dram_for_check();
 
-	awe_poke_dw(AWE_SMALW    , 0x00200000);     /* DRAM start address */
-	awe_poke(   AWE_SMLD     , 0x1234);
-	awe_poke(   AWE_SMLD     , 0x7777);
+	/* set up unique two id numbers */
+	awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET);
+	awe_poke(AWE_SMLD, UNIQUE_ID1);
+	awe_poke(AWE_SMLD, UNIQUE_ID2);
 
 	awe_mem_size = 0;
-	while (awe_mem_size < 28*1024) {     /* 28 MB is max onboard memory */
+	while (awe_mem_size < AWE_MAX_DRAM_SIZE) {
 		awe_wait(2);
-		awe_poke_dw(AWE_SMALR, 0x00200000); /* Address for reading */
-		awe_peek(AWE_SMLD);		/* Discard stale data  */
-		if (awe_peek(AWE_SMLD) != 0x1234)
+		/* read a data on the DRAM start address */
+		awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET);
+		awe_peek(AWE_SMLD); /* discard stale data  */
+		if (awe_peek(AWE_SMLD) != UNIQUE_ID1)
 			break;
-		if (awe_peek(AWE_SMLD) != 0x7777)
+		if (awe_peek(AWE_SMLD) != UNIQUE_ID2)
 			break;
-		awe_mem_size += 32;
-		/* Address for writing */
-		awe_poke_dw(AWE_SMALW, 0x00200000+awe_mem_size*512L);
-		awe_poke(AWE_SMLD, 0xFFFF);
+		awe_mem_size += 32;  /* increment 32 Kbytes */
+		/* Write a unique data on the test address;
+		 * if the address is out of range, the data is written on
+		 * 0x200000(=AWE_DRAM_OFFSET).  Then the two id words are
+		 * broken by this data.
+		 */
+		awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET + awe_mem_size*512L);
+		awe_poke(AWE_SMLD, UNIQUE_ID3);
 	}
 	awe_close_dram();
 
@@ -3119,113 +3555,107 @@
 
 
 /*================================================================
- * chorus and reverb controls
+ * chorus and reverb controls; from VV's guide
  *================================================================*/
 
-static unsigned short ChorusEffects[24] =
-{
-  0xE600,0x03F6,0xBC2C,0xE608,0x031A,0xBC6E,0xE610,0x031A,
-  0xBC84,0xE620,0x0269,0xBC6E,0xE680,0x04D3,0xBCA6,0xE6E0,
-  0x044E,0xBC37,0xE600,0x0B06,0xBC00,0xE6C0,0x0B06,0xBC00
+/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */
+static unsigned short chorus_parm[8][5] = {
+	{0xE600, 0x03F6, 0xBC2C ,0x0000, 0x006D}, /* chorus 1 */
+	{0xE608, 0x031A, 0xBC6E, 0x0000, 0x017C}, /* chorus 2 */
+	{0xE610, 0x031A, 0xBC84, 0x0000, 0x0083}, /* chorus 3 */
+	{0xE620, 0x0269, 0xBC6E, 0x0000, 0x017C}, /* chorus 4 */
+	{0xE680, 0x04D3, 0xBCA6, 0x0000, 0x005B}, /* feedback */
+	{0xE6E0, 0x044E, 0xBC37, 0x0000, 0x0026}, /* flanger */
+	{0xE600, 0x0B06, 0xBC00, 0xE000, 0x0083}, /* short delay */
+	{0xE6C0, 0x0B06, 0xBC00, 0xE000, 0x0083}, /* short delay + feedback */
 };
 
-static unsigned long ChorusEffects2[] =
+static void awe_set_chorus_mode(int effect)
 {
-  0x0000 ,0x006D,0x8000,0x0000,0x0000 ,0x017C,0x8000,0x0000,
-  0x0000 ,0x0083,0x8000,0x0000,0x0000 ,0x017C,0x8000,0x0000,
-  0x0000 ,0x005B,0x8000,0x0000,0x0000 ,0x0026,0x8000,0x0000,
-  0x6E000,0x0083,0x8000,0x0000,0x6E000,0x0083,0x8000,0x0000
-};
+	awe_poke(AWE_INIT3(9), chorus_parm[effect][0]);
+	awe_poke(AWE_INIT3(12), chorus_parm[effect][1]);
+	awe_poke(AWE_INIT4(3), chorus_parm[effect][2]);
+	awe_poke_dw(AWE_HWCF4, (unsigned long)chorus_parm[effect][3]);
+	awe_poke_dw(AWE_HWCF5, (unsigned long)chorus_parm[effect][4]);
+	awe_poke_dw(AWE_HWCF6, 0x8000);
+	awe_poke_dw(AWE_HWCF7, 0x0000);
+}
 
-static unsigned short ChorusCommand[14] =
-{
-  0x69,0xA20,0x6C,0xA20,0x63,0xA22,0x29,
-  0xA20,0x2A,0xA20,0x2D,0xA20,0x2E,0xA20
-};
+/*----------------------------------------------------------------*/
 
-static unsigned short ReverbEffects[224] =
-{
-  /* Room 1 */
-  0xB488,0xA450,0x9550,0x84B5,0x383A,0x3EB5,0x72F4,
-  0x72A4,0x7254,0x7204,0x7204,0x7204,0x4416,0x4516,
-  0xA490,0xA590,0x842A,0x852A,0x842A,0x852A,0x8429,
-  0x8529,0x8429,0x8529,0x8428,0x8528,0x8428,0x8528,
-  /* Room 2 */
-  0xB488,0xA458,0x9558,0x84B5,0x383A,0x3EB5,0x7284,
-  0x7254,0x7224,0x7224,0x7254,0x7284,0x4448,0x4548,
-  0xA440,0xA540,0x842A,0x852A,0x842A,0x852A,0x8429,
-  0x8529,0x8429,0x8529,0x8428,0x8528,0x8428,0x8528,
-  /* Room 3 */
-  0xB488,0xA460,0x9560,0x84B5,0x383A,0x3EB5,0x7284,
-  0x7254,0x7224,0x7224,0x7254,0x7284,0x4416,0x4516,
-  0xA490,0xA590,0x842C,0x852C,0x842C,0x852C,0x842B,
-  0x852B,0x842B,0x852B,0x842A,0x852A,0x842A,0x852A,
-  /* Hall 1 */
-  0xB488,0xA470,0x9570,0x84B5,0x383A,0x3EB5,0x7284,
-  0x7254,0x7224,0x7224,0x7254,0x7284,0x4448,0x4548,
-  0xA440,0xA540,0x842B,0x852B,0x842B,0x852B,0x842A,
-  0x852A,0x842A,0x852A,0x8429,0x8529,0x8429,0x8529,
-  /* Hall 2 */
-  0xB488,0xA470,0x9570,0x84B5,0x383A,0x3EB5,0x7254,
-  0x7234,0x7224,0x7254,0x7264,0x7294,0x44C3,0x45C3,
-  0xA404,0xA504,0x842A,0x852A,0x842A,0x852A,0x8429,
-  0x8529,0x8429,0x8529,0x8428,0x8528,0x8428,0x8528,
-  /* Plate */
-  0xB4FF,0xA470,0x9570,0x84B5,0x383A,0x3EB5,0x7234,
-  0x7234,0x7234,0x7234,0x7234,0x7234,0x4448,0x4548,
-  0xA440,0xA540,0x842A,0x852A,0x842A,0x852A,0x8429,
-  0x8529,0x8429,0x8529,0x8428,0x8528,0x8428,0x8528,
-  /* Delay */
-  0xB4FF,0xA470,0x9500,0x84B5,0x333A,0x39B5,0x7204,
-  0x7204,0x7204,0x7204,0x7204,0x72F4,0x4400,0x4500,
-  0xA4FF,0xA5FF,0x8420,0x8520,0x8420,0x8520,0x8420,
-  0x8520,0x8420,0x8520,0x8420,0x8520,0x8420,0x8520,
-  /* Panning Delay */
-  0xB4FF,0xA490,0x9590,0x8474,0x333A,0x39B5,0x7204,
-  0x7204,0x7204,0x7204,0x7204,0x72F4,0x4400,0x4500,
-  0xA4FF,0xA5FF,0x8420,0x8520,0x8420,0x8520,0x8420,
-  0x8520,0x8420,0x8520,0x8420,0x8520,0x8420,0x8520
+/* reverb mode settings; write the following 28 data of 16 bit length
+ *   on the corresponding ports in the reverb_cmds array
+ */
+static unsigned short reverb_parm[8][28] = {
+{  /* room 1 */
+	0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4,
+	0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516,
+	0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+},
+{  /* room 2 */
+	0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
+	0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+},
+{  /* room 3 */
+	0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516,
+	0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B,
+	0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A,
+},
+{  /* hall 1 */
+	0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
+	0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A,
+	0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529,
+},
+{  /* hall 2 */
+	0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254,
+	0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3,
+	0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+},
+{  /* plate */
+	0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234,
+	0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548,
+	0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+},
+{  /* delay */
+	0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204,
+	0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
+	0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
+	0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
+},
+{  /* panning delay */
+	0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204,
+	0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
+	0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
+	0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
+},
 };
 
-static unsigned short ReverbCommand[56] =
-{
-  0x43,0xA20,0x45,0xA20,0x7F,0xA22,0x47,0xA20,
-  0x54,0xA22,0x56,0xA22,0x4F,0xA20,0x57,0xA20,
-  0x5F,0xA20,0x47,0xA22,0x4F,0xA22,0x57,0xA22,
-  0x5D,0xA22,0x5F,0xA22,0x61,0xA20,0x63,0xA20,
-  0x49,0xA20,0x4B,0xA20,0x51,0xA20,0x53,0xA20,
-  0x59,0xA20,0x5B,0xA20,0x41,0xA22,0x43,0xA22,
-  0x49,0xA22,0x4B,0xA22,0x51,0xA22,0x53,0xA22
+static struct ReverbCmdPair {
+	unsigned short cmd, port;
+} reverb_cmds[28] = {
+  {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)},
+  {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)},
+  {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)},
+  {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)},
+  {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)},
+  {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)},
+  {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)},
 };
 
-static void awe_set_chorus_mode(int effect)
-{
-	int k;
-	DECL_INTR_FLAGS(flags);
-
-	DISABLE_INTR(flags);
-	for (k = 0; k < 3; k++)
-		awe_poke(ChorusCommand[k*2],
-			 ChorusCommand[k*2+1],
-			 ChorusEffects[k+effect*3]);
-	for (k = 0; k < 4; k++)
-		awe_poke_dw(ChorusCommand[6+k*2],
-			    ChorusCommand[6+k*2+1],
-			    ChorusEffects2[k+effect*4]);
-	RESTORE_INTR(flags);
-}
 
 static void awe_set_reverb_mode(int effect)
 {
-	int k;
-	DECL_INTR_FLAGS(flags);
-
-	DISABLE_INTR(flags);
-	for (k = 0; k < 28; k++)
-		awe_poke(ReverbCommand[k*2],
-			 ReverbCommand[k*2+1],
-			 ReverbEffects[k+effect*28]);
-	RESTORE_INTR(flags);
+	int i;
+	for (i = 0; i < 28; i++)
+		awe_poke(reverb_cmds[i].cmd, reverb_cmds[i].port,
+			 reverb_parm[effect][i]);
 }
 
 #endif /* CONFIG_AWE32_SYNTH */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov