/* 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; if (s->length > MAX_SAMPLE_SIZE) { _mm_errno = MMERR_NOT_A_STREAM;/* better error? */ return -1; } /* 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: */