diff options
Diffstat (limited to 'sound/oss/awe_wave.c')
-rw-r--r-- | sound/oss/awe_wave.c | 6147 |
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 00000000000..d2b9beda8ac --- /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)); + |