X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=dosdemo;a=blobdiff_plain;f=libs%2Fmikmod%2Fdrivers%2Fdrv_ultra.c;fp=libs%2Fmikmod%2Fdrivers%2Fdrv_ultra.c;h=dbd0b7ce21ead6e641709f069634d79d920a7b3a;hp=0000000000000000000000000000000000000000;hb=cf94899f4f4d8535074db6421245b973d2fcde8c;hpb=8024ae981f39d370af5cceb3cb97f62820b0a120 diff --git a/libs/mikmod/drivers/drv_ultra.c b/libs/mikmod/drivers/drv_ultra.c new file mode 100644 index 0000000..dbd0b7c --- /dev/null +++ b/libs/mikmod/drivers/drv_ultra.c @@ -0,0 +1,1020 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library 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 Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Driver for Gravis Ultrasound cards using libGUS. + A subset of libGUS is provided for DOS/DJGPP and OS/2 + +==============================================================================*/ + +/* + + Written by Andy Lo A Foe + + Updated to work with later versions of both the ultrasound driver and + libmikmod by C. Ray C. + + Major fixes by Andrew Zabolotny + + Ported to OS/2 and DOS. + + Eight-bit samples are not converted to 16-bit anymore. + + Samples are no longer kept in normal memory. + + Removed sample 'unclick' logic... libGUS does unclick internally. + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mikmod_internals.h" + +#ifdef DRV_ULTRA + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_MEMORY_H +#include +#endif + +#ifdef MIKMOD_DYNAMIC +#include +#endif +#include +#include +#include + +#include + +#if !defined(GUS_INSTR_SIMPLE) || !defined(GUS_WAVE_BIDIR) +#error libgus version is too old +#endif +/* just in case */ +#ifndef LIBGUS_VERSION_MAJOR +#define LIBGUS_VERSION_MAJOR 0x0003 +#endif + +/* DOS/DJGPP and OS/2 libGUS'es have gus_get_voice_status() */ +#if defined(__EMX__) || defined(__DJGPP__) +#define HAVE_VOICE_STATUS +#else +#include +#endif + + +#ifdef MIKMOD_DYNAMIC +/* runtime link with libgus */ +static int (*_libgus_cards) (void); + +#if LIBGUS_VERSION_MAJOR < 0x0004 +static int (*_libgus_close) (int); +static int (*_libgus_do_flush) (void); +static void (*_libgus_do_tempo) (unsigned int); +static void (*_libgus_do_voice_frequency) (unsigned char, unsigned int); +static void (*_libgus_do_voice_pan) (unsigned char, unsigned short); +static void (*_libgus_do_voice_start) (unsigned char, unsigned int, + unsigned int, unsigned short, + unsigned short); +static void (*_libgus_do_voice_start_position) (unsigned char, unsigned int, + unsigned int, unsigned short, + unsigned short, unsigned int); +static void (*_libgus_do_voice_stop) (unsigned char, unsigned char); +static void (*_libgus_do_voice_volume) (unsigned char, unsigned short); +static void (*_libgus_do_wait) (unsigned int); +static int (*_libgus_get_handle) (void); +static int (*_libgus_info) (gus_info_t *, int); +static int (*_libgus_memory_alloc) (gus_instrument_t *); +static int (*_libgus_memory_free) (gus_instrument_t *); +static int (*_libgus_memory_free_size) (void); +static int (*_libgus_memory_pack) (void); +static int (*_libgus_open) (int, size_t, int); +static int (*_libgus_queue_flush) (void); +static int (*_libgus_queue_read_set_size) (int); +static int (*_libgus_queue_write_set_size) (int); +static int (*_libgus_reset) (int, unsigned int); +static int (*_libgus_select) (int); +static int (*_libgus_timer_start) (void); +static int (*_libgus_timer_stop) (void); +static int (*_libgus_timer_tempo) (int); +#else +static int (*_libgus_close) (void*); +static int (*_libgus_do_flush) (void*); +static void (*_libgus_do_tempo) (void*, unsigned int); +static void (*_libgus_do_voice_frequency) (void*, unsigned char, unsigned int); +static void (*_libgus_do_voice_pan) (void*, unsigned char,unsigned short); +static void (*_libgus_do_voice_start) (void*, unsigned char, unsigned int, + unsigned int, unsigned short, + unsigned short); +static void (*_libgus_do_voice_start_position) (void*, unsigned char, unsigned int, + unsigned int,unsigned short, + unsigned short, unsigned int); +static void (*_libgus_do_voice_stop) (void*, unsigned char, unsigned char); +static void (*_libgus_do_voice_volume) (void*, unsigned char, unsigned short); +static void (*_libgus_do_wait) (void*, unsigned int); +static int (*_libgus_get_file_descriptor) (void*); +static int (*_libgus_info) (void*, gus_info_t*, int); +static int (*_libgus_memory_alloc) (void*, gus_instrument_t*); +static int (*_libgus_memory_free) (void*, gus_instrument_t*); +static int (*_libgus_memory_free_size) (void*); +static int (*_libgus_memory_pack) (void*); +static int (*_libgus_open) (void**, int, int, size_t, int); +static int (*_libgus_queue_flush) (void*); +static int (*_libgus_queue_read_set_size) (void*, int); +static int (*_libgus_queue_write_set_size) (void*, int); +static int (*_libgus_reset) (void*, int, unsigned int); +static int (*_libgus_timer_start)(void*); +static int (*_libgus_timer_stop) (void*); +static int (*_libgus_timer_tempo) (void*, int); +#endif +#ifndef HAVE_RTLD_GLOBAL +#define RTLD_GLOBAL (0) +#endif +static void *libgus = NULL; + +#else +/* compile-time link with libgus */ +#define _libgus_cards gus_cards +#define _libgus_close gus_close +#define _libgus_do_flush gus_do_flush +#define _libgus_do_tempo gus_do_tempo +#define _libgus_do_voice_frequency gus_do_voice_frequency +#define _libgus_do_voice_pan gus_do_voice_pan +#define _libgus_do_voice_start gus_do_voice_start +#define _libgus_do_voice_start_position gus_do_voice_start_position +#define _libgus_do_voice_stop gus_do_voice_stop +#define _libgus_do_voice_volume gus_do_voice_volume +#define _libgus_do_wait gus_do_wait +#if LIBGUS_VERSION_MAJOR < 0x0004 +#define _libgus_get_handle gus_get_handle +#else +#define _libgus_get_file_descriptor gus_get_file_descriptor +#endif +#define _libgus_info gus_info +#define _libgus_memory_alloc gus_memory_alloc +#define _libgus_memory_free gus_memory_free +#define _libgus_memory_free_size gus_memory_free_size +#define _libgus_memory_pack gus_memory_pack +#define _libgus_open gus_open +#define _libgus_queue_flush gus_queue_flush +#define _libgus_queue_read_set_size gus_queue_read_set_size +#define _libgus_queue_write_set_size gus_queue_write_set_size +#define _libgus_reset gus_reset +#if LIBGUS_VERSION_MAJOR < 0x0004 +#define _libgus_select gus_select +#endif +#define _libgus_timer_start gus_timer_start +#define _libgus_timer_stop gus_timer_stop +#define _libgus_timer_tempo gus_timer_tempo +#endif + +#define libgus_cards _libgus_cards /* same between v3 and v4 */ +#define libgus_open _libgus_open /* different between v3 and v4: must use #ifdef */ +#define libgus_close _libgus_close /* different between v3 and v4: must use #ifdef */ +/* the following can be handled easily by macros: v4 only adds them the handle as the first param */ +#if LIBGUS_VERSION_MAJOR < 0x0004 +#define libgus_get_handle _libgus_get_handle /* only in v3 */ +#define libgus_do_flush _libgus_do_flush +#define libgus_do_tempo _libgus_do_tempo +#define libgus_do_voice_frequency _libgus_do_voice_frequency +#define libgus_do_voice_pan _libgus_do_voice_pan +#define libgus_do_voice_start _libgus_do_voice_start +#define libgus_do_voice_start_position _libgus_do_voice_start_position +#define libgus_do_voice_stop _libgus_do_voice_stop +#define libgus_do_voice_volume _libgus_do_voice_volume +#define libgus_do_wait _libgus_do_wait +#define libgus_info _libgus_info +#define libgus_memory_alloc _libgus_memory_alloc +#define libgus_memory_free _libgus_memory_free +#define libgus_memory_free_size _libgus_memory_free_size +#define libgus_memory_pack _libgus_memory_pack +#define libgus_queue_flush _libgus_queue_flush +#define libgus_queue_read_set_size _libgus_queue_read_set_size +#define libgus_queue_write_set_size _libgus_queue_write_set_size +#define libgus_reset _libgus_reset +#define libgus_select _libgus_select +#define libgus_timer_start _libgus_timer_start +#define libgus_timer_stop _libgus_timer_stop +#define libgus_timer_tempo _libgus_timer_tempo +#else +#define libgus_get_file_descriptor _libgus_get_file_descriptor /* only in v4 */ +#define libgus_do_flush() _libgus_do_flush(ultra_h) +#define libgus_do_tempo(t) _libgus_do_tempo(ultra_h,t) +#define libgus_do_voice_frequency(a,b) _libgus_do_voice_frequency(ultra_h,a,b) +#define libgus_do_voice_pan(a,b) _libgus_do_voice_pan(ultra_h,a,b) +#define libgus_do_voice_start(a,b,c,d,e) _libgus_do_voice_start(ultra_h,a,b,c,d,e) +#define libgus_do_voice_start_position(a,b,c,d,e,f) _libgus_do_voice_start_position(ultra_h,a,b,c,d,e,f) +#define libgus_do_voice_stop(a,b) _libgus_do_voice_stop(ultra_h,a,b) +#define libgus_do_voice_volume(a,b) _libgus_do_voice_volume(ultra_h,a,b) +#define libgus_do_wait(a) _libgus_do_wait(ultra_h,a) +#define libgus_info(a,b) _libgus_info(ultra_h,a,b) +#define libgus_memory_alloc(a) _libgus_memory_alloc(ultra_h,a) +#define libgus_memory_free(a) _libgus_memory_free(ultra_h,a) +#define libgus_memory_free_size() _libgus_memory_free_size(ultra_h) +#define libgus_memory_pack() _libgus_memory_pack(ultra_h) +#define libgus_queue_flush() _libgus_queue_flush(ultra_h) +#define libgus_queue_read_set_size(a) _libgus_queue_read_set_size(ultra_h,a) +#define libgus_queue_write_set_size(a) _libgus_queue_write_set_size(ultra_h,a) +#define libgus_reset(a,b) _libgus_reset(ultra_h,a,b) +#define libgus_timer_start() _libgus_timer_start(ultra_h) +#define libgus_timer_stop() _libgus_timer_stop(ultra_h) +#define libgus_timer_tempo(a) _libgus_timer_tempo(ultra_h,a) +#endif + +#define GUS_SAMPLES 256 /* Max. GUS samples loadable */ +#define GUS_CHANNELS 32 /* Max. GUS channels available */ +#define SIZE_OF_SEQBUF (8 * 1024) /* Size of the sequence buffer */ +#define ULTRA_PAN_MIDDLE (16383 >> 1) /* Middle balance position */ + +#define CH_FREQ 1 +#define CH_VOL 2 +#define CH_PAN 4 + +/* This structure holds the current state of a GUS voice channel. */ +typedef struct GUS_VOICE { + UBYTE kick; + UBYTE active; + UWORD flags; + SWORD handle; + ULONG start; + ULONG size; + ULONG reppos; + ULONG repend; + ULONG frq; + int vol; + int decvol; + int pan; + + int changes; +#ifndef HAVE_VOICE_STATUS + time_t started; +#endif +} GUS_VOICE; + +/* Global declarations follow */ + +static SAMPLE *samples[GUS_SAMPLES]; /* sample handles */ +static GUS_VOICE voices[GUS_CHANNELS]; /* channel status */ + +static int ultra_dev = 0; /* GUS index, if more than one card */ +#if LIBGUS_VERSION_MAJOR < 0x0004 +static int ultra_card = -1; /* returned by gus_open(ultra_dev,,) - must be same as ultra_dev */ +#else +static void* ultra_h = NULL; /* GUS handle */ +#endif +static int ultra_fd = -1; /* GUS file descriptor */ + + +#ifdef MIKMOD_DYNAMIC +static int Ultra_Link(void) +{ + if (libgus) + return 0; + + /* load libgus.so */ +#if LIBGUS_VERSION_MAJOR < 0x0004 + libgus = dlopen("libgus.so.3", RTLD_LAZY | RTLD_GLOBAL); +#else + libgus = dlopen("libgus.so.4", RTLD_LAZY | RTLD_GLOBAL); +#endif + if (!libgus) /* then this won't succeed either, but whatever.. */ + libgus = dlopen("libgus.so", RTLD_LAZY | RTLD_GLOBAL); + if (!libgus) + return 1; + + /* resolve function references */ +#define IMPORT_SYMBOL(x,ret,params) \ + if (!(_lib##x = (ret (*)params) dlsym(libgus, #x))) return 1 + + IMPORT_SYMBOL(gus_cards, int, (void)); +#if LIBGUS_VERSION_MAJOR < 0x0004 + IMPORT_SYMBOL(gus_close, int, (int)); + IMPORT_SYMBOL(gus_do_flush, int, (void)); + IMPORT_SYMBOL(gus_do_tempo, void, (unsigned int)); + IMPORT_SYMBOL(gus_do_voice_frequency, void, (unsigned char, unsigned int)); + IMPORT_SYMBOL(gus_do_voice_pan, void, (unsigned char, unsigned short)); + IMPORT_SYMBOL(gus_do_voice_start, void, (unsigned char, unsigned int, unsigned int, unsigned short, unsigned short)); + IMPORT_SYMBOL(gus_do_voice_start_position, void, (unsigned char, unsigned int, unsigned int, unsigned short, unsigned short, unsigned int)); + IMPORT_SYMBOL(gus_do_voice_stop, void, (unsigned char, unsigned char)); + IMPORT_SYMBOL(gus_do_voice_volume, void, (unsigned char, unsigned short)); + IMPORT_SYMBOL(gus_do_wait, void, (unsigned int)); + IMPORT_SYMBOL(gus_get_handle, int, (void)); + IMPORT_SYMBOL(gus_info, int, (gus_info_t *, int)); + IMPORT_SYMBOL(gus_memory_alloc, int, (gus_instrument_t *)); + IMPORT_SYMBOL(gus_memory_free, int, (gus_instrument_t *)); + IMPORT_SYMBOL(gus_memory_free_size, int, (void)); + IMPORT_SYMBOL(gus_memory_pack, int, (void)); + IMPORT_SYMBOL(gus_open, int, (int, size_t, int)); + IMPORT_SYMBOL(gus_queue_flush, int, (void)); + IMPORT_SYMBOL(gus_queue_read_set_size, int, (int)); + IMPORT_SYMBOL(gus_queue_write_set_size, int, (int)); + IMPORT_SYMBOL(gus_reset, int, (int, unsigned int)); + IMPORT_SYMBOL(gus_select, int, (int)); + IMPORT_SYMBOL(gus_timer_start, int, (void)); + IMPORT_SYMBOL(gus_timer_stop, int, (void)); + IMPORT_SYMBOL(gus_timer_tempo, int, (int)); +#else + IMPORT_SYMBOL(gus_close, int, (void*)); + IMPORT_SYMBOL(gus_do_flush, int, (void*)); + IMPORT_SYMBOL(gus_do_tempo, void, (void*, unsigned int)); + IMPORT_SYMBOL(gus_do_voice_frequency, void, (void*, unsigned char, unsigned int)); + IMPORT_SYMBOL(gus_do_voice_pan, void, (void*, unsigned char, unsigned short)); + IMPORT_SYMBOL(gus_do_voice_start, void, (void*, unsigned char, unsigned int, unsigned int, unsigned short, unsigned short)); + IMPORT_SYMBOL(gus_do_voice_start_position, void, (void*, unsigned char, unsigned int, unsigned int, unsigned short, unsigned short, unsigned int)); + IMPORT_SYMBOL(gus_do_voice_stop, void, (void*, unsigned char, unsigned char)); + IMPORT_SYMBOL(gus_do_voice_volume, void, (void*, unsigned char, unsigned short)); + IMPORT_SYMBOL(gus_do_wait, void, (void*, unsigned int)); + IMPORT_SYMBOL(gus_get_file_descriptor, int, (void*)); + IMPORT_SYMBOL(gus_info, int, (void*, gus_info_t *, int)); + IMPORT_SYMBOL(gus_memory_alloc, int, (void*, gus_instrument_t *)); + IMPORT_SYMBOL(gus_memory_free, int, (void*, gus_instrument_t *)); + IMPORT_SYMBOL(gus_memory_free_size, int, (void*)); + IMPORT_SYMBOL(gus_memory_pack, int, (void*)); + IMPORT_SYMBOL(gus_open, int, (void**, int, int, size_t, int)); + IMPORT_SYMBOL(gus_queue_flush, int, (void*)); + IMPORT_SYMBOL(gus_queue_read_set_size, int, (void*, int)); + IMPORT_SYMBOL(gus_queue_write_set_size, int, (void*, int)); + IMPORT_SYMBOL(gus_reset, int, (void*, int, unsigned int)); + IMPORT_SYMBOL(gus_timer_start, int, (void*)); + IMPORT_SYMBOL(gus_timer_stop, int, (void*)); + IMPORT_SYMBOL(gus_timer_tempo, int, (void*, int)); +#endif +#undef IMPORT_SYMBOL + + return 0; +} + +static void Ultra_Unlink(void) +{ + _libgus_cards = NULL; + _libgus_close = NULL; + _libgus_do_flush = NULL; + _libgus_do_tempo = NULL; + _libgus_do_voice_frequency = NULL; + _libgus_do_voice_pan = NULL; + _libgus_do_voice_start = NULL; + _libgus_do_voice_start_position = NULL; + _libgus_do_voice_stop = NULL; + _libgus_do_voice_volume = NULL; + _libgus_do_wait = NULL; +#if LIBGUS_VERSION_MAJOR < 0x0004 + _libgus_get_handle = NULL; +#else + _libgus_get_file_descriptor = NULL; +#endif + _libgus_info = NULL; + _libgus_memory_alloc = NULL; + _libgus_memory_free = NULL; + _libgus_memory_free_size = NULL; + _libgus_memory_pack = NULL; + _libgus_open = NULL; + _libgus_queue_flush = NULL; + _libgus_queue_read_set_size = NULL; + _libgus_queue_write_set_size = NULL; + _libgus_reset = NULL; +#if LIBGUS_VERSION_MAJOR < 0x0004 + _libgus_select = NULL; +#endif + _libgus_timer_start = NULL; + _libgus_timer_stop = NULL; + _libgus_timer_tempo = NULL; + + if (libgus) { + dlclose(libgus); + libgus = NULL; + } +} +#endif + +static void Ultra_CommandLine(const CHAR *cmdline) +{ + CHAR *ptr = MD_GetAtom("card", cmdline, 0); + + if (ptr) { + int buf = atoi(ptr); + + if (buf >= 0 && buf <= 8) + ultra_dev = buf; + MikMod_free(ptr); + } +#ifdef __DJGPP__ + ptr = MD_GetAtom("dma", cmdline, 0); + if (ptr) { + gus_dma_usage (atoi(ptr)); + MikMod_free(ptr); + } +#endif +} + +/* Checks for the presence of GUS cards */ +static BOOL Ultra_IsThere(void) +{ + BOOL retval; + +#ifdef MIKMOD_DYNAMIC + if (Ultra_Link()) + return 0; +#endif + retval = libgus_cards()? 1 : 0; +#ifdef MIKMOD_DYNAMIC + Ultra_Unlink(); +#endif + return retval; +} + +/* Load a new sample directly into GUS DRAM and return a handle */ +static SWORD Ultra_SampleLoad(struct SAMPLOAD *sload, int type) +{ + int handle; + SAMPLE *s = sload->sample; + gus_instrument_t instrument; + gus_layer_t layer; + gus_wave_t wave; + unsigned char *buffer; + unsigned int length, loopstart, loopend; + + /* Find empty slot to put sample in */ + for (handle = 0; handle < GUS_SAMPLES; handle++) + if (!samples[handle]) + break; + + if (handle == GUS_SAMPLES) { + _mm_errno = MMERR_OUT_OF_HANDLES; + return -1; + } + + /* Fill an gus_instrument_t structure and feed it to libgus. We can + download 8 and 16 bit, both signed and unsigned samples into GUS, so + don't bother much about formats here. */ + + /* convert position/length data from samples to bytes */ + length = s->length; + loopstart = s->loopstart; + loopend = s->loopend ? s->loopend : length; + /* sanity checks */ + if (loopend > length) + loopend = length; + if (loopstart > loopend) + loopstart = loopend; + if (s->flags & SF_16BITS) { + length <<= 1; + loopstart <<= 1; + loopend <<= 1; + } + + /* Load sample into normal memory */ + if (!(buffer = (unsigned char *) MikMod_malloc(length))) { + _mm_errno = MMERR_SAMPLE_TOO_BIG; + return -1; + } + + if (SL_Load(buffer, sload, s->length)) { + MikMod_free(buffer); + return -1; + } + + samples[handle] = s; + + memset(&wave, 0, sizeof(wave)); + memset(&layer, 0, sizeof(layer)); + memset(&instrument, 0, sizeof(instrument)); + + wave.format = + ((s->flags & SF_SIGNED) ? 0 : GUS_WAVE_INVERT) | + ((s->flags & SF_16BITS) ? GUS_WAVE_16BIT : 0) | + ((s->flags & SF_DELTA ) ? GUS_WAVE_DELTA : 0) | + ((s->flags & SF_LOOP ) ? GUS_WAVE_LOOP : 0) | + ((s->flags & SF_BIDI ) ? GUS_WAVE_BIDIR : 0); + wave.begin.ptr = buffer; + wave.loop_start = loopstart << 4; + wave.loop_end = loopend << 4; + wave.size = length; + + layer.wave = &wave; + + instrument.mode = layer.mode = wave.mode = GUS_INSTR_SIMPLE; + instrument.number.instrument = handle; + instrument.info.layer = &layer; + + /* Download the sample to GUS RAM */ + if (libgus_memory_alloc(&instrument)) { + MikMod_free(buffer); + _mm_errno = MMERR_SAMPLE_TOO_BIG; + return -1; + } + + MikMod_free(buffer); + return handle; +} + +/* Discards a sample from the GUS memory and mark handle as free */ +static void Ultra_SampleUnload(SWORD handle) +{ + gus_instrument_t instrument; + + if (handle >= GUS_SAMPLES || handle < 0 || !samples[handle]) + return; + + memset(&instrument, 0, sizeof(instrument)); + instrument.mode = GUS_INSTR_SIMPLE; + instrument.number.instrument = handle; + libgus_memory_free(&instrument); + samples[handle] = NULL; +} + +/* Reports available sample space */ +static ULONG Ultra_SampleSpace(int type) +{ + libgus_memory_pack(); + return (libgus_memory_free_size()); +} + +/* Reports the size of a sample */ +static ULONG Ultra_SampleLength(int type, SAMPLE *s) +{ + if (!s) + return 0; + + if (s->flags & SF_16BITS) + return ((s->length << 1) + 31) & ~31; + else + return ( s->length + 15) & ~15; +} + +/* Initializes the driver */ +static int Ultra_Init_internal(void) +{ + gus_info_t info; + +#if LIBGUS_VERSION_MAJOR < 0x0004 + if ((ultra_card = libgus_open(ultra_dev, SIZE_OF_SEQBUF, 0)) < 0) { + _mm_errno = (errno == ENOMEM)? MMERR_OUT_OF_MEMORY : MMERR_INVALID_DEVICE; + return 1; + } + libgus_select(ultra_card); + ultra_fd = libgus_get_handle(); +#else + if (libgus_open(&ultra_h, ultra_dev, 0, SIZE_OF_SEQBUF, GUS_OPEN_FLAG_NONE) < 0) { + _mm_errno = (errno == ENOMEM)? MMERR_OUT_OF_MEMORY : MMERR_INVALID_DEVICE; + return 1; + } + ultra_fd = libgus_get_file_descriptor(ultra_h); +#endif + + /* We support only 16-bit stereo with 44K mixing frequency. On UltraSound + Classic mixing frequency depends on number of channels, on Interwave it + is always 44KHz. */ + md_mode |= DMODE_16BITS | DMODE_STEREO; + md_mixfreq = info.mixing_freq; + + libgus_info(&info, 0); +#ifdef MIKMOD_DEBUG + switch (info.version) { + case 0x24: + fputs("GUS 2.4", stderr); + break; + case 0x35: + fputs("GUS 3.7 (flipped)", stderr); + break; + case 0x37: + fputs("GUS 3.7", stderr); + break; + case 0x90: + fputs("GUS ACE", stderr); + break; + case 0xa0: + fputs("GUS MAX 10", stderr); + break; + case 0xa1: + fputs("GUS MAX 11", stderr); + break; + case 0x100: + fputs("Interwave/GUS PnP", stderr); + break; + default: + fprintf(stderr, "Unknown GUS type %x", info.version); + break; + } + fprintf(stderr, " with %dKb RAM on board\n", info.memory_size >> 10); +#endif + + /* Zero the voice states and sample handles */ + memset (&voices, 0, sizeof (voices)); + memset (&samples, 0, sizeof (samples)); + + return 0; +} + +static int Ultra_Init(void) +{ +#ifdef MIKMOD_DYNAMIC + if (Ultra_Link()) { + _mm_errno = MMERR_DYNAMIC_LINKING; + return 1; + } +#endif + return Ultra_Init_internal(); +} + +/* Closes the driver */ +static void Ultra_Exit_internal(void) +{ +#if LIBGUS_VERSION_MAJOR < 0x0004 + if (ultra_card >= 0) { + ultra_card = -1; + libgus_close(ultra_dev); + } +#else + if (ultra_h) { + libgus_close(ultra_h); + ultra_h = NULL; + } +#endif + ultra_fd = -1; +} + +static void Ultra_Exit(void) +{ + Ultra_Exit_internal(); +#ifdef MIKMOD_DYNAMIC + Ultra_Unlink(); +#endif +} + +/* Poor man's reset function */ +static int Ultra_Reset(void) +{ + Ultra_Exit_internal(); + return Ultra_Init_internal(); +} + +static int Ultra_SetNumVoices(void) +{ + return 0; +} + +/* Module player tick function */ +static void UltraPlayer(void) +{ + int channel, panning; + struct GUS_VOICE *voice; + static BOOL ultra_pause = 1; /* paused status */ + + md_player(); + + if (ultra_pause != Player_Paused()) + if ((ultra_pause = Player_Paused())) + for (channel = 0, voice = voices; channel < md_numchn; + channel++, voice++) { + libgus_do_voice_volume (channel, 0); + voices->changes |= (CH_VOL | CH_FREQ | CH_PAN); + } + + if (ultra_pause) + return; + + for (channel = 0, voice = voices; channel < md_numchn; channel++, voice++) { + panning = (voice->pan == PAN_SURROUND) ? + ULTRA_PAN_MIDDLE : (voice->pan << 6); + + if (voice->kick) { + voice->kick = 0; + voice->decvol = voice->vol; + if (voice->start > 0) + libgus_do_voice_start_position(channel, voice->handle, + voice->frq, voice->vol << 6, panning, voice->start << 4); + else + libgus_do_voice_start(channel, voice->handle, voice->frq, + voice->vol << 6, voice->pan << 6); + } else { + if (voice->changes & CH_FREQ) + libgus_do_voice_frequency(channel, voice->frq); + if (voice->changes & CH_VOL) + libgus_do_voice_volume(channel, voice->vol << 6); + if (voice->changes & CH_PAN) + libgus_do_voice_pan(channel, panning); + if (voice->decvol) + voice->decvol -= 4; + } + voice->changes = 0; + } +} + +/* Play sound */ +#if defined(__DJGPP__) || defined(__EMX__) +static void Ultra_Callback(void) +{ + UltraPlayer(); + libgus_do_flush(); +} + +static void Ultra_Update(void) +{ + static UWORD ultra_bpm = 0; /* current GUS tempo */ + + /* All real work is done during GF1 timer 1 interrupt */ + if (ultra_bpm != md_bpm) { + libgus_do_tempo((md_bpm * 50) / 125); + ultra_bpm = md_bpm; + } +} + +#else +static void Ultra_Update(void) +{ + fd_set write_fds; + int need_write; + static UWORD ultra_bpm = 0; /* current GUS tempo */ + + if (ultra_bpm != md_bpm) { + libgus_do_tempo((md_bpm * 50) / 125); + ultra_bpm = md_bpm; + } + + UltraPlayer(); + + do { + need_write = libgus_do_flush(); + + FD_ZERO(&write_fds); + do { + FD_SET(ultra_fd, &write_fds); + + select(ultra_fd + 1, NULL, &write_fds, NULL, NULL); + } while (!FD_ISSET(ultra_fd, &write_fds)); + } while(need_write > 0); + + /* Wait so that all voice commands gets executed */ + libgus_do_wait (1); +} + +#endif + +/* Start playback */ +static int Ultra_PlayStart(void) +{ + int t; + gus_info_t info; + + for (t = 0; t < md_numchn; t++) { + voices[t].flags = 0; + voices[t].handle = 0; + voices[t].size = 0; + voices[t].start = 0; + voices[t].reppos = 0; + voices[t].repend = 0; + voices[t].changes = 0; + voices[t].kick = 0; + voices[t].frq = 10000; + voices[t].vol = 0; + voices[t].pan = ULTRA_PAN_MIDDLE; + voices[t].active = 0; + } + +#if LIBGUS_VERSION_MAJOR < 0x0004 + libgus_select(ultra_card); +#endif + if (libgus_reset(md_numchn, 0) < 0) { + _mm_errno = MMERR_GUS_RESET; + return 1; + } + + /* Query mixing frequency */ + libgus_info(&info, 0); + md_mixfreq = info.mixing_freq; + + libgus_queue_write_set_size(1024); + libgus_queue_read_set_size(128); + + if (libgus_timer_start() < 0) { + _mm_errno = MMERR_GUS_TIMER; + return 1; + } + +#if defined(__DJGPP__) || defined(__EMX__) + gus_timer_callback(Ultra_Callback); +#endif + + libgus_timer_tempo(50); + + for (t = 0; t < md_numchn; t++) { + libgus_do_voice_pan(t, ULTRA_PAN_MIDDLE); + libgus_do_voice_volume(t, 0 << 8); + } + + libgus_do_flush(); + + return 0; +} + +/* Stop playback */ +static void Ultra_PlayStop(void) +{ + int voice; + + libgus_queue_flush(); + libgus_timer_stop(); + libgus_queue_write_set_size(0); + libgus_queue_read_set_size(0); + for(voice = 0; voice < md_numchn; voice++) + libgus_do_voice_stop(voice, 0); + +#if defined(__DJGPP__) || defined(__EMX__) + gus_timer_callback(NULL); +#endif + + libgus_do_flush(); +} + +/* Set the volume for the given voice */ +static void Ultra_VoiceSetVolume(UBYTE voice, UWORD vol) +{ + if (voice < md_numchn) + if (vol != voices[voice].vol) { + voices[voice].decvol = + voices[voice].vol = vol; + voices[voice].changes |= CH_VOL; + } +} + +/* Returns the volume of the given voice */ +static UWORD Ultra_VoiceGetVolume(UBYTE voice) +{ + return (voice < md_numchn) ? voices[voice].vol : 0; +} + +/* Set the pitch for the given voice */ +static void Ultra_VoiceSetFrequency(UBYTE voice, ULONG frq) +{ + if (voice < md_numchn) + if (frq != voices[voice].frq) { + voices[voice].frq = frq; + voices[voice].changes |= CH_FREQ; + } +} + +/* Returns the frequency of the given voice */ +static ULONG Ultra_VoiceGetFrequency(UBYTE voice) +{ + return (voice < md_numchn) ? voices[voice].frq : 0; +} + +/* Set the panning position for the given voice */ +static void Ultra_VoiceSetPanning(UBYTE voice, ULONG pan) +{ + if (voice < md_numchn) + if (pan != voices[voice].pan) { + voices[voice].pan = pan; + voices[voice].changes |= CH_PAN; + } +} + +/* Returns the panning of the given voice */ +static ULONG Ultra_VoiceGetPanning(UBYTE voice) +{ + return (voice < md_numchn) ? voices[voice].pan : 0; +} + +/* Start a new sample on a voice */ +static void Ultra_VoicePlay(UBYTE voice, SWORD handle, ULONG start, + ULONG size, ULONG reppos, ULONG repend, + UWORD flags) +{ + if ((voice >= md_numchn) || (start >= size)) + return; + + if (flags & SF_LOOP) + if (repend > size) + repend = size; + + voices[voice].flags = flags; + voices[voice].handle = handle; + voices[voice].start = start; + voices[voice].size = size; + voices[voice].reppos = reppos; + voices[voice].repend = repend; + voices[voice].kick = 1; + voices[voice].active = 1; +#ifndef HAVE_VOICE_STATUS + voices[voice].started = time(NULL); +#endif +} + +/* Stops a voice */ +static void Ultra_VoiceStop(UBYTE voice) +{ + if (voice < md_numchn) + voices[voice].active = 0; +} + +/* Returns whether a voice is stopped */ +static BOOL Ultra_VoiceStopped(UBYTE voice) +{ + if (voice >= md_numchn) + return 1; + +#ifdef HAVE_VOICE_STATUS + if (voices[voice].active) + return gus_get_voice_status(voice) ? 0 : 1; + else + return 1; +#else + if (voices[voice].active) { + /* is sample looping ? */ + if (voices[voice].flags & (SF_LOOP | SF_BIDI)) + return 0; + else + if (time(NULL) - voices[voice].started >= + ((voices[voice].size - voices[voice].start + md_mixfreq -1) + / md_mixfreq)) { + voices[voice].active = 0; + return 1; + } + return 0; + } else + return 1; +#endif +} + +/* Returns current voice position */ +static SLONG Ultra_VoiceGetPosition(UBYTE voice) +{ + /* NOTE This information can not be determined. */ + return -1; +} + +/* Returns voice real volume */ +static ULONG Ultra_VoiceRealVolume(UBYTE voice) +{ + int retval = 0; + if (!Ultra_VoiceStopped (voice)) { + /* NOTE This information can not be accurately determined. */ + retval = (voices [voice].decvol) << 8; + if (retval > 0xffff) retval = 0xffff; + } + return retval; +} + +MDRIVER drv_ultra = { + NULL, + "Gravis Ultrasound native mode", + "Gravis Ultrasound native mode driver v1.2", + 0, (GUS_CHANNELS)-1, + "ultra", +#ifdef __DJGPP__ + "dma:b:1:Use DMA for transferring samples into GUS DRAM\n" +#endif + "card:r:0,8,0:Soundcard number\n", + + Ultra_CommandLine, + Ultra_IsThere, + Ultra_SampleLoad, + Ultra_SampleUnload, + Ultra_SampleSpace, + Ultra_SampleLength, + Ultra_Init, + Ultra_Exit, + Ultra_Reset, + Ultra_SetNumVoices, + Ultra_PlayStart, + Ultra_PlayStop, + Ultra_Update, + NULL, + Ultra_VoiceSetVolume, + Ultra_VoiceGetVolume, + Ultra_VoiceSetFrequency, + Ultra_VoiceGetFrequency, + Ultra_VoiceSetPanning, + Ultra_VoiceGetPanning, + Ultra_VoicePlay, + Ultra_VoiceStop, + Ultra_VoiceStopped, + Ultra_VoiceGetPosition, + Ultra_VoiceRealVolume +}; +#else + +MISSING(drv_ultra); + +#endif /* DRV_ULTRA */ + +/* ex:set ts=8: */