summaryrefslogtreecommitdiffstats
path: root/sound/oss/awe_wave.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/oss/awe_wave.c')
-rw-r--r--sound/oss/awe_wave.c6147
1 files changed, 6147 insertions, 0 deletions
diff --git a/sound/oss/awe_wave.c b/sound/oss/awe_wave.c
new file mode 100644
index 000000000000..d2b9beda8ace
--- /dev/null
+++ b/sound/oss/awe_wave.c
@@ -0,0 +1,6147 @@
+/*
+ * sound/awe_wave.c
+ *
+ * The low level driver for the AWE32/SB32/AWE64 wave table synth.
+ * version 0.4.4; Jan. 4, 2000
+ *
+ * Copyright (C) 1996-2000 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.
+ */
+
+/*
+ * Changelog:
+ * Aug 18, 2003, Adam Belay <ambx1@neo.rr.com>
+ * - detection code rewrite
+ */
+
+#include <linux/awe_voice.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/pnp.h>
+
+#include "sound_config.h"
+
+#include "awe_wave.h"
+#include "awe_hw.h"
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+#include "tuning.h"
+#include <linux/ultrasound.h>
+#endif
+
+/*
+ * debug message
+ */
+
+#ifdef AWE_DEBUG_ON
+#define DEBUG(LVL,XXX) {if (ctrls[AWE_MD_DEBUG_MODE] > LVL) { XXX; }}
+#define ERRMSG(XXX) {if (ctrls[AWE_MD_DEBUG_MODE]) { XXX; }}
+#define FATALERR(XXX) XXX
+#else
+#define DEBUG(LVL,XXX) /**/
+#define ERRMSG(XXX) XXX
+#define FATALERR(XXX) XXX
+#endif
+
+/*
+ * bank and voice record
+ */
+
+typedef struct _sf_list sf_list;
+typedef struct _awe_voice_list awe_voice_list;
+typedef struct _awe_sample_list awe_sample_list;
+
+/* soundfont record */
+struct _sf_list {
+ unsigned short sf_id; /* id number */
+ unsigned short type; /* lock & shared flags */
+ int num_info; /* current info table index */
+ int num_sample; /* current sample table index */
+ int mem_ptr; /* current word byte pointer */
+ awe_voice_list *infos, *last_infos; /* instruments */
+ awe_sample_list *samples, *last_samples; /* samples */
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+ sf_list *shared; /* shared list */
+ unsigned char name[AWE_PATCH_NAME_LEN]; /* sharing id */
+#endif
+ sf_list *next, *prev;
+};
+
+/* instrument list */
+struct _awe_voice_list {
+ awe_voice_info v; /* instrument information */
+ sf_list *holder; /* parent sf_list of this record */
+ unsigned char bank, instr; /* preset number information */
+ char type, disabled; /* type=normal/mapped, disabled=boolean */
+ awe_voice_list *next; /* linked list with same sf_id */
+ awe_voice_list *next_instr; /* instrument list */
+ awe_voice_list *next_bank; /* hash table list */
+};
+
+/* voice list type */
+#define V_ST_NORMAL 0
+#define V_ST_MAPPED 1
+
+/* sample list */
+struct _awe_sample_list {
+ awe_sample_info v; /* sample information */
+ sf_list *holder; /* parent sf_list of this record */
+ awe_sample_list *next; /* linked list with same sf_id */
+};
+
+/* sample and information table */
+static int current_sf_id; /* current number of fonts */
+static int locked_sf_id; /* locked position */
+static sf_list *sfhead, *sftail; /* linked-lists */
+
+#define awe_free_mem_ptr() (sftail ? sftail->mem_ptr : 0)
+#define awe_free_info() (sftail ? sftail->num_info : 0)
+#define awe_free_sample() (sftail ? sftail->num_sample : 0)
+
+#define AWE_MAX_PRESETS 256
+#define AWE_DEFAULT_PRESET 0
+#define AWE_DEFAULT_BANK 0
+#define AWE_DEFAULT_DRUM 0
+#define AWE_DRUM_BANK 128
+
+#define MAX_LAYERS AWE_MAX_VOICES
+
+/* preset table index */
+static awe_voice_list *preset_table[AWE_MAX_PRESETS];
+
+/*
+ * voice table
+ */
+
+/* effects table */
+typedef struct FX_Rec { /* channel effects */
+ unsigned char flags[AWE_FX_END];
+ short val[AWE_FX_END];
+} FX_Rec;
+
+
+/* channel parameters */
+typedef struct _awe_chan_info {
+ int channel; /* channel number */
+ 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) */
+ int chan_press; /* channel pressure */
+ int sustained; /* sustain status in MIDI */
+ FX_Rec fx; /* effects */
+ FX_Rec fx_layer[MAX_LAYERS]; /* layer effects */
+} awe_chan_info;
+
+/* voice parameters */
+typedef struct _voice_info {
+ int state;
+#define AWE_ST_OFF (1<<0) /* no sound */
+#define AWE_ST_ON (1<<1) /* playing */
+#define AWE_ST_STANDBY (1<<2) /* stand by for playing */
+#define AWE_ST_SUSTAINED (1<<3) /* sustained */
+#define AWE_ST_MARK (1<<4) /* marked for allocation */
+#define AWE_ST_DRAM (1<<5) /* DRAM read/write */
+#define AWE_ST_FM (1<<6) /* reserved for FM */
+#define AWE_ST_RELEASED (1<<7) /* released */
+
+ int ch; /* midi channel */
+ int key; /* internal key for search */
+ int layer; /* layer number (for channel mode only) */
+ int time; /* allocated time */
+ awe_chan_info *cinfo; /* channel info */
+
+ int note; /* midi key (0-127) */
+ int velocity; /* midi velocity (0-127) */
+ int sostenuto; /* sostenuto on/off */
+ awe_voice_info *sample; /* assigned voice */
+
+ /* EMU8000 parameters */
+ int apitch; /* pitch parameter */
+ int avol; /* volume parameter */
+ int apan; /* panning parameter */
+ int acutoff; /* cutoff parameter */
+ short aaux; /* aux word */
+} voice_info;
+
+/* voice information */
+static voice_info voices[AWE_MAX_VOICES];
+
+#define IS_NO_SOUND(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_RELEASED|AWE_ST_STANDBY|AWE_ST_SUSTAINED))
+#define IS_NO_EFFECT(v) (voices[v].state != AWE_ST_ON)
+#define IS_PLAYING(v) (voices[v].state & (AWE_ST_ON|AWE_ST_SUSTAINED|AWE_ST_RELEASED))
+#define IS_EMPTY(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_MARK|AWE_ST_DRAM|AWE_ST_FM))
+
+
+/* MIDI channel effects information (for hw control) */
+static awe_chan_info channels[AWE_MAX_CHANNELS];
+
+
+/*
+ * global variables
+ */
+
+#ifndef AWE_DEFAULT_BASE_ADDR
+#define AWE_DEFAULT_BASE_ADDR 0 /* autodetect */
+#endif
+
+#ifndef AWE_DEFAULT_MEM_SIZE
+#define AWE_DEFAULT_MEM_SIZE -1 /* autodetect */
+#endif
+
+static int io = AWE_DEFAULT_BASE_ADDR; /* Emu8000 base address */
+static int memsize = AWE_DEFAULT_MEM_SIZE; /* memory size in Kbytes */
+#ifdef CONFIG_PNP
+static int isapnp = -1;
+#else
+static int isapnp;
+#endif
+
+MODULE_AUTHOR("Takashi Iwai <iwai@ww.uni-erlangen.de>");
+MODULE_DESCRIPTION("SB AWE32/64 WaveTable driver");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "base i/o port of Emu8000");
+module_param(memsize, int, 0);
+MODULE_PARM_DESC(memsize, "onboard DRAM size in Kbytes");
+module_param(isapnp, bool, 0);
+MODULE_PARM_DESC(isapnp, "use ISAPnP detection");
+
+/* DRAM start offset */
+static int awe_mem_start = AWE_DRAM_OFFSET;
+
+/* maximum channels for playing */
+static int awe_max_voices = AWE_MAX_VOICES;
+
+static int patch_opened; /* sample already loaded? */
+
+static char atten_relative = FALSE;
+static short atten_offset;
+
+static int awe_present = FALSE; /* awe device present? */
+static int awe_busy = FALSE; /* awe device opened? */
+
+static int my_dev = -1;
+
+#define DEFAULT_DRUM_FLAGS ((1 << 9) | (1 << 25))
+#define IS_DRUM_CHANNEL(c) (drum_flags & (1 << (c)))
+#define DRUM_CHANNEL_ON(c) (drum_flags |= (1 << (c)))
+#define DRUM_CHANNEL_OFF(c) (drum_flags &= ~(1 << (c)))
+static unsigned int drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */
+
+static int playing_mode = AWE_PLAY_INDIRECT;
+#define SINGLE_LAYER_MODE() (playing_mode == AWE_PLAY_INDIRECT || playing_mode == AWE_PLAY_DIRECT)
+#define MULTI_LAYER_MODE() (playing_mode == AWE_PLAY_MULTI || playing_mode == AWE_PLAY_MULTI2)
+
+static int current_alloc_time; /* voice allocation index for channel mode */
+
+static struct synth_info awe_info = {
+ "AWE32 Synth", /* name */
+ 0, /* device */
+ SYNTH_TYPE_SAMPLE, /* synth_type */
+ SAMPLE_TYPE_AWE32, /* synth_subtype */
+ 0, /* perc_mode (obsolete) */
+ AWE_MAX_VOICES, /* nr_voices */
+ 0, /* nr_drums (obsolete) */
+ 400 /* instr_bank_size */
+};
+
+
+static struct voice_alloc_info *voice_alloc; /* set at initialization */
+
+
+/*
+ * function prototypes
+ */
+
+static int awe_request_region(void);
+static void awe_release_region(void);
+
+static void awe_reset_samples(void);
+/* emu8000 chip i/o access */
+static void setup_ports(int p1, int p2, int p3);
+static void awe_poke(unsigned short cmd, unsigned short port, unsigned short data);
+static void awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data);
+static unsigned short awe_peek(unsigned short cmd, unsigned short port);
+static unsigned int awe_peek_dw(unsigned short cmd, unsigned short port);
+static void awe_wait(unsigned short delay);
+
+/* initialize emu8000 chip */
+static void awe_initialize(void);
+
+/* set voice parameters */
+static void awe_init_ctrl_parms(int init_all);
+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);*/
+static int calc_parm_hold(int msec);
+static int calc_parm_attack(int msec);
+static int calc_parm_decay(int msec);
+static int calc_parm_search(int msec, short *table);
+#endif /* gus compat */
+
+/* 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_note_off_all(int do_sustain);
+
+/* calculate voice parameters */
+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, int forced);
+static void awe_fx_tremfrq(int voice, int forced);
+static void awe_fx_fm2frq2(int voice, int forced);
+static void awe_fx_filterQ(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_update_volume(void);
+static void awe_change_master_volume(short val);
+static void awe_voice_init(int voice, int init_all);
+static void awe_channel_init(int ch, int init_all);
+static void awe_fx_init(int ch);
+static void awe_send_effect(int voice, int layer, int type, int val);
+static void awe_modwheel_change(int voice, int value);
+
+/* sequencer interface */
+static int awe_open(int dev, int mode);
+static void awe_close(int dev);
+static int awe_ioctl(int dev, unsigned int cmd, void __user * arg);
+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 __user *addr,
+ int offs, int count, int pmgr_flag);
+static void awe_aftertouch(int dev, int voice, int pressure);
+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);
+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);
+
+#define awe_key_pressure(dev,voice,key,press) awe_start_note(dev,voice,(key)+128,press)
+
+/* 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_sostenuto_on(int voice, int forced);
+static void awe_sustain_off(int voice, int forced);
+static void awe_terminate_and_init(int voice, int forced);
+
+/* voice search */
+static int awe_search_key(int bank, int preset, int note);
+static awe_voice_list *awe_search_instr(int bank, int preset, int note);
+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, int key);
+static void awe_alloc_one_voice(int voice, int note, int velocity);
+static int awe_clear_voice(void);
+
+/* load / remove patches */
+static int awe_open_patch(awe_patch_info *patch, const char __user *addr, int count);
+static int awe_close_patch(awe_patch_info *patch, const char __user *addr, int count);
+static int awe_unload_patch(awe_patch_info *patch, const char __user *addr, int count);
+static int awe_load_info(awe_patch_info *patch, const char __user *addr, int count);
+static int awe_remove_info(awe_patch_info *patch, const char __user *addr, int count);
+static int awe_load_data(awe_patch_info *patch, const char __user *addr, int count);
+static int awe_replace_data(awe_patch_info *patch, const char __user *addr, int count);
+static int awe_load_map(awe_patch_info *patch, const char __user *addr, int count);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+static int awe_load_guspatch(const char __user *addr, int offs, int size, int pmgr_flag);
+#endif
+/*static int awe_probe_info(awe_patch_info *patch, const char __user *addr, int count);*/
+static int awe_probe_data(awe_patch_info *patch, const char __user *addr, int count);
+static sf_list *check_patch_opened(int type, char *name);
+static int awe_write_wave_data(const char __user *addr, int offset, awe_sample_list *sp, int channels);
+static int awe_create_sf(int type, char *name);
+static void awe_free_sf(sf_list *sf);
+static void add_sf_info(sf_list *sf, awe_voice_list *rec);
+static void add_sf_sample(sf_list *sf, awe_sample_list *smp);
+static void purge_old_list(awe_voice_list *rec, awe_voice_list *next);
+static void add_info_list(awe_voice_list *rec);
+static void awe_remove_samples(int sf_id);
+static void rebuild_preset_list(void);
+static short awe_set_sample(awe_voice_list *rec);
+static awe_sample_list *search_sample_index(sf_list *sf, int sample);
+
+static int is_identical_holder(sf_list *sf1, sf_list *sf2);
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+static int is_identical_name(unsigned char *name, sf_list *p);
+static int is_shared_sf(unsigned char *name);
+static int info_duplicated(sf_list *sf, awe_voice_list *rec);
+#endif /* allow sharing */
+
+/* lowlevel functions */
+static void awe_init_audio(void);
+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, int channels);
+static void awe_open_dram_for_check(void);
+static void awe_close_dram(void);
+/*static void awe_write_dram(unsigned short c);*/
+static int awe_detect_base(int addr);
+static int awe_detect(void);
+static void awe_check_dram(void);
+static int awe_load_chorus_fx(awe_patch_info *patch, const char __user *addr, int count);
+static void awe_set_chorus_mode(int mode);
+static void awe_update_chorus_mode(void);
+static int awe_load_reverb_fx(awe_patch_info *patch, const char __user *addr, int count);
+static void awe_set_reverb_mode(int mode);
+static void awe_update_reverb_mode(void);
+static void awe_equalizer(int bass, int treble);
+static void awe_update_equalizer(void);
+
+#ifdef CONFIG_AWE32_MIXER
+static void attach_mixer(void);
+static void unload_mixer(void);
+#endif
+
+#ifdef CONFIG_AWE32_MIDIEMU
+static void attach_midiemu(void);
+static void unload_midiemu(void);
+#endif
+
+#define limitvalue(x, a, b) if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b)
+
+/*
+ * control parameters
+ */
+
+
+#ifdef AWE_USE_NEW_VOLUME_CALC
+#define DEF_VOLUME_CALC TRUE
+#else
+#define DEF_VOLUME_CALC FALSE
+#endif /* new volume */
+
+#define DEF_ZERO_ATTEN 32 /* 12dB below */
+#define DEF_MOD_SENSE 18
+#define DEF_CHORUS_MODE 2
+#define DEF_REVERB_MODE 4
+#define DEF_BASS_LEVEL 5
+#define DEF_TREBLE_LEVEL 9
+
+static struct CtrlParmsDef {
+ int value;
+ int init_each_time;
+ void (*update)(void);
+} ctrl_parms[AWE_MD_END] = {
+ {0,0, NULL}, {0,0, NULL}, /* <-- not used */
+ {AWE_VERSION_NUMBER, FALSE, NULL},
+ {TRUE, FALSE, NULL}, /* exclusive */
+ {TRUE, FALSE, NULL}, /* realpan */
+ {AWE_DEFAULT_BANK, FALSE, NULL}, /* gusbank */
+ {FALSE, TRUE, NULL}, /* keep effect */
+ {DEF_ZERO_ATTEN, FALSE, awe_update_volume}, /* zero_atten */
+ {FALSE, FALSE, NULL}, /* chn_prior */
+ {DEF_MOD_SENSE, FALSE, NULL}, /* modwheel sense */
+ {AWE_DEFAULT_PRESET, FALSE, NULL}, /* def_preset */
+ {AWE_DEFAULT_BANK, FALSE, NULL}, /* def_bank */
+ {AWE_DEFAULT_DRUM, FALSE, NULL}, /* def_drum */
+ {FALSE, FALSE, NULL}, /* toggle_drum_bank */
+ {DEF_VOLUME_CALC, FALSE, awe_update_volume}, /* new_volume_calc */
+ {DEF_CHORUS_MODE, FALSE, awe_update_chorus_mode}, /* chorus mode */
+ {DEF_REVERB_MODE, FALSE, awe_update_reverb_mode}, /* reverb mode */
+ {DEF_BASS_LEVEL, FALSE, awe_update_equalizer}, /* bass level */
+ {DEF_TREBLE_LEVEL, FALSE, awe_update_equalizer}, /* treble level */
+ {0, FALSE, NULL}, /* debug mode */
+ {FALSE, FALSE, NULL}, /* pan exchange */
+};
+
+static int ctrls[AWE_MD_END];
+
+
+/*
+ * synth operation table
+ */
+
+static struct synth_operations awe_operations =
+{
+ .owner = THIS_MODULE,
+ .id = "EMU8K",
+ .info = &awe_info,
+ .midi_dev = 0,
+ .synth_type = SYNTH_TYPE_SAMPLE,
+ .synth_subtype = SAMPLE_TYPE_AWE32,
+ .open = awe_open,
+ .close = awe_close,
+ .ioctl = awe_ioctl,
+ .kill_note = awe_kill_note,
+ .start_note = awe_start_note,
+ .set_instr = awe_set_instr_2,
+ .reset = awe_reset,
+ .hw_control = awe_hw_control,
+ .load_patch = awe_load_patch,
+ .aftertouch = awe_aftertouch,
+ .controller = awe_controller,
+ .panning = awe_panning,
+ .volume_method = awe_volume_method,
+ .bender = awe_bender,
+ .alloc_voice = awe_alloc,
+ .setup_voice = awe_setup_voice
+};
+
+static void free_tables(void)
+{
+ if (sftail) {
+ sf_list *p, *prev;
+ for (p = sftail; p; p = prev) {
+ prev = p->prev;
+ awe_free_sf(p);
+ }
+ }
+ sfhead = sftail = NULL;
+}
+
+/*
+ * clear sample tables
+ */
+
+static void
+awe_reset_samples(void)
+{
+ /* free all bank tables */
+ memset(preset_table, 0, sizeof(preset_table));
+ free_tables();
+
+ current_sf_id = 0;
+ locked_sf_id = 0;
+ patch_opened = 0;
+}
+
+
+/*
+ * EMU register access
+ */
+
+/* select a given AWE32 pointer */
+static int awe_ports[5];
+static int port_setuped = FALSE;
+static int awe_cur_cmd = -1;
+#define awe_set_cmd(cmd) \
+if (awe_cur_cmd != cmd) { outw(cmd, awe_ports[Pointer]); awe_cur_cmd = cmd; }
+
+/* write 16bit data */
+static void
+awe_poke(unsigned short cmd, unsigned short port, unsigned short data)
+{
+ awe_set_cmd(cmd);
+ outw(data, awe_ports[port]);
+}
+
+/* write 32bit data */
+static void
+awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data)
+{
+ unsigned short addr = awe_ports[port];
+ awe_set_cmd(cmd);
+ outw(data, addr); /* write lower 16 bits */
+ outw(data >> 16, addr + 2); /* write higher 16 bits */
+}
+
+/* read 16bit data */
+static unsigned short
+awe_peek(unsigned short cmd, unsigned short port)
+{
+ unsigned short k;
+ awe_set_cmd(cmd);
+ k = inw(awe_ports[port]);
+ return k;
+}
+
+/* read 32bit data */
+static unsigned int
+awe_peek_dw(unsigned short cmd, unsigned short port)
+{
+ unsigned int k1, k2;
+ unsigned short addr = awe_ports[port];
+ awe_set_cmd(cmd);
+ k1 = inw(addr);
+ k2 = inw(addr + 2);
+ k1 |= k2 << 16;
+ return k1;
+}
+
+/* wait delay number of AWE32 44100Hz clocks */
+#ifdef WAIT_BY_LOOP /* wait by loop -- that's not good.. */
+static void
+awe_wait(unsigned short delay)
+{
+ unsigned short clock, target;
+ unsigned short port = awe_ports[AWE_WC_Port];
+ int counter;
+
+ /* sample counter */
+ awe_set_cmd(AWE_WC_Cmd);
+ clock = (unsigned short)inw(port);
+ target = clock + delay;
+ counter = 0;
+ if (target < clock) {
+ for (; (unsigned short)inw(port) > target; counter++)
+ if (counter > 65536)
+ break;
+ }
+ for (; (unsigned short)inw(port) < target; counter++)
+ if (counter > 65536)
+ break;
+}
+#else
+
+static void awe_wait(unsigned short delay)
+{
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout((HZ*(unsigned long)delay + 44099)/44100);
+}
+/*
+static void awe_wait(unsigned short delay)
+{
+ udelay(((unsigned long)delay * 1000000L + 44099) / 44100);
+}
+*/
+#endif /* wait by loop */
+
+/* write a word data */
+#define awe_write_dram(c) awe_poke(AWE_SMLD, c)
+
+/*
+ * AWE32 voice parameters
+ */
+
+/* initialize voice_info record */
+static void
+awe_init_voice_info(awe_voice_info *vp)
+{
+ vp->sample = 0;
+ vp->rate_offset = 0;
+
+ vp->start = 0;
+ vp->end = 0;
+ vp->loopstart = 0;
+ vp->loopend = 0;
+ vp->mode = 0;
+ vp->root = 60;
+ vp->tune = 0;
+ vp->low = 0;
+ vp->high = 127;
+ vp->vellow = 0;
+ vp->velhigh = 127;
+
+ vp->fixkey = -1;
+ vp->fixvel = -1;
+ vp->fixpan = -1;
+ vp->pan = -1;
+
+ vp->exclusiveClass = 0;
+ vp->amplitude = 127;
+ vp->attenuation = 0;
+ vp->scaleTuning = 100;
+
+ awe_init_voice_parm(&vp->parm);
+}
+
+/* initialize voice_parm record:
+ * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0.
+ * Vibrato and Tremolo effects are zero.
+ * Cutoff is maximum.
+ * Chorus and Reverb effects are zero.
+ */
+static void
+awe_init_voice_parm(awe_voice_parm *pp)
+{
+ pp->moddelay = 0x8000;
+ pp->modatkhld = 0x7f7f;
+ pp->moddcysus = 0x7f7f;
+ pp->modrelease = 0x807f;
+ pp->modkeyhold = 0;
+ pp->modkeydecay = 0;
+
+ pp->voldelay = 0x8000;
+ pp->volatkhld = 0x7f7f;
+ pp->voldcysus = 0x7f7f;
+ pp->volrelease = 0x807f;
+ pp->volkeyhold = 0;
+ pp->volkeydecay = 0;
+
+ pp->lfo1delay = 0x8000;
+ pp->lfo2delay = 0x8000;
+ pp->pefe = 0;
+
+ pp->fmmod = 0;
+ pp->tremfrq = 0;
+ pp->fm2frq2 = 0;
+
+ pp->cutoff = 0xff;
+ pp->filterQ = 0;
+
+ pp->chorus = 0;
+ pp->reverb = 0;
+}
+
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+
+/* convert frequency mHz to abstract cents (= midi key * 100) */
+static int
+freq_to_note(int mHz)
+{
+ /* abscents = log(mHz/8176) / log(2) * 1200 */
+ unsigned int max_val = (unsigned int)0xffffffff / 10000;
+ int i, times;
+ unsigned int base;
+ unsigned int freq;
+ int note, tune;
+
+ if (mHz == 0)
+ return 0;
+ if (mHz < 0)
+ return 12799; /* maximum */
+
+ freq = mHz;
+ note = 0;
+ for (base = 8176 * 2; freq >= base; base *= 2) {
+ note += 12;
+ if (note >= 128) /* over maximum */
+ return 12799;
+ }
+ base /= 2;
+
+ /* to avoid overflow... */
+ times = 10000;
+ while (freq > max_val) {
+ max_val *= 10;
+ times /= 10;
+ base /= 10;
+ }
+
+ freq = freq * times / base;
+ for (i = 0; i < 12; i++) {
+ if (freq < semitone_tuning[i+1])
+ break;
+ note++;
+ }
+
+ tune = 0;
+ freq = freq * 10000 / semitone_tuning[i];
+ for (i = 0; i < 100; i++) {
+ if (freq < cent_tuning[i+1])
+ break;
+ tune++;
+ }
+
+ return note * 100 + tune;
+}
+
+
+/* convert Hz to AWE32 rate offset:
+ * sample pitch offset for the specified sample rate
+ * rate=44100 is no offset, each 4096 is 1 octave (twice).
+ * eg, when rate is 22050, this offset becomes -4096.
+ */
+static int
+calc_rate_offset(int Hz)
+{
+ /* offset = log(Hz / 44100) / log(2) * 4096 */
+ int freq, base, i;
+
+ /* maybe smaller than max (44100Hz) */
+ if (Hz <= 0 || Hz >= 44100) return 0;
+
+ base = 0;
+ for (freq = Hz * 2; freq < 44100; freq *= 2)
+ base++;
+ base *= 1200;
+
+ freq = 44100 * 10000 / (freq/2);
+ for (i = 0; i < 12; i++) {
+ if (freq < semitone_tuning[i+1])
+ break;
+ base += 100;
+ }
+ freq = freq * 10000 / semitone_tuning[i];
+ for (i = 0; i < 100; i++) {
+ if (freq < cent_tuning[i+1])
+ break;
+ base++;
+ }
+ return -base * 4096 / 1200;
+}
+
+
+/*
+ * convert envelope time parameter to AWE32 raw parameter
+ */
+
+/* attack & decay/release time table (msec) */
+static short attack_time_tbl[128] = {
+32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816,
+707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377,
+361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188,
+180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94,
+90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47,
+45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23,
+22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12,
+11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0,
+};
+
+static short decay_time_tbl[128] = {
+32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082,
+2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507,
+1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722,
+691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361,
+345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180,
+172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90,
+86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45,
+43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22,
+};
+
+#define calc_parm_delay(msec) (0x8000 - (msec) * 1000 / 725);
+
+/* delay time = 0x8000 - msec/92 */
+static int
+calc_parm_hold(int msec)
+{
+ int val = (0x7f * 92 - msec) / 92;
+ if (val < 1) val = 1;
+ if (val > 127) val = 127;
+ 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 = 1, right = 127, mid;
+ while (left < right) {
+ mid = (left + right) / 2;
+ if (msec < (int)table[mid])
+ left = mid + 1;
+ else
+ right = mid;
+ }
+ return left;
+}
+#endif /* AWE_HAS_GUS_COMPATIBILITY */
+
+
+/*
+ * effects table
+ */
+
+/* set an effect value */
+#define FX_FLAG_OFF 0
+#define FX_FLAG_SET 1
+#define FX_FLAG_ADD 2
+
+#define FX_SET(rec,type,value) \
+ ((rec)->flags[type] = FX_FLAG_SET, (rec)->val[type] = (value))
+#define FX_ADD(rec,type,value) \
+ ((rec)->flags[type] = FX_FLAG_ADD, (rec)->val[type] = (value))
+#define FX_UNSET(rec,type) \
+ ((rec)->flags[type] = FX_FLAG_OFF, (rec)->val[type] = 0)
+
+/* check the effect value is set */
+#define FX_ON(rec,type) ((rec)->flags[type])
+
+#define PARM_BYTE 0
+#define PARM_WORD 1
+#define PARM_SIGN 2
+
+static struct PARM_DEFS {
+ int type; /* byte or word */
+ int low, high; /* value range */
+ fx_affect_func realtime; /* realtime paramater change */
+} parm_defs[] = {
+ {PARM_WORD, 0, 0x8000, NULL}, /* env1 delay */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env1 attack */
+ {PARM_BYTE, 0, 0x7e, NULL}, /* env1 hold */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env1 decay */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env1 release */
+ {PARM_BYTE, 0, 0x7f, NULL}, /* env1 sustain */
+ {PARM_BYTE, 0, 0xff, NULL}, /* env1 pitch */
+ {PARM_BYTE, 0, 0xff, NULL}, /* env1 cutoff */
+
+ {PARM_WORD, 0, 0x8000, NULL}, /* env2 delay */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env2 attack */
+ {PARM_BYTE, 0, 0x7e, NULL}, /* env2 hold */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env2 decay */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env2 release */
+ {PARM_BYTE, 0, 0x7f, NULL}, /* env2 sustain */
+
+ {PARM_WORD, 0, 0x8000, NULL}, /* lfo1 delay */
+ {PARM_BYTE, 0, 0xff, awe_fx_tremfrq}, /* lfo1 freq */
+ {PARM_SIGN, -128, 127, awe_fx_tremfrq}, /* lfo1 volume */
+ {PARM_SIGN, -128, 127, awe_fx_fmmod}, /* lfo1 pitch */
+ {PARM_BYTE, 0, 0xff, awe_fx_fmmod}, /* lfo1 cutoff */
+
+ {PARM_WORD, 0, 0x8000, NULL}, /* lfo2 delay */
+ {PARM_BYTE, 0, 0xff, awe_fx_fm2frq2}, /* lfo2 freq */
+ {PARM_SIGN, -128, 127, awe_fx_fm2frq2}, /* lfo2 pitch */
+
+ {PARM_WORD, 0, 0xffff, awe_set_voice_pitch}, /* initial pitch */
+ {PARM_BYTE, 0, 0xff, NULL}, /* chorus */
+ {PARM_BYTE, 0, 0xff, NULL}, /* reverb */
+ {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial cutoff */
+ {PARM_BYTE, 0, 15, awe_fx_filterQ}, /* initial resonance */
+
+ {PARM_WORD, 0, 0xffff, NULL}, /* sample start */
+ {PARM_WORD, 0, 0xffff, NULL}, /* loop start */
+ {PARM_WORD, 0, 0xffff, NULL}, /* loop end */
+ {PARM_WORD, 0, 0xffff, NULL}, /* coarse sample start */
+ {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop start */
+ {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop end */
+ {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial attenuation */
+};
+
+
+static unsigned char
+FX_BYTE(FX_Rec *rec, FX_Rec *lay, int type, unsigned char value)
+{
+ int effect = 0;
+ int on = 0;
+ if (lay && (on = FX_ON(lay, type)) != 0)
+ effect = lay->val[type];
+ if (!on && (on = FX_ON(rec, type)) != 0)
+ effect = rec->val[type];
+ if (on == FX_FLAG_ADD) {
+ if (parm_defs[type].type == PARM_SIGN) {
+ if (value > 0x7f)
+ effect += (int)value - 0x100;
+ else
+ effect += (int)value;
+ } else {
+ effect += (int)value;
+ }
+ }
+ if (on) {
+ if (effect < parm_defs[type].low)
+ effect = parm_defs[type].low;
+ else if (effect > parm_defs[type].high)
+ effect = parm_defs[type].high;
+ return (unsigned char)effect;
+ }
+ return value;
+}
+
+/* get word effect value */
+static unsigned short
+FX_WORD(FX_Rec *rec, FX_Rec *lay, int type, unsigned short value)
+{
+ int effect = 0;
+ int on = 0;
+ if (lay && (on = FX_ON(lay, type)) != 0)
+ effect = lay->val[type];
+ if (!on && (on = FX_ON(rec, type)) != 0)
+ effect = rec->val[type];
+ if (on == FX_FLAG_ADD)
+ effect += (int)value;
+ if (on) {
+ if (effect < parm_defs[type].low)
+ effect = parm_defs[type].low;
+ else if (effect > parm_defs[type].high)
+ effect = parm_defs[type].high;
+ return (unsigned short)effect;
+ }
+ return value;
+}
+
+/* get word (upper=type1/lower=type2) effect value */
+static unsigned short
+FX_COMB(FX_Rec *rec, FX_Rec *lay, int type1, int type2, unsigned short value)
+{
+ unsigned short tmp;
+ tmp = FX_BYTE(rec, lay, type1, (unsigned char)(value >> 8));
+ tmp <<= 8;
+ tmp |= FX_BYTE(rec, lay, type2, (unsigned char)(value & 0xff));
+ return tmp;
+}
+
+/* address offset */
+static int
+FX_OFFSET(FX_Rec *rec, FX_Rec *lay, int lo, int hi, int mode)
+{
+ int addr = 0;
+ if (lay && FX_ON(lay, hi))
+ addr = (short)lay->val[hi];
+ else if (FX_ON(rec, hi))
+ addr = (short)rec->val[hi];
+ addr = addr << 15;
+ if (lay && FX_ON(lay, lo))
+ addr += (short)lay->val[lo];
+ else if (FX_ON(rec, lo))
+ addr += (short)rec->val[lo];
+ if (!(mode & AWE_SAMPLE_8BITS))
+ addr /= 2;
+ return addr;
+}
+
+
+/*
+ * turn on/off sample
+ */
+
+/* table for volume target calculation */
+static unsigned short voltarget[16] = {
+ 0xEAC0, 0XE0C8, 0XD740, 0XCE20, 0XC560, 0XBD08, 0XB500, 0XAD58,
+ 0XA5F8, 0X9EF0, 0X9830, 0X91C0, 0X8B90, 0X85A8, 0X8000, 0X7A90
+};
+
+static void
+awe_note_on(int voice)
+{
+ unsigned int temp;
+ int addr;
+ int vtarget, ftarget, ptarget, pitch;
+ awe_voice_info *vp;
+ awe_voice_parm_block *parm;
+ FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+ /* A voice sample must assigned before calling */
+ if ((vp = voices[voice].sample) == NULL || vp->index == 0)
+ return;
+
+ parm = (awe_voice_parm_block*)&vp->parm;
+
+ /* channel to be silent and idle */
+ awe_poke(AWE_DCYSUSV(voice), 0x0080);
+ awe_poke(AWE_VTFT(voice), 0x0000FFFF);
+ awe_poke(AWE_CVCF(voice), 0x0000FFFF);
+ awe_poke(AWE_PTRX(voice), 0);
+ awe_poke(AWE_CPF(voice), 0);
+
+ /* set pitch offset */
+ awe_set_pitch(voice, TRUE);
+
+ /* modulation & volume envelope */
+ if (parm->modatk >= 0x80 && parm->moddelay >= 0x8000) {
+ awe_poke(AWE_ENVVAL(voice), 0xBFFF);
+ pitch = (parm->env1pit<<4) + voices[voice].apitch;
+ if (pitch > 0xffff) pitch = 0xffff;
+ /* calculate filter target */
+ ftarget = parm->cutoff + parm->env1fc;
+ limitvalue(ftarget, 0, 255);
+ ftarget <<= 8;
+ } else {
+ awe_poke(AWE_ENVVAL(voice),
+ FX_WORD(fx, fx_lay, AWE_FX_ENV1_DELAY, parm->moddelay));
+ ftarget = parm->cutoff;
+ ftarget <<= 8;
+ pitch = voices[voice].apitch;
+ }
+
+ /* calcualte pitch target */
+ if (pitch != 0xffff) {
+ ptarget = 1 << (pitch >> 12);
+ if (pitch & 0x800) ptarget += (ptarget*0x102e)/0x2710;
+ if (pitch & 0x400) ptarget += (ptarget*0x764)/0x2710;
+ if (pitch & 0x200) ptarget += (ptarget*0x389)/0x2710;
+ ptarget += (ptarget>>1);
+ if (ptarget > 0xffff) ptarget = 0xffff;
+
+ } else ptarget = 0xffff;
+ if (parm->modatk >= 0x80)
+ awe_poke(AWE_ATKHLD(voice),
+ FX_BYTE(fx, fx_lay, AWE_FX_ENV1_HOLD, parm->modhld) << 8 | 0x7f);
+ else
+ awe_poke(AWE_ATKHLD(voice),
+ FX_COMB(fx, fx_lay, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK,
+ vp->parm.modatkhld));
+ awe_poke(AWE_DCYSUS(voice),
+ FX_COMB(fx, fx_lay, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY,
+ vp->parm.moddcysus));
+
+ if (parm->volatk >= 0x80 && parm->voldelay >= 0x8000) {
+ awe_poke(AWE_ENVVOL(voice), 0xBFFF);
+ vtarget = voltarget[voices[voice].avol%0x10]>>(voices[voice].avol>>4);
+ } else {
+ awe_poke(AWE_ENVVOL(voice),
+ FX_WORD(fx, fx_lay, AWE_FX_ENV2_DELAY, vp->parm.voldelay));
+ vtarget = 0;
+ }
+ if (parm->volatk >= 0x80)
+ awe_poke(AWE_ATKHLDV(voice),
+ FX_BYTE(fx, fx_lay, AWE_FX_ENV2_HOLD, parm->volhld) << 8 | 0x7f);
+ else
+ awe_poke(AWE_ATKHLDV(voice),
+ FX_COMB(fx, fx_lay, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK,
+ vp->parm.volatkhld));
+ /* decay/sustain parameter for volume envelope must be set at last */
+
+ /* cutoff and volume */
+ awe_set_volume(voice, TRUE);
+
+ /* modulation envelope heights */
+ awe_poke(AWE_PEFE(voice),
+ FX_COMB(fx, fx_lay, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF,
+ vp->parm.pefe)