📄 drv_ultra.c
字号:
/* 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: drv_ultra.c,v 1.4 2004/02/05 17:34:49 raph Exp $
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 <andy@alsa-project.org>
Updated to work with later versions of both the ultrasound driver and
libmikmod by C. Ray C. <crayc@pyro.net>
Major fixes by Andrew Zabolotny <bit@eltech.ru>
+ 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 <unistd.h>
#endif
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#ifdef MIKMOD_DYNAMIC
#include <dlfcn.h>
#endif
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <libgus.h>
/* DOS/DJGPP and OS/2 libGUS'es have gus_get_voice_status() */
#if defined (__EMX__) || defined (__DJGPP__)
#define HAVE_VOICE_STATUS
#else
#include <time.h>
#endif
#ifdef MIKMOD_DYNAMIC
/* runtime link with libgus */
static int (*libgus_cards) (void);
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);
static void *libgus = NULL;
#ifndef HAVE_RTLD_GLOBAL
#define RTLD_GLOBAL 0
#endif
#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
#define libgus_get_handle gus_get_handle
#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
#define libgus_select gus_select
#define libgus_timer_start gus_timer_start
#define libgus_timer_stop gus_timer_stop
#define libgus_timer_tempo gus_timer_tempo
#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 */
static int ultra_handle = -1; /* GUS handle */
static int ultra_fd = -1; /* GUS file descriptor */
#ifdef MIKMOD_DYNAMIC
static BOOL Ultra_Link(void)
{
if (libgus)
return 0;
/* load libgus.so */
libgus = dlopen("libgus.so", RTLD_LAZY | RTLD_GLOBAL);
if (!libgus)
return 1;
/* resolve function references */
#define IMPORT_SYMBOL(x) \
if (!(lib##x = dlsym(libgus, #x))) return 1
IMPORT_SYMBOL(gus_cards);
IMPORT_SYMBOL(gus_close);
IMPORT_SYMBOL(gus_do_flush);
IMPORT_SYMBOL(gus_do_tempo);
IMPORT_SYMBOL(gus_do_voice_frequency);
IMPORT_SYMBOL(gus_do_voice_pan);
IMPORT_SYMBOL(gus_do_voice_start);
IMPORT_SYMBOL(gus_do_voice_start_position);
IMPORT_SYMBOL(gus_do_voice_stop);
IMPORT_SYMBOL(gus_do_voice_volume);
IMPORT_SYMBOL(gus_do_wait);
IMPORT_SYMBOL(gus_get_handle);
IMPORT_SYMBOL(gus_info);
IMPORT_SYMBOL(gus_memory_alloc);
IMPORT_SYMBOL(gus_memory_free);
IMPORT_SYMBOL(gus_memory_free_size);
IMPORT_SYMBOL(gus_memory_pack);
IMPORT_SYMBOL(gus_open);
IMPORT_SYMBOL(gus_queue_flush);
IMPORT_SYMBOL(gus_queue_read_set_size);
IMPORT_SYMBOL(gus_queue_write_set_size);
IMPORT_SYMBOL(gus_reset);
IMPORT_SYMBOL(gus_select);
IMPORT_SYMBOL(gus_timer_start);
IMPORT_SYMBOL(gus_timer_stop);
IMPORT_SYMBOL(gus_timer_tempo);
#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;
libgus_get_handle = NULL;
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;
libgus_select = NULL;
libgus_timer_start = NULL;
libgus_timer_stop = NULL;
libgus_timer_tempo = NULL;
if (libgus) {
dlclose(libgus);
libgus = NULL;
}
}
#endif
static void Ultra_CommandLine(CHAR *cmdline)
{
CHAR *ptr = MD_GetAtom("card", cmdline, 0);
if (ptr) {
int buf = atoi(ptr);
if (buf >= 0 && buf <= 8)
ultra_dev = buf;
free(ptr);
}
#ifdef __DJGPP__
if ((ptr = MD_GetAtom("dma", cmdline, 0))) {
gus_dma_usage (atoi(ptr));
free(ptr);
}
#endif
}
/* Checks for the presence of GUS cards */
static BOOL Ultra_IsThere(void)
{
int 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 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 = _mm_malloc(length))) {
_mm_errno = MMERR_SAMPLE_TOO_BIG;
return -1;
}
if (SL_Load(buffer, sload, s->length)) {
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)) {
free(buffer);
_mm_errno = MMERR_SAMPLE_TOO_BIG;
return -1;
}
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(void)
{
libgus_memory_pack();
return (libgus_memory_free_size());
}
/* Reports the size of a sample */
static ULONG Ultra_SampleLength(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 BOOL Ultra_Init_internal(void)
{
gus_info_t info;
/* Open the GUS card */
if ((ultra_handle = 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_handle);
ultra_fd = libgus_get_handle();
/* Get card information */
libgus_info(&info, 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -