Mercurial > ~darius > hgwebdir.cgi > mikmod
diff playercode/mdriver.c @ 5:42e11dc15457
Initial revision
author | darius |
---|---|
date | Fri, 23 Jan 1998 16:05:08 +0000 |
parents | |
children | 437e8455d862 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/mdriver.c Fri Jan 23 16:05:08 1998 +0000 @@ -0,0 +1,556 @@ +/* + +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; + + +static void LimitHardVoices(int limit) + +// Limits the number of hardware voices to the specified amount. +// This function should only be used by the low-level drivers. + +{ + 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; +} + + +static void LimitSoftVoices(int limit) + +// Limits the number of hardware voices to the specified amount. +// This function should only be used by the low-level drivers. + +{ + 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); +} + + +UWORD MD_SetDMA(int secs) + +// 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. + +{ + 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; +} + + +SWORD MD_SampleLoad(SAMPLOAD *s, int type, FILE *fp) +// type - sample type .. MD_MUSIC or MD_SNDFX +{ + 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/16384UL); +} + + +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++); + + 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; +} + + +BOOL MikMod_Reset(void) + +// Reset the driver using the new global variable settings. +// If the driver has not been initialized, it will be now. + +{ + 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; +} + + +BOOL MikMod_SetNumVoices(int music, int sfx) + +// If either parameter is -1, the current set value will be retained. + +{ + 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; +} + + +int MikMod_PlaySample(SAMPLE *s, ULONG start, UBYTE flags) + +// 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 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; +} +