view playercode/mdriver.c @ 17:80fa6dd10e14

Fix some C++ 'ism's
author darius
date Thu, 23 Apr 1998 22:54:49 +0000
parents 437e8455d862
children
line wrap: on
line source

/*
 * Name:  MDRIVER.C
 * 
 * Description: These routines are used to access the available soundcard
 * drivers.
 * 
 * Portability: All systems - all compilers
 * 
 */

#include "mikmod.h"

MDRIVER        *firstdriver = NULL, *md_driver = &drv_nos;

UWORD           md_device = 0;
UWORD           md_mixfreq = 44100;
UWORD           md_mode = DMODE_STEREO | DMODE_16BITS | DMODE_SURROUND;
UWORD           md_dmabufsize = 50;
UBYTE           md_pansep = 128;		/* 128 == 100% (full left/right) */

UBYTE           md_reverb = 6;			/* Reverb */

UBYTE           md_volume = 96;			/* Global sound volume (0-128) */
UBYTE           md_musicvolume = 128;	/* volume of song */
UBYTE           md_sndfxvolume = 128;	/* volume of sound effects */

UBYTE           md_bpm = 125;


/* Do not modify the numchn variables yourself!  use MD_SetVoices() */

UBYTE           md_numchn = 0, md_sngchn = 0, md_sfxchn = 0;
UBYTE           md_hardchn = 0, md_softchn = 0;


void            (*md_player) (void) = Player_HandleTick;
static BOOL     isplaying = 0, initialized = 0;
static UBYTE   *sfxinfo;
static int      sfxpool;

static SAMPLE **md_sample = NULL;

/*
 * Backup variables.  This way, the end programmer can fiddle with the main
 * globals without mikmod blowing up.
 */

static UWORD    idevice, imixfreq, imode, idmabufsize;

/*
 * Limits the number of hardware voices to the specified amount. This
 * function should only be used by the low-level drivers.
 */
static void 
LimitHardVoices(int limit)
{
    int             t = 0;

    if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn > limit))
		md_sfxchn = limit;
    if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn > limit))
		md_sngchn = limit;

    if (!(md_mode & DMODE_SOFT_SNDFX))
		md_hardchn = md_sfxchn;
    else
		md_hardchn = 0;

    if (!(md_mode & DMODE_SOFT_MUSIC))
		md_hardchn += md_sngchn;

    while (md_hardchn > limit) {
		if (++t & 1)
			if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn > 4))
				md_sfxchn--;
			else if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn > 8))
				md_sngchn--;

		if (!(md_mode & DMODE_SOFT_SNDFX))
			md_hardchn = md_sfxchn;
		else
			md_hardchn = 0;

		if (!(md_mode & DMODE_SOFT_MUSIC))
			md_hardchn += md_sngchn;
    }

    md_numchn = md_hardchn + md_softchn;
}

/*
 * Limits the number of hardware voices to the specified amount. This
 * function should only be used by the low-level drivers.
 */
static void 
LimitSoftVoices(int limit)
{
    int             t = 0;

    if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn > limit))
		md_sfxchn = limit;
    if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn > limit))
		md_sngchn = limit;

    if (md_mode & DMODE_SOFT_SNDFX)
		md_softchn = md_sfxchn;
    else
		md_softchn = 0;

    if (md_mode & DMODE_SOFT_MUSIC)
		md_softchn += md_sngchn;

    while (md_softchn > limit) {
		if (++t & 1)
			if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn > 4))
				md_sfxchn--;
			else if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn > 8))
				md_sngchn--;

		if (!(md_mode & DMODE_SOFT_SNDFX))
			md_softchn = md_sfxchn;
		else
			md_softchn = 0;

		if (!(md_mode & DMODE_SOFT_MUSIC))
			md_softchn += md_sngchn;
    }

    md_numchn = md_hardchn + md_softchn;
}


/*
 * Note: 'type' indicates whether the returned value should be for music or
 * for sound effects.
 */
ULONG 
MD_SampleSpace(int type)
{
    if (type == MD_MUSIC)
		type = (md_mode & DMODE_SOFT_MUSIC) ? MD_SOFTWARE : MD_HARDWARE;
    else if (type == MD_SNDFX)
		type = (md_mode & DMODE_SOFT_SNDFX) ? MD_SOFTWARE : MD_HARDWARE;

    return md_driver->FreeSampleSpace(type);
}


ULONG 
MD_SampleLength(int type, SAMPLE * s)
{
    if (type == MD_MUSIC)
		type = (md_mode & DMODE_SOFT_MUSIC) ? MD_SOFTWARE : MD_HARDWARE;
    else if (type == MD_SNDFX)
		type = (md_mode & DMODE_SOFT_SNDFX) ? MD_SOFTWARE : MD_HARDWARE;

    return md_driver->RealSampleLength(type, s);
}


/*
 * Converts the given number of 1/10th seconds into the number of bytes of
 * audio that a sample # 1/10th seconds long would require at the current md_*
 * settings.
 */
UWORD 
MD_SetDMA(int secs)
{
    ULONG           result;

    result = (md_mixfreq * ((md_mode & DMODE_STEREO) ? 2 : 1) *
			  ((md_mode & DMODE_16BITS) ? 2 : 1) * secs) * 10;

    if (result > 32000)
		result = 32000;
    return (md_dmabufsize = (result & ~3));	/* round it off to an 8 byte
											 * boundry */
}


void 
MD_InfoDriver(void)
{
    int             t;
    MDRIVER        *l;

    /* list all registered devicedrivers: */
    for (t = 1, l = firstdriver; l != NULL; l = l->next, t++)
		printf("%d. %s\n", t, l->Version);
}


void 
MD_RegisterDriver(MDRIVER * drv)
{
    MDRIVER        *cruise = firstdriver;

    if (cruise != NULL) {
		while (cruise->next != NULL)
			cruise = cruise->next;
		cruise->next = drv;
    } else
		firstdriver = drv;
}


/* type - sample type .. MD_MUSIC or MD_SNDFX */
SWORD 
MD_SampleLoad(SAMPLOAD * s, int type, FILE * fp)
{
    SWORD           result;

    if (type == MD_MUSIC)
		type = (md_mode & DMODE_SOFT_MUSIC) ? MD_SOFTWARE : MD_HARDWARE;
    else if (type == MD_SNDFX)
		type = (md_mode & DMODE_SOFT_SNDFX) ? MD_SOFTWARE : MD_HARDWARE;

    SL_Init(s);
    result = md_driver->SampleLoad(s, type, fp);
    SL_Exit(s);

    return result;
}


void 
MD_SampleUnLoad(SWORD handle)
{
    md_driver->SampleUnLoad(handle);
}


void 
MD_SetBPM(UBYTE bpm)
{
    md_bpm = bpm;
}


void 
MikMod_RegisterPlayer(void (*player) (void))
{
    md_player = player;
}


void 
MikMod_Update(void)
{
    if (isplaying)
		md_driver->Update();
}


void 
Voice_SetVolume(int voice, UWORD vol)
{
    ULONG           tmp;

    if ((voice < 0) || (voice >= md_numchn))
		return;
    tmp = (ULONG) vol *(ULONG) md_volume *
		((voice < md_sngchn) ? (ULONG) md_musicvolume : (ULONG) md_sndfxvolume);
    md_driver->VoiceSetVolume(voice, tmp / 16384);
}


void 
Voice_SetFrequency(int voice, ULONG frq)
{
    if ((voice < 0) || (voice >= md_numchn))
		return;
    if (md_sample[voice] != NULL && md_sample[voice]->divfactor != 0)
		frq /= md_sample[voice]->divfactor;
    md_driver->VoiceSetFrequency(voice, frq);
}


void 
Voice_SetPanning(int voice, ULONG pan)
{
    if ((voice < 0) || (voice >= md_numchn))
		return;
    if (pan != PAN_SURROUND) {
		if (md_mode & DMODE_REVERSE)
			pan = 255 - pan;
		pan = (((SWORD) (pan - 128) * md_pansep) / 128) + 128;
    }
    md_driver->VoiceSetPanning(voice, pan);
}


void 
Voice_Play(int voice, SAMPLE * s, ULONG start)
{
    ULONG           repend;

    if ((voice < 0) || (voice >= md_numchn) || (start >= s->length))
		return;

    md_sample[voice] = s;
    repend = s->loopend;

    if (s->flags & SF_LOOP)
		if (repend > s->length)
			repend = s->length;	/* repend can't be bigger than size */

    md_driver->VoicePlay(voice, s->handle, start, s->length, s->loopstart, repend, s->flags);
}


void 
Voice_Stop(int voice)
{
    if ((voice < 0) || (voice >= md_numchn))
		return;
    if (voice >= md_sngchn) {	/* It is a sound effects channel, so flag the
								 * voice as non-critical! */
		sfxinfo[voice - md_sngchn] = 0;
    }
    md_driver->VoiceStop(voice);
}


BOOL 
Voice_Stopped(int voice)
{
    if ((voice < 0) || (voice >= md_numchn))
		return 0;
    return (md_driver->VoiceStopped(voice));
}


SLONG 
Voice_GetPosition(int voice)
{
    if ((voice < 0) || (voice >= md_numchn))
		return 0;
    return (md_driver->VoiceGetPosition(voice));
}


ULONG 
Voice_RealVolume(int voice)
{
    if ((voice < 0) || (voice >= md_numchn))
		return 0;
    return (md_driver->VoiceRealVolume(voice));
}


/*
 * Functions prefixed with MikMod
 */
BOOL 
MikMod_Init(void)
{
    UWORD           t;

    _mm_critical = 1;

    /* if md_device==0, try to find a device number */

    if (md_device == 0) {
		for (t = 1, md_driver = firstdriver; md_driver != NULL; md_driver = md_driver->next, t++) {
			if (md_driver->IsPresent())
				break;
		}

		if (md_driver == NULL) {
			_mm_errno = MMERR_DETECTING_DEVICE;
			if (_mm_errorhandler != NULL)
				_mm_errorhandler();
			md_driver = &drv_nos;
			return 1;
		}
		md_device = t;
    } else {			/* if n>0 use that driver */
		for (t = 1, md_driver = firstdriver; 
			 (md_driver != NULL) && (t != md_device);
			 md_driver = md_driver->next, t++)
			; 	/* Nothing */

		if (md_driver == NULL) {
			_mm_errno = MMERR_INVALID_DEVICE;
			if (_mm_errorhandler != NULL)
				_mm_errorhandler();
			md_driver = &drv_nos;
			return 1;
		}
		if (!md_driver->IsPresent()) {
			_mm_errno = MMERR_DETECTING_DEVICE;
			if (_mm_errorhandler != NULL)
				_mm_errorhandler();
			md_driver = &drv_nos;
			return 1;
		}
    }

    if (md_driver->Init()) {
		MikMod_Exit();
		if (_mm_errorhandler != NULL)
			_mm_errorhandler();
		return 1;
    }
    idevice = md_device;
    imode = md_mode;
    imixfreq = md_mixfreq;
    idmabufsize = md_dmabufsize;
    initialized = 1;
    _mm_critical = 0;

    return 0;
}


void 
MikMod_Exit(void)
{
    MikMod_DisableOutput();
    md_driver->Exit();
    md_numchn = md_sfxchn = md_sngchn = 0;
    md_driver = &drv_nos;
    initialized = 0;
}


/*
 * Reset the driver using the new global variable settings. If the driver has
 * not been initialized, it will be now.
 */
BOOL 
MikMod_Reset(void)
{
    if (!initialized)
		return MikMod_Init();
    if ((md_driver->Reset == NULL) || 
		(md_device != idevice)) {	/* md_driver->Reset was
									 * NULL, or md_device
									 * was changed, so do a
									 * full reset of the
									 * driver. */
		if (isplaying)
			md_driver->PlayStop();
		md_driver->Exit();
		if (MikMod_Init()) {
			MikMod_Exit();
			if (_mm_errorhandler != NULL)
				_mm_errorhandler();
			return 1;
		}
		if (isplaying)
			md_driver->PlayStart();
    } else {
		if (md_driver->Reset()) {
			MikMod_Exit();
			if (_mm_errorhandler != NULL)
				_mm_errorhandler();
			return 1;
		}
    }

    return 0;
}


/* If either parameter is -1, the current set value will be retained. */
BOOL 
MikMod_SetNumVoices(int music, int sfx)
{
    BOOL            resume = 0;
    int             t, oldchn = 0;

    if ((music == 0) && (sfx == 0))
		return 0;

    _mm_critical = 1;

    if (isplaying) {
		MikMod_DisableOutput();
		oldchn = md_numchn;
		resume = 1;
    }
    if (sfxinfo != NULL)
		free(sfxinfo);
    if (md_sample != NULL)
		free(md_sample);
    md_sample = NULL;
    sfxinfo = NULL;

    /*
     * md_softchn = md_hardchn = 0;
     * 
     * if(md_mode & DMODE_SOFT_SNDFX) md_softchn = sfx; else md_hardchn = sfx;
     * 
     * if(md_mode & DMODE_SOFT_MUSIC) md_softchn += music; else md_hardchn +=
     * music;
     */

    if (music != -1)
		md_sngchn = music;
    if (sfx != -1)
		md_sfxchn = sfx;

    md_numchn = md_sngchn + md_sfxchn;

    LimitHardVoices(md_driver->HardVoiceLimit);
    LimitSoftVoices(md_driver->SoftVoiceLimit);

    if (md_driver->SetNumVoices()) {
		MikMod_Exit();
		md_numchn = md_softchn = md_hardchn = md_sfxchn = md_sngchn = 0;
		if (_mm_errorhandler != NULL)
			_mm_errorhandler();
		return 1;
    }
    if (md_sngchn || md_sfxchn)
		md_sample = (SAMPLE **) _mm_calloc(md_sngchn + md_sfxchn, sizeof(SAMPLE *));
    if (md_sfxchn)
		sfxinfo = (UBYTE *) _mm_calloc(md_sfxchn, sizeof(UBYTE));

    /* make sure the player doesn't start with garbage */
    for (t = oldchn; t < md_numchn; t++)
		Voice_Stop(t);

    sfxpool = 0;

    if (resume)
		MikMod_EnableOutput();
    _mm_critical = 0;

    return 0;
}


BOOL 
MikMod_EnableOutput(void)
{
    /*
     * safety valve, prevents entering playstart twice:
     */
    _mm_critical = 1;
    if (!isplaying) {
		if (md_driver->PlayStart())
			return 1;
		isplaying = 1;
    }
    _mm_critical = 0;
    return 0;
}


void 
MikMod_DisableOutput(void)
{
    /*
     * safety valve, prevents calling playStop when playstart hasn't been
     * called:
     */

    if (isplaying && md_driver != NULL) {
		isplaying = 0;
		md_driver->PlayStop();
    }
}


BOOL 
MikMod_Active(void)
{
    return isplaying;
}


/*
 * Plays a sound effects sample.  Picks a voice from the number of voices
 * allocated for use as sound effects (loops through voices, skipping all
 * active criticals).
 * 
 * Returns the voice that the sound is being played on.
 */
int 
MikMod_PlaySample(SAMPLE * s, ULONG start, UBYTE flags)
{
    int             orig = sfxpool;	/* for cases where all channels are
									 * critical */
    int             c;

    if (md_sfxchn == 0)
		return -1;
    if (s->volume > 64)
		s->volume = 64;

    /* check the first location after sfxpool */
    do {
		if (sfxinfo[sfxpool] & SFX_CRITICAL) {
			if (md_driver->VoiceStopped(c = sfxpool + md_sngchn)) {
				sfxinfo[sfxpool] = flags;
				Voice_Play(c, s, start);
				md_driver->VoiceSetVolume(c, s->volume << 2);
				md_driver->VoiceSetPanning(c, s->panning);
				md_driver->VoiceSetFrequency(c, s->speed);
				sfxpool++;
				if (sfxpool >= md_sfxchn)
					sfxpool = 0;
				return c;
			}
		} else {
			sfxinfo[sfxpool] = flags;
			Voice_Play(c = sfxpool + md_sngchn, s, start);
			md_driver->VoiceSetVolume(c, s->volume << 2);
			md_driver->VoiceSetPanning(c, s->panning);
			md_driver->VoiceSetFrequency(c, s->speed);
			sfxpool++;
			if (sfxpool >= md_sfxchn)
				sfxpool = 0;
			return c;
		}

		sfxpool++;
		if (sfxpool >= md_sfxchn)
			sfxpool = 0;
    } while (sfxpool != orig);

    return -1;
}