add mikmod source
[dosdemo] / libs / mikmod / playercode / mdriver.c
diff --git a/libs/mikmod/playercode/mdriver.c b/libs/mikmod/playercode/mdriver.c
new file mode 100644 (file)
index 0000000..b3d3d4a
--- /dev/null
@@ -0,0 +1,968 @@
+/*     MikMod sound library
+       (c) 1998-2014 Miodrag Vallat and others - see file AUTHORS
+       for a 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.
+*/
+
+/*==============================================================================
+
+  These routines are used to access the available soundcard drivers.
+
+==============================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <string.h>
+
+#include "mikmod_internals.h"
+
+#if (MIKMOD_UNIX)
+#include <pwd.h>
+#include <sys/stat.h>
+#endif
+
+#ifdef SUNOS
+extern int fprintf(FILE *, const char *, ...);
+#endif
+
+extern MODULE *pf; /* modfile being played */
+
+/* EXPORTED GLOBALS */
+MIKMODAPI MDRIVER *md_driver   = NULL;
+
+/* Initial global settings */
+MIKMODAPI UWORD md_device      = 0;    /* autodetect */
+MIKMODAPI UWORD md_mixfreq     = 44100;
+MIKMODAPI UWORD md_mode                = DMODE_STEREO | DMODE_16BITS |
+                                 DMODE_SURROUND |
+                                 DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX;
+MIKMODAPI UBYTE md_pansep      = 128;  /* 128 == 100% (full left/right) */
+MIKMODAPI UBYTE md_reverb      = 0;    /* no reverb */
+MIKMODAPI UBYTE md_volume      = 128;  /* global sound volume (0-128) */
+MIKMODAPI UBYTE md_musicvolume = 128;  /* volume of song */
+MIKMODAPI UBYTE md_sndfxvolume = 128;  /* volume of sound effects */
+
+/* INTERNAL GLOBALS */
+UWORD md_bpm = 125;    /* tempo */
+
+/* Do not modify the numchn variables yourself!  use MikMod_SetNumVoices() */
+UBYTE md_numchn  = 0, md_sngchn = 0, md_sfxchn = 0;
+UBYTE md_hardchn = 0, md_softchn= 0;
+
+void (*md_player)(void) = Player_HandleTick;
+
+MikMod_callback_t vc_callback = NULL;
+
+/* PRIVATE VARS */
+static MDRIVER *firstdriver = NULL;
+
+static volatile BOOL isplaying = 0, initialized = 0;
+
+static UBYTE *sfxinfo;
+static int sfxpool;
+
+static SAMPLE **md_sample = NULL;
+
+/* Previous driver in use */
+static SWORD olddevice = -1;
+
+/* 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);
+}
+
+MIKMODAPI CHAR* MikMod_InfoDriver(void)
+{
+       int t;
+       size_t len=0;
+       MDRIVER *l;
+       CHAR *list=NULL;
+
+       MUTEX_LOCK(lists);
+       /* compute size of buffer */
+       for(l = firstdriver; l; l = l->next)
+               len += 4 + (l->next ? 1 : 0) + strlen(l->Version);
+
+       if(len)
+         if((list=(CHAR*)MikMod_malloc(len*sizeof(CHAR))) != NULL) {
+               CHAR *list_end = list;
+               list[0] = 0;
+               /* list all registered device drivers : */
+               for(t = 1, l = firstdriver; l; l = l->next, t++) {
+                   list_end += sprintf(list_end, "%2d %s%s", t, l->Version, (l->next)? "\n" : "");
+               }
+       }
+       MUTEX_UNLOCK(lists);
+       return list;
+}
+
+void _mm_registerdriver(struct MDRIVER* drv)
+{
+       MDRIVER *cruise = firstdriver;
+
+       /* don't register a MISSING() driver */
+       if ((drv->Name) && (drv->Version)) {
+               if (cruise) {
+                       if ( cruise == drv )
+                               return;
+                       while(cruise->next) {
+                               cruise = cruise->next;
+                               if ( cruise == drv )
+                                       return;
+                       }
+                       cruise->next = drv;
+               } else
+                       firstdriver = drv;
+       }
+}
+
+MIKMODAPI void MikMod_RegisterDriver(struct MDRIVER* drv)
+{
+       /* if we try to register an invalid driver, or an already registered driver,
+          ignore this attempt */
+       if ((!drv)||(drv->next)||(!drv->Name))
+               return;
+
+       MUTEX_LOCK(lists);
+       _mm_registerdriver(drv);
+       MUTEX_UNLOCK(lists);
+}
+
+MIKMODAPI int MikMod_DriverFromAlias(const CHAR *alias)
+{
+       int rank=1;
+       MDRIVER *cruise;
+
+       MUTEX_LOCK(lists);
+       cruise=firstdriver;
+       while(cruise) {
+               if (cruise->Alias) {
+                       if (!(_mm_strcasecmp(alias,cruise->Alias))) break;
+                       rank++;
+               }
+               cruise=cruise->next;
+       }
+       if(!cruise) rank=0;
+       MUTEX_UNLOCK(lists);
+
+       return rank;
+}
+
+MIKMODAPI MDRIVER *MikMod_DriverByOrdinal(int ordinal)
+{
+       MDRIVER *cruise;
+
+       /* Allow only driver ordinals > 0 */
+       if (!ordinal) return NULL;
+
+       MUTEX_LOCK(lists);
+       cruise = firstdriver;
+       while (cruise && --ordinal)
+               cruise = cruise->next;
+       MUTEX_UNLOCK(lists);
+       return cruise;
+}
+
+SWORD MD_SampleLoad(SAMPLOAD* s, int type)
+{
+       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);
+       SL_Exit(s);
+
+       return result;
+}
+
+void MD_SampleUnload(SWORD handle)
+{
+       md_driver->SampleUnload(handle);
+}
+
+MIKMODAPI MikMod_player_t MikMod_RegisterPlayer(MikMod_player_t player)
+{
+       MikMod_player_t result;
+
+       MUTEX_LOCK(vars);
+       result=md_player;
+       md_player=player;
+       MUTEX_UNLOCK(vars);
+
+       return result;
+}
+
+MIKMODAPI void MikMod_Update(void)
+{
+       MUTEX_LOCK(vars);
+       if(isplaying) {
+               if((!pf)||(!pf->forbid))
+                       md_driver->Update();
+               else {
+                       if (md_driver->Pause)
+                               md_driver->Pause();
+               }
+       }
+       MUTEX_UNLOCK(vars);
+}
+
+void Voice_SetVolume_internal(SBYTE voice,UWORD vol)
+{
+       ULONG  tmp;
+
+       if((voice<0)||(voice>=md_numchn)) return;
+
+       /* range checks */
+       if(md_musicvolume>128) md_musicvolume=128;
+       if(md_sndfxvolume>128) md_sndfxvolume=128;
+       if(md_volume>128) md_volume=128;
+
+       tmp=(ULONG)vol*(ULONG)md_volume*
+            ((voice<md_sngchn)?(ULONG)md_musicvolume:(ULONG)md_sndfxvolume);
+       md_driver->VoiceSetVolume(voice,tmp/16384UL);
+}
+
+MIKMODAPI void Voice_SetVolume(SBYTE voice,UWORD vol)
+{
+       MUTEX_LOCK(vars);
+       Voice_SetVolume_internal(voice,vol);
+       MUTEX_UNLOCK(vars);
+}
+
+MIKMODAPI UWORD Voice_GetVolume(SBYTE voice)
+{
+       UWORD result=0;
+
+       MUTEX_LOCK(vars);
+       if((voice>=0)&&(voice<md_numchn))
+               result=md_driver->VoiceGetVolume(voice);
+       MUTEX_UNLOCK(vars);
+
+       return result;
+}
+
+void Voice_SetFrequency_internal(SBYTE voice,ULONG frq)
+{
+       if((voice<0)||(voice>=md_numchn)) return;
+       if((md_sample[voice])&&(md_sample[voice]->divfactor))
+               frq/=md_sample[voice]->divfactor;
+       md_driver->VoiceSetFrequency(voice,frq);
+}
+
+MIKMODAPI void Voice_SetFrequency(SBYTE voice,ULONG frq)
+{
+       MUTEX_LOCK(vars);
+       Voice_SetFrequency_internal(voice,frq);
+       MUTEX_UNLOCK(vars);
+}
+
+MIKMODAPI ULONG Voice_GetFrequency(SBYTE voice)
+{
+       ULONG result=0;
+
+       MUTEX_LOCK(vars);
+       if((voice>=0)&&(voice<md_numchn))
+               result=md_driver->VoiceGetFrequency(voice);
+       MUTEX_UNLOCK(vars);
+
+       return result;
+}
+
+void Voice_SetPanning_internal(SBYTE voice,ULONG pan)
+{
+       if((voice<0)||(voice>=md_numchn)) return;
+       if(pan!=PAN_SURROUND) {
+               if(md_pansep>128) md_pansep=128;
+               if(md_mode & DMODE_REVERSE) pan=255-pan;
+               pan = (((SWORD)(pan-128)*md_pansep)/128)+128;
+       }
+       md_driver->VoiceSetPanning(voice, pan);
+}
+
+MIKMODAPI void Voice_SetPanning(SBYTE voice,ULONG pan)
+{
+#ifdef MIKMOD_DEBUG
+       if((pan!=PAN_SURROUND)&&((pan<0)||(pan>255)))
+               fprintf(stderr,"\rVoice_SetPanning called with pan=%ld\n",(long)pan);
+#endif
+
+       MUTEX_LOCK(vars);
+       Voice_SetPanning_internal(voice,pan);
+       MUTEX_UNLOCK(vars);
+}
+
+MIKMODAPI ULONG Voice_GetPanning(SBYTE voice)
+{
+       ULONG result=PAN_CENTER;
+
+       MUTEX_LOCK(vars);
+       if((voice>=0)&&(voice<md_numchn))
+               result=md_driver->VoiceGetPanning(voice);
+       MUTEX_UNLOCK(vars);
+
+       return result;
+}
+
+void Voice_Play_internal(SBYTE voice,SAMPLE* s,ULONG start)
+{
+       ULONG  repend;
+
+       if((voice<0)||(voice>=md_numchn)) return;
+
+       md_sample[voice]=s;
+       repend=s->loopend;
+
+       if(s->flags&SF_LOOP)
+               /* repend can't be bigger than size */
+               if(repend>s->length) repend=s->length;
+
+       md_driver->VoicePlay(voice,s->handle,start,s->length,s->loopstart,repend,s->flags);
+}
+
+MIKMODAPI void Voice_Play(SBYTE voice,SAMPLE* s,ULONG start)
+{
+       if(start>s->length) return;
+
+       MUTEX_LOCK(vars);
+       Voice_Play_internal(voice,s,start);
+       MUTEX_UNLOCK(vars);
+}
+
+void Voice_Stop_internal(SBYTE 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);
+}
+
+MIKMODAPI void Voice_Stop(SBYTE voice)
+{
+       MUTEX_LOCK(vars);
+       Voice_Stop_internal(voice);
+       MUTEX_UNLOCK(vars);
+}
+
+BOOL Voice_Stopped_internal(SBYTE voice)
+{
+       if((voice<0)||(voice>=md_numchn)) return 0;
+       return(md_driver->VoiceStopped(voice));
+}
+
+MIKMODAPI BOOL Voice_Stopped(SBYTE voice)
+{
+       BOOL result;
+
+       MUTEX_LOCK(vars);
+       result=Voice_Stopped_internal(voice);
+       MUTEX_UNLOCK(vars);
+
+       return result;
+}
+
+MIKMODAPI SLONG Voice_GetPosition(SBYTE voice)
+{
+       SLONG result=0;
+
+       MUTEX_LOCK(vars);
+       if((voice>=0)&&(voice<md_numchn)) {
+               if (md_driver->VoiceGetPosition)
+                       result=(md_driver->VoiceGetPosition(voice));
+               else
+                       result=-1;
+       }
+       MUTEX_UNLOCK(vars);
+
+       return result;
+}
+
+MIKMODAPI ULONG Voice_RealVolume(SBYTE voice)
+{
+       ULONG result=0;
+
+       MUTEX_LOCK(vars);
+       if((voice>=0)&&(voice<md_numchn)&& md_driver->VoiceRealVolume)
+               result=(md_driver->VoiceRealVolume(voice));
+       MUTEX_UNLOCK(vars);
+
+       return result;
+}
+
+MIKMODAPI void VC_SetCallback(MikMod_callback_t callback)
+{
+       vc_callback = callback;
+}
+
+static int _mm_init(const CHAR *cmdline)
+{
+       UWORD t;
+
+       _mm_critical = 1;
+
+       /* if md_device==0, try to find a device number */
+       if(!md_device) {
+               cmdline=NULL;
+
+               for(t=1,md_driver=firstdriver;md_driver;md_driver=md_driver->next,t++)
+                       if(md_driver->IsPresent()) break;
+
+               if(!md_driver) {
+                       _mm_errno = MMERR_DETECTING_DEVICE;
+                       if(_mm_errorhandler) _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)&&(t!=md_device);md_driver=md_driver->next)
+                       t++;
+
+               if(!md_driver) {
+                       _mm_errno = MMERR_INVALID_DEVICE;
+                       if(_mm_errorhandler) _mm_errorhandler();
+                       md_driver = &drv_nos;
+                       return 1;
+               }
+
+               /* arguments here might be necessary for the presence check to succeed */
+               if(cmdline&&(md_driver->CommandLine))
+                       md_driver->CommandLine(cmdline);
+
+               if(!md_driver->IsPresent()) {
+                       _mm_errno = MMERR_DETECTING_DEVICE;
+                       if(_mm_errorhandler) _mm_errorhandler();
+                       md_driver = &drv_nos;
+                       return 1;
+               }
+       }
+
+       olddevice = md_device;
+       if(md_driver->Init()) {
+               MikMod_Exit_internal();
+               if(_mm_errorhandler) _mm_errorhandler();
+               return 1;
+       }
+
+       initialized=1;
+       _mm_critical=0;
+
+       return 0;
+}
+
+MIKMODAPI int MikMod_Init(const CHAR *cmdline)
+{
+       int result;
+
+       MUTEX_LOCK(vars);
+       MUTEX_LOCK(lists);
+       result=_mm_init(cmdline);
+       MUTEX_UNLOCK(lists);
+       MUTEX_UNLOCK(vars);
+
+       return result;
+}
+
+void MikMod_Exit_internal(void)
+{
+       MikMod_DisableOutput_internal();
+       md_driver->Exit();
+       md_numchn = md_sfxchn = md_sngchn = 0;
+       md_driver = &drv_nos;
+
+       MikMod_free(sfxinfo);
+       MikMod_free(md_sample);
+       md_sample  = NULL;
+       sfxinfo    = NULL;
+
+       initialized = 0;
+}
+
+MIKMODAPI void MikMod_Exit(void)
+{
+       MUTEX_LOCK(vars);
+       MUTEX_LOCK(lists);
+       MikMod_Exit_internal();
+       MUTEX_UNLOCK(lists);
+       MUTEX_UNLOCK(vars);
+}
+
+/* Reset the driver using the new global variable settings.
+   If the driver has not been initialized, it will be now. */
+static int _mm_reset(const CHAR *cmdline)
+{
+       BOOL wasplaying = 0;
+
+       if(!initialized) return _mm_init(cmdline);
+
+       if (isplaying) {
+               wasplaying = 1;
+               md_driver->PlayStop();
+       }
+
+       if((!md_driver->Reset)||(md_device != olddevice)) {
+               /* md_driver->Reset was NULL, or md_device was changed, so do a full
+                  reset of the driver. */
+               md_driver->Exit();
+               if(_mm_init(cmdline)) {
+                       MikMod_Exit_internal();
+                       if(_mm_errno)
+                               if(_mm_errorhandler) _mm_errorhandler();
+                       return 1;
+               }
+       } else {
+               if(md_driver->Reset()) {
+                       MikMod_Exit_internal();
+                       if(_mm_errno)
+                               if(_mm_errorhandler) _mm_errorhandler();
+                       return 1;
+               }
+       }
+
+       if (wasplaying) return md_driver->PlayStart();
+       return 0;
+}
+
+MIKMODAPI int MikMod_Reset(const CHAR *cmdline)
+{
+       int result;
+
+       MUTEX_LOCK(vars);
+       MUTEX_LOCK(lists);
+       result=_mm_reset(cmdline);
+       MUTEX_UNLOCK(lists);
+       MUTEX_UNLOCK(vars);
+
+       return result;
+}
+
+/* If either parameter is -1, the current set value will be retained. */
+int MikMod_SetNumVoices_internal(int music, int sfx)
+{
+       BOOL resume = 0;
+       int t, oldchn = 0;
+
+       if((!music)&&(!sfx)) return 1;
+       _mm_critical = 1;
+       if(isplaying) {
+               MikMod_DisableOutput_internal();
+               oldchn = md_numchn;
+               resume = 1;
+       }
+
+       MikMod_free(sfxinfo);
+       MikMod_free(md_sample);
+       md_sample  = NULL;
+       sfxinfo    = NULL;
+
+       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_internal();
+               if(_mm_errno)
+                       if(_mm_errorhandler!=NULL) _mm_errorhandler();
+               md_numchn = md_softchn = md_hardchn = md_sfxchn = md_sngchn = 0;
+               return 1;
+       }
+
+       if(md_sngchn+md_sfxchn)
+               md_sample=(SAMPLE**)MikMod_calloc(md_sngchn+md_sfxchn,sizeof(SAMPLE*));
+       if(md_sfxchn)
+               sfxinfo = (UBYTE *)MikMod_calloc(md_sfxchn,sizeof(UBYTE));
+
+       /* make sure the player doesn't start with garbage */
+       for(t=oldchn;t<md_numchn;t++)  Voice_Stop_internal(t);
+
+       sfxpool = 0;
+       if(resume) MikMod_EnableOutput_internal();
+       _mm_critical = 0;
+
+       return 0;
+}
+
+MIKMODAPI int MikMod_SetNumVoices(int music, int sfx)
+{
+       int result;
+
+       MUTEX_LOCK(vars);
+       result=MikMod_SetNumVoices_internal(music,sfx);
+       MUTEX_UNLOCK(vars);
+
+       return result;
+}
+
+int MikMod_EnableOutput_internal(void)
+{
+       _mm_critical = 1;
+       if(!isplaying) {
+               if(md_driver->PlayStart()) return 1;
+               isplaying = 1;
+       }
+       _mm_critical = 0;
+       return 0;
+}
+
+MIKMODAPI int MikMod_EnableOutput(void)
+{
+       int result;
+
+       MUTEX_LOCK(vars);
+       result=MikMod_EnableOutput_internal();
+       MUTEX_UNLOCK(vars);
+
+       return result;
+}
+
+void MikMod_DisableOutput_internal(void)
+{
+       if(isplaying && md_driver) {
+               isplaying = 0;
+               md_driver->PlayStop();
+       }
+}
+
+MIKMODAPI void MikMod_DisableOutput(void)
+{
+       MUTEX_LOCK(vars);
+       MikMod_DisableOutput_internal();
+       MUTEX_UNLOCK(vars);
+}
+
+BOOL MikMod_Active_internal(void)
+{
+       return isplaying;
+}
+
+MIKMODAPI BOOL MikMod_Active(void)
+{
+       BOOL result;
+
+       MUTEX_LOCK(vars);
+       result=MikMod_Active_internal();
+       MUTEX_UNLOCK(vars);
+
+       return result;
+}
+
+/* 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. */
+static SBYTE Sample_Play_internal(SAMPLE *s,ULONG start,UBYTE flags)
+{
+       int orig=sfxpool;/* for cases where all channels are critical */
+       int c;
+
+       if(!md_sfxchn) 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_internal(c,s,start);
+                               md_driver->VoiceSetVolume(c,s->volume<<2);
+                               Voice_SetPanning_internal(c,s->panning);
+                               md_driver->VoiceSetFrequency(c,s->speed);
+                               sfxpool++;
+                               if(sfxpool>=md_sfxchn) sfxpool=0;
+                               return c;
+                       }
+               } else {
+                       sfxinfo[sfxpool]=flags;
+                       Voice_Play_internal(c=sfxpool+md_sngchn,s,start);
+                       md_driver->VoiceSetVolume(c,s->volume<<2);
+                       Voice_SetPanning_internal(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;
+}
+
+MIKMODAPI SBYTE Sample_Play(SAMPLE *s,ULONG start,UBYTE flags)
+{
+       SBYTE result;
+
+       MUTEX_LOCK(vars);
+       result=Sample_Play_internal(s,start,flags);
+       MUTEX_UNLOCK(vars);
+
+       return result;
+}
+
+MIKMODAPI long MikMod_GetVersion(void)
+{
+       return LIBMIKMOD_VERSION;
+}
+
+/*========== MT-safe stuff */
+
+#ifdef HAVE_PTHREAD
+#define INIT_MUTEX(name) \
+       pthread_mutex_t _mm_mutex_##name=PTHREAD_MUTEX_INITIALIZER
+
+#elif defined(__OS2__)||defined(__EMX__)
+#define INIT_MUTEX(name) \
+       HMTX _mm_mutex_##name
+
+#elif defined(_WIN32)
+#define INIT_MUTEX(name) \
+       HANDLE _mm_mutex_##name
+
+#else
+#define INIT_MUTEX(name) \
+       void *_mm_mutex_##name = NULL
+#endif
+
+INIT_MUTEX(vars);
+INIT_MUTEX(lists);
+
+MIKMODAPI BOOL MikMod_InitThreads(void)
+{
+       static int firstcall=1;
+       static BOOL result = 0;
+
+       if (firstcall) {
+               firstcall=0;
+#ifdef HAVE_PTHREAD
+               result=1;
+#elif defined(__OS2__)||defined(__EMX__)
+               if(DosCreateMutexSem((PSZ)NULL,&_mm_mutex_lists,0,0) ||
+                  DosCreateMutexSem((PSZ)NULL,&_mm_mutex_vars,0,0)) {
+                       _mm_mutex_lists=_mm_mutex_vars=(HMTX)NULL;
+                       result=0;
+               } else
+                       result=1;
+#elif defined(_WIN32)
+               if((!(_mm_mutex_lists=CreateMutex(NULL,FALSE,TEXT("libmikmod(lists)"))))||
+                  (!(_mm_mutex_vars=CreateMutex(NULL,FALSE,TEXT("libmikmod(vars)")))))
+                       result=0;
+               else
+                       result=1;
+#endif
+       }
+       return result;
+}
+
+MIKMODAPI void MikMod_Unlock(void)
+{
+       MUTEX_UNLOCK(lists);
+       MUTEX_UNLOCK(vars);
+}
+
+MIKMODAPI void MikMod_Lock(void)
+{
+       MUTEX_LOCK(vars);
+       MUTEX_LOCK(lists);
+}
+
+/*========== Parameter extraction helper */
+
+CHAR *MD_GetAtom(const CHAR *atomname, const CHAR *cmdline, BOOL implicit)
+{
+       CHAR *ret=NULL;
+
+       if(cmdline) {
+               const CHAR *buf=strstr(cmdline,atomname);
+
+               if((buf)&&((buf==cmdline)||(*(buf-1)==','))) {
+                       const CHAR *ptr=buf+strlen(atomname);
+
+                       if(*ptr=='=') {
+                               for(buf=++ptr;(*ptr)&&((*ptr)!=',');ptr++);
+                               ret=(CHAR *)MikMod_malloc((1+ptr-buf)*sizeof(CHAR));
+                               if(ret)
+                                       strncpy(ret,buf,ptr-buf);
+                       } else if((*ptr==',')||(!*ptr)) {
+                               if(implicit) {
+                                       ret=(CHAR *)MikMod_malloc((1+ptr-buf)*sizeof(CHAR));
+                                       if(ret)
+                                               strncpy(ret,buf,ptr-buf);
+                               }
+                       }
+               }
+       }
+       return ret;
+}
+
+#if (MIKMOD_UNIX)
+
+/*========== Posix helper functions */
+
+/* Check if the file is a regular or nonexistant file (or a link to a such a
+   file), and that, should the calling program be setuid, the access rights are
+   reasonable. Returns 1 if it is safe to rewrite the file, 0 otherwise.
+   The goal is to prevent a setuid root libmikmod application from overriding
+   files like /etc/passwd with digital sound... */
+BOOL MD_Access(const CHAR * filename)
+{
+       struct stat buf;
+
+       if(!stat(filename,&buf)) {
+               /* not a regular file ? */
+               if(!S_ISREG(buf.st_mode)) return 0;
+               /* more than one hard link to the file ? */
+               if(buf.st_nlink>1) return 0;
+               /* check access rights with the real user and group id */
+               if(getuid()==buf.st_uid) {
+                       if(!(buf.st_mode&S_IWUSR)) return 0;
+               } else if(getgid()==buf.st_gid) {
+                       if(!(buf.st_mode&S_IWGRP)) return 0;
+               } else
+                       if(!(buf.st_mode&S_IWOTH)) return 0;
+       }
+
+       return 1;
+}
+
+/* Drop all root privileges we might have */
+int MD_DropPrivileges(void)
+{
+       if(!geteuid()) {
+               if(getuid()) {
+                       /* we are setuid root -> drop setuid to become the real user */
+                       if(setuid(getuid())) return 1;
+               } else {
+                       /* we are run as root -> drop all and become user 'nobody' */
+                       struct passwd *nobody;
+                       int uid;
+
+                       if(!(nobody=getpwnam("nobody"))) return 1; /* no such user ? */
+                       uid=nobody->pw_uid;
+                       if (!uid) /* user 'nobody' has root privileges ? weird... */
+                               return 1;
+                       if (setuid(uid)) return 1;
+               }
+       }
+       return 0;
+}
+
+#endif
+
+/* ex:set ts=8: */