/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * AWE32/EMU8000 driver for the MIDI player. * * By George Foot. * * Modified by J. Flynn to remove floating point log calculations * during interrupt - replaced by precomputed lookup table. * * See readme.txt for copyright information. */ #include "allegro.h" #include "allegro/internal/aintern.h" #include "allegro/platform/aintdos.h" #include "emu8k.h" #ifndef ALLEGRO_DOS #error something is wrong with the makefile #endif /* variables from awedata.c, containing the envelope data from synthgm.sf2 */ extern short int _awe_sf_defaults[]; extern int _awe_sf_num_presets; extern short int _awe_sf_presets[]; extern short int _awe_sf_splits[]; extern short int _awe_sf_gens[]; extern int _awe_sf_sample_data[]; /* external interface to the AWE32 driver */ static int awe32_detect(int input); static int awe32_init(int input, int voices); static void awe32_exit(int input); static void awe32_key_on(int inst, int note, int bend, int vol, int pan); static void awe32_key_off(int voice); static void _awe32_do_note(int inst, int note, int bend, int vol, int pan); static void awe32_set_volume(int voice, int vol); static void awe32_set_pitch(int voice, int note, int bend); static void translate_soundfont_into_something_useful(void); static void destroy_useful_version_of_soundfont(void); static struct midi_preset_t { /* struct to hold envelope data for each preset */ int num_splits; /* number of splits in this preset */ struct envparms_t **split; /* array of num_splits pointers to envelope data */ } *midi_preset; /* global variable to hold the data */ static struct envparms_t **voice_envelope; /* array of pointers pointing at the envelope playing on each voice */ static int *exclusive_class_info; /* exclusive class information */ static const unsigned char attentbl[] = /* logarithm table */ { 255, 255, 221, 199, 184, 172, 162, 154, 147, 141, 135, 130, 125, 121, 117, 113, 110, 107, 104, 101, 98, 95, 93, 91, 88, 86, 84, 82, 80, 78, 76, 75, 73, 71, 70, 68, 67, 65, 64, 62, 61, 60, 59, 57, 56, 55, 54, 53, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 39, 38, 37, 36, 35, 34, 34, 33, 32, 31, 31, 30, 29, 28, 28, 27, 26, 25, 25, 24, 23, 23, 22, 22, 21, 20, 20, 19, 18, 18, 17, 17, 16, 16, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 3, 2, 2, 1, 1, 0, 0, 0, }; static char awe32_desc[256] = EMPTY_STRING; MIDI_DRIVER midi_awe32 = { MIDI_AWE32, /* id */ empty_string, /* name */ empty_string, /* desc */ "AWE32/EMU8000", /* ASCII name */ 32, 0, 32, 32, -1, -1, /* voices, basevoice, max_voices, def_voices, xmin, xmax */ awe32_detect, /* detect */ awe32_init, /* init */ awe32_exit, /* exit */ NULL, /* mixer_volume */ NULL, /* raw_midi */ _dummy_load_patches, /* load_patches */ _dummy_adjust_patches, /* adjust_patches */ awe32_key_on, /* key_on */ awe32_key_off, /* key_off */ awe32_set_volume, /* set_volume */ awe32_set_pitch, /* set_pitch */ _dummy_noop2, /* set_pan */ _dummy_noop2 /* set_vibrato */ }; /* awe32_key_on: * Triggers the specified voice. The instrument is specified as a GM * patch number, pitch as a midi note number, and volume from 0-127. * The bend parameter is _not_ expressed as a midi pitch bend value. * It ranges from 0 (no pitch change) to 0xFFF (almost a semitone sharp). * Drum sounds are indicated by passing an instrument number greater than * 128, in which case the sound is GM percussion key #(inst-128). */ static void awe32_key_on(int inst, int note, int bend, int vol, int pan) { if (inst > 127) { /* drum sound? */ _awe32_do_note(128, inst - 128, bend, vol, pan); } else { /* regular instrument */ _awe32_do_note(inst, note, bend, vol, pan); } } END_OF_STATIC_FUNCTION(awe32_key_on); /* _awe32_do_note: * Actually plays the note as described above; the above function just remaps * the drums. */ static void _awe32_do_note(int inst, int note, int bend, int vol, int pan) { int voice; int i; envparms_t *env; int key, vel; int atten; int pan_pos; /* EMU8000 pan is back-to-front and twice the scale */ pan = 0x100 - 2 * pan; if (pan > 0xff) pan = 0xff; if (pan < 0x00) pan = 0x00; for (i=0; i= env->minkey) && (note <= env->maxkey) && (vol >= env->minvel) && (vol <= env->maxvel)) { /* get a voice (any voice) to play it on */ voice = _midi_allocate_voice(-1, -1); /* did we get one? */ if (voice >= 0) { /* set the current envelope for this voice */ voice_envelope[voice] = env; /* set pitch and velocity */ key = note * 0x1000 + bend; vel = vol; /* override key and velocity if envelope says so */ if ((env->key >= 0) && (env->key <= 127)) key = env->key * 0x1000; if ((env->vel >= 0) && (env->vel <= 127)) vel = env->vel; /* check key and velocity numbers are within range */ if (key > 0x7ffff) key = 0x7ffff; if (key < 0) key = 0; if (vel > 127) vel = 127; if (vel < 0) vel = 0; /* add one-off information to the envelope (these have no side-effects on the other voices using this envelope) */ env->ip = env->ipbase + (env->ipscale * key) / 1200; /* remap MIDI velocity to attenuation */ if (vel) atten = env->atten + attentbl[vel]; else atten = 0xff; if (atten < 0x00) atten = 0x00; if (atten > 0xff) atten = 0xff; /* update it in the envelope */ env->ifatn = env->filter + atten; /* modify pan with envelope's built-in pan */ if (pan < 0x80) { pan_pos = (pan * env->pan) / 0x80; } else { pan_pos = env->pan + (pan - 0x80) * (256 - env->pan) / 0x80; } if (pan_pos < 0x00) pan_pos = 0x00; if (pan_pos > 0xff) pan_pos = 0xff; /* update pan in the envelope */ env->psst = (pan_pos << 24) + env->loopst; /* test exclusive class */ exclusive_class_info[voice] = (inst << 8) + env->exc; if (env->exc) { int chan; for (chan=0; chan<32; chan++) if ((chan != voice) && (exclusive_class_info[chan] == exclusive_class_info[voice])) emu8k_terminatesound(chan); } /* start the note playing */ emu8k_startsound(voice, env); } } } } END_OF_STATIC_FUNCTION(_awe32_do_note); /* awe32_set_*: * Modulation routines */ static void awe32_set_volume(int voice, int vol) { int atten; struct envparms_t *env; /* get envelope in use on this voice */ env = voice_envelope[voice]; /* allow envelope to override new volume */ if ((env->vel >= 0) && (env->vel <= 127)) vol = env->vel; /* check velocity number is within range */ if (vol > 127) vol = 127; if (vol < 0) vol = 0; /* remap MIDI velocity to attenuation */ if (vol) atten = env->atten + attentbl[vol]; else atten = 0xff; if (atten < 0x00) atten = 0x00; if (atten > 0xff) atten = 0xff; emu8k_modulate_atten(voice, atten); } END_OF_STATIC_FUNCTION(awe32_set_volume); static void awe32_set_pitch(int voice, int note, int bend) { struct envparms_t *env; int key, ip; /* get envelope in use on this voice */ env = voice_envelope[voice]; key = note * 0x1000 + bend; /* override key if envelope says so */ if ((env->key >= 0) && (env->key <= 127)) key = env->key * 0x1000; /* check key number is within range */ if (key > 0x7ffff) key = 0x7ffff; if (key < 0) key = 0; ip = env->ipbase + (env->ipscale * key) / 1200; emu8k_modulate_ip(voice, ip); } END_OF_STATIC_FUNCTION(awe32_set_pitch); /* awe32_key_off: * Hey, guess what this does :-) */ static void awe32_key_off(int voice) { emu8k_releasesound(voice, voice_envelope[voice]); } END_OF_STATIC_FUNCTION(awe32_key_off); /* awe32_detect: * AWE32/EMU8000 detection routine. */ static int awe32_detect(int input) { if (input) return FALSE; if (emu8k_detect()) { uszprintf(awe32_desc, sizeof(awe32_desc), get_config_text("SB AWE32/compatible on port 0x%04x"), _emu8k_baseport); midi_awe32.desc = awe32_desc; return TRUE; } ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("AWE32 not detected")); return FALSE; } /* awe32_lockmem: * Locks required memory blocks */ static void awe32_lockmem(void) { /* functions */ LOCK_FUNCTION(awe32_key_on); LOCK_FUNCTION(awe32_key_off); LOCK_FUNCTION(_awe32_do_note); LOCK_FUNCTION(awe32_set_volume); LOCK_FUNCTION(awe32_set_pitch); /* data */ LOCK_VARIABLE(midi_preset); LOCK_VARIABLE(voice_envelope); LOCK_VARIABLE(exclusive_class_info); LOCK_VARIABLE(midi_awe32); LOCK_VARIABLE(attentbl); /* stuff in emu8k.c */ emu8k_lock(); } /* awe32_init: * Setup the AWE32/EMU8000 driver. */ static int awe32_init(int input, int voices) { int chan; emu8k_init(); translate_soundfont_into_something_useful(); voice_envelope = (struct envparms_t **)_lock_malloc(32 * sizeof(struct envparms_t *)); exclusive_class_info = (int *)_lock_malloc(32 * sizeof(int)); awe32_lockmem(); for (chan=0; chan<32; chan++) { voice_envelope[chan] = NULL; exclusive_class_info[chan] = 0; } return 0; } /* awe32_exit: * Cleanup when we are finished. */ static void awe32_exit(int input) { int i; for (i=0; i<_emu8k_numchannels; i++) emu8k_terminatesound(i); destroy_useful_version_of_soundfont(); free(voice_envelope); free(exclusive_class_info); } /* translate_soundfont_into_something_useful: * Like it says, translate the soundfont data into something we can use * when playing notes. */ static void translate_soundfont_into_something_useful(void) { int p, s, gen, weirdo; struct midi_preset_t *thing_to_write = NULL; generators_t temp_gens; int global_split = 0, global_weirdo = 0, num_weirdos; midi_preset = (struct midi_preset_t *)_lock_malloc(129 * sizeof(struct midi_preset_t)); for (p=0; p<_awe_sf_num_presets; p++) { if (_awe_sf_presets[p * 3 + 1] == 0) { thing_to_write = &midi_preset[_awe_sf_presets[p * 3 + 0]]; } else if (_awe_sf_presets[p * 3 + 1] == 128) { thing_to_write = &midi_preset[128]; } else { thing_to_write = NULL; } if (thing_to_write) { thing_to_write->num_splits = _awe_sf_presets[p * 3 + 2]; thing_to_write->split = (struct envparms_t **)_lock_malloc(thing_to_write->num_splits * sizeof(struct envparms_t *)); for (s=0; snum_splits; s++) { for (gen=0; gensplit[s] = emu8k_createenvelope(temp_gens); } } else { ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("AWE32 driver: had trouble with the embedded data")); } } } /* destroy_useful_version_of_soundfont: * Destroys the data created by the above function */ static void destroy_useful_version_of_soundfont(void) { int p, s; for (p=0; p<129; p++) { if (midi_preset[p].num_splits > 0) { for (s=0; s