Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / playercode / mdriver.c
diff --git a/apps/plugins/mikmod/playercode/mdriver.c b/apps/plugins/mikmod/playercode/mdriver.c
new file mode 100644 (file)
index 0000000..247423a
--- /dev/null
@@ -0,0 +1,961 @@
+/*     MikMod sound library\r
+       (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS\r
+       for complete list.\r
+\r
+       This library is free software; you can redistribute it and/or modify\r
+       it under the terms of the GNU Library General Public License as\r
+       published by the Free Software Foundation; either version 2 of\r
+       the License, or (at your option) any later version.\r
\r
+       This program is distributed in the hope that it will be useful,\r
+       but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+       GNU Library General Public License for more details.\r
\r
+       You should have received a copy of the GNU Library General Public\r
+       License along with this library; if not, write to the Free Software\r
+       Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\r
+       02111-1307, USA.\r
+*/\r
+\r
+/*==============================================================================\r
+\r
+  $Id: mdriver.c,v 1.3 2004/02/18 13:29:19 raph Exp $\r
+\r
+  These routines are used to access the available soundcard drivers.\r
+\r
+==============================================================================*/\r
+\r
+#ifdef HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif\r
+\r
+#ifdef HAVE_UNISTD_H\r
+#include <unistd.h>\r
+#endif\r
+\r
+#if defined unix || (defined __APPLE__ && defined __MACH__)\r
+#include <pwd.h>\r
+#include <sys/stat.h>\r
+#endif\r
+\r
+#include <string.h>\r
+#ifdef HAVE_STRINGS_H\r
+#include <strings.h>\r
+#endif\r
+\r
+#include "mikmod_internals.h"\r
+\r
+#ifdef SUNOS\r
+extern int fprintf(FILE *, const char *, ...);\r
+#endif\r
+\r
+static MDRIVER *firstdriver=NULL;\r
+\r
+extern MODULE *pf; /* modfile being played */\r
+\r
+/* Initial global settings */\r
+MIKMODAPI      UWORD md_device         = 0;    /* autodetect */\r
+MIKMODAPI      UWORD md_mixfreq        = 44100;\r
+MIKMODAPI      UWORD md_mode           = DMODE_STEREO | DMODE_16BITS |\r
+                                                                         DMODE_SURROUND |DMODE_SOFT_MUSIC |\r
+                                                                         DMODE_SOFT_SNDFX;\r
+MIKMODAPI      UBYTE md_pansep         = 128;  /* 128 == 100% (full left/right) */\r
+MIKMODAPI      UBYTE md_reverb         = 0;    /* no reverb */\r
+MIKMODAPI      UBYTE md_volume         = 128;  /* global sound volume (0-128) */\r
+MIKMODAPI      UBYTE md_musicvolume    = 128;  /* volume of song */\r
+MIKMODAPI      UBYTE md_sndfxvolume    = 128;  /* volume of sound effects */\r
+                       UWORD md_bpm            = 125;  /* tempo */\r
+\r
+/* Do not modify the numchn variables yourself!  use MD_SetVoices() */\r
+                       UBYTE md_numchn=0,md_sngchn=0,md_sfxchn=0;\r
+                       UBYTE md_hardchn=0,md_softchn=0;\r
+\r
+                       void (*md_player)(void) = Player_HandleTick;\r
+static         BOOL  isplaying=0, initialized = 0;\r
+static         UBYTE *sfxinfo;\r
+static         int sfxpool;\r
+\r
+static         SAMPLE **md_sample = NULL;\r
+\r
+/* Previous driver in use *\r
+static         SWORD olddevice = -1;\r
+*/\r
+/* Limits the number of hardware voices to the specified amount.\r
+   This function should only be used by the low-level drivers. */\r
+static void LimitHardVoices(int limit)\r
+{\r
+       int t=0;\r
+\r
+       if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>limit)) md_sfxchn=limit;\r
+       if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>limit)) md_sngchn=limit;\r
+\r
+       if (!(md_mode & DMODE_SOFT_SNDFX))\r
+               md_hardchn=md_sfxchn;\r
+       else\r
+               md_hardchn=0;\r
+\r
+       if (!(md_mode & DMODE_SOFT_MUSIC)) md_hardchn += md_sngchn;\r
+\r
+       while (md_hardchn>limit) {\r
+               if (++t & 1) {\r
+                       if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>4)) md_sfxchn--;\r
+               } else {\r
+                       if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>8)) md_sngchn--;\r
+               }\r
+\r
+               if (!(md_mode & DMODE_SOFT_SNDFX))\r
+                       md_hardchn=md_sfxchn;\r
+               else\r
+                       md_hardchn=0;\r
+\r
+               if (!(md_mode & DMODE_SOFT_MUSIC))\r
+                       md_hardchn+=md_sngchn;\r
+       }\r
+       md_numchn=md_hardchn+md_softchn;\r
+}\r
+\r
+/* Limits the number of hardware voices to the specified amount.\r
+   This function should only be used by the low-level drivers. */\r
+static void LimitSoftVoices(int limit)\r
+{\r
+       int t=0;\r
+\r
+       if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>limit)) md_sfxchn=limit;\r
+       if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>limit)) md_sngchn=limit;\r
+\r
+       if (md_mode & DMODE_SOFT_SNDFX)\r
+               md_softchn=md_sfxchn;\r
+       else\r
+               md_softchn=0;\r
+\r
+       if (md_mode & DMODE_SOFT_MUSIC) md_softchn+=md_sngchn;\r
+\r
+       while (md_softchn>limit) {\r
+               if (++t & 1) {\r
+                       if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>4)) md_sfxchn--;\r
+               } else {\r
+                       if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>8)) md_sngchn--;\r
+               }\r
+\r
+               if (!(md_mode & DMODE_SOFT_SNDFX))\r
+                       md_softchn=md_sfxchn;\r
+               else\r
+                       md_softchn=0;\r
+\r
+               if (!(md_mode & DMODE_SOFT_MUSIC))\r
+                       md_softchn+=md_sngchn;\r
+       }\r
+       md_numchn=md_hardchn+md_softchn;\r
+}\r
+\r
+/* Note: 'type' indicates whether the returned value should be for music or for\r
+   sound effects. */\r
+ULONG MD_SampleSpace(int type)\r
+{\r
+       if(type==MD_MUSIC)\r
+               type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE;\r
+       else if(type==MD_SNDFX)\r
+               type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE;\r
+\r
+       return VC_SampleSpace(type);\r
+}\r
+\r
+ULONG MD_SampleLength(int type,SAMPLE* s)\r
+{\r
+       if(type==MD_MUSIC)\r
+               type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE;\r
+       else\r
+         if(type==MD_SNDFX)\r
+               type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE;\r
+\r
+       return VC_SampleLength(type,s);\r
+}\r
+/*\r
+MIKMODAPI CHAR* MikMod_InfoDriver(void)\r
+{\r
+       int t,len=0;\r
+       MDRIVER *l;\r
+       CHAR *list=NULL;\r
+\r
+       MUTEX_LOCK(lists);\r
+       /* compute size of buffer *\r
+       for(l=firstdriver;l;l=l->next)\r
+               len+=4+(l->next?1:0)+strlen(l->Version);\r
+\r
+       if(len)\r
+               if((list=_mm_malloc(len*sizeof(CHAR)))) {\r
+                       list[0]=0;\r
+                       /* list all registered device drivers : *\r
+                       for(t=1,l=firstdriver;l;l=l->next,t++)\r
+                               sprintf(list,(l->next)?"%s%2d %s\n":"%s%2d %s",\r
+                                   list,t,l->Version);\r
+               }\r
+       MUTEX_UNLOCK(lists);\r
+       return list;\r
+}\r
+*/\r
+void _mm_registerdriver(struct MDRIVER* drv)\r
+{\r
+       MDRIVER *cruise = firstdriver;\r
+\r
+       /* don't register a MISSING() driver */\r
+       if ((drv->Name) && (drv->Version)) {\r
+               if (cruise) {\r
+                       while (cruise->next) cruise = cruise->next;\r
+                       cruise->next = drv;\r
+               } else\r
+                       firstdriver = drv; \r
+       }\r
+}\r
+\r
+MIKMODAPI void MikMod_RegisterDriver(struct MDRIVER* drv)\r
+{\r
+       /* if we try to register an invalid driver, or an already registered driver,\r
+          ignore this attempt */\r
+       if ((!drv)||(drv->next)||(!drv->Name))\r
+               return;\r
+\r
+       MUTEX_LOCK(lists);\r
+       _mm_registerdriver(drv);\r
+       MUTEX_UNLOCK(lists);\r
+}\r
+\r
+MIKMODAPI int MikMod_DriverFromAlias(CHAR *alias)\r
+{\r
+       int rank=1;\r
+       MDRIVER *cruise;\r
+\r
+       MUTEX_LOCK(lists);\r
+       cruise=firstdriver;\r
+       while(cruise) {\r
+               if (cruise->Alias) {\r
+                       if (!(strcasecmp(alias,cruise->Alias))) break;\r
+                       rank++;\r
+               }\r
+               cruise=cruise->next;\r
+       }\r
+       if(!cruise) rank=0;\r
+       MUTEX_UNLOCK(lists);\r
+\r
+       return rank;\r
+}\r
+\r
+MIKMODAPI MDRIVER *MikMod_DriverByOrdinal(int ordinal)\r
+{\r
+        MDRIVER *cruise;\r
+\r
+        /* Allow only driver ordinals > 0 */\r
+        if (!ordinal)\r
+                return 0;\r
+\r
+        MUTEX_LOCK(lists);\r
+        cruise = firstdriver;\r
+        while (cruise && --ordinal)\r
+                cruise = cruise->next;\r
+        MUTEX_UNLOCK(lists);\r
+        return cruise;\r
+}\r
+\r
+SWORD MD_SampleLoad(SAMPLOAD* s, int type)\r
+{\r
+       SWORD result;\r
+\r
+       if(type==MD_MUSIC)\r
+               type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE;\r
+       else if(type==MD_SNDFX)\r
+               type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE;\r
+\r
+       SL_Init(s);\r
+       result=VC_SampleLoad(s,type);\r
+       SL_Exit(s);\r
+\r
+       return result;\r
+}\r
+\r
+void MD_SampleUnload(SWORD handle)\r
+{\r
+       VC_SampleUnload(handle);\r
+}\r
+\r
+MIKMODAPI MikMod_player_t MikMod_RegisterPlayer(MikMod_player_t player)\r
+{\r
+       MikMod_player_t result;\r
+\r
+       MUTEX_LOCK(vars);\r
+       result=md_player;\r
+       md_player=player;\r
+       MUTEX_UNLOCK(vars);\r
+\r
+       return result;\r
+}\r
+\r
+MIKMODAPI void MikMod_Update(void)\r
+{\r
+       MUTEX_LOCK(vars);\r
+       /* FIXME - if I'm broken...\r
+       if(isplaying)\r
+       {\r
+               if((!pf)||(!pf->forbid))\r
+               {\r
+                       md_driver->Update();\r
+               }\r
+               else\r
+               {\r
+                       if (md_driver->Pause)\r
+                               md_driver->Pause();\r
+               }\r
+       }\r
+       */\r
+       MUTEX_UNLOCK(vars);\r
+}\r
+\r
+void Voice_SetVolume_internal(SBYTE voice,UWORD vol)\r
+{\r
+       ULONG  tmp;\r
+\r
+       if((voice<0)||(voice>=md_numchn)) return;\r
+\r
+       /* range checks */\r
+       if(md_musicvolume>128) md_musicvolume=128;\r
+       if(md_sndfxvolume>128) md_sndfxvolume=128;\r
+       if(md_volume>128) md_volume=128;\r
+\r
+       tmp=(ULONG)vol*(ULONG)md_volume*\r
+            ((voice<md_sngchn)?(ULONG)md_musicvolume:(ULONG)md_sndfxvolume);\r
+       VC_VoiceSetVolume(voice,tmp/16384UL);\r
+}\r
+\r
+MIKMODAPI void Voice_SetVolume(SBYTE voice,UWORD vol)\r
+{\r
+       MUTEX_LOCK(vars);\r
+       Voice_SetVolume_internal(voice,vol);\r
+       MUTEX_UNLOCK(vars);\r
+}\r
+\r
+MIKMODAPI UWORD Voice_GetVolume(SBYTE voice)\r
+{\r
+       UWORD result=0;\r
+\r
+       MUTEX_LOCK(vars);\r
+       if((voice>=0)&&(voice<md_numchn))\r
+               result=VC_VoiceGetVolume(voice);\r
+       MUTEX_UNLOCK(vars);\r
+\r
+       return result;\r
+}\r
+\r
+void Voice_SetFrequency_internal(SBYTE voice,ULONG frq)\r
+{\r
+       if((voice<0)||(voice>=md_numchn)) return;\r
+       if((md_sample[voice])&&(md_sample[voice]->divfactor))\r
+               frq/=md_sample[voice]->divfactor;\r
+       VC_VoiceSetFrequency(voice,frq);\r
+}\r
+\r
+MIKMODAPI void Voice_SetFrequency(SBYTE voice,ULONG frq)\r
+{\r
+       MUTEX_LOCK(vars);\r
+       Voice_SetFrequency_internal(voice,frq);\r
+       MUTEX_UNLOCK(vars);\r
+}\r
+\r
+MIKMODAPI ULONG Voice_GetFrequency(SBYTE voice)\r
+{\r
+       ULONG result=0;\r
+\r
+       MUTEX_LOCK(vars);\r
+       if((voice>=0)&&(voice<md_numchn))\r
+               result=VC_VoiceGetFrequency(voice);\r
+       MUTEX_UNLOCK(vars);\r
+\r
+       return result;\r
+}\r
+\r
+void Voice_SetPanning_internal(SBYTE voice,ULONG pan)\r
+{\r
+       if((voice<0)||(voice>=md_numchn)) return;\r
+       if(pan!=PAN_SURROUND) {\r
+               if(md_pansep>128) md_pansep=128;\r
+               if(md_mode & DMODE_REVERSE) pan=255-pan;\r
+               pan = (((SWORD)(pan-128)*md_pansep)/128)+128;\r
+       }\r
+       VC_VoiceSetPanning(voice, pan);\r
+}\r
+\r
+MIKMODAPI void Voice_SetPanning(SBYTE voice,ULONG pan)\r
+{\r
+#ifdef MIKMOD_DEBUG\r
+       if((pan!=PAN_SURROUND)&&((pan<0)||(pan>255)))\r
+               fprintf(stderr,"\rVoice_SetPanning called with pan=%ld\n",(long)pan);\r
+#endif\r
+\r
+       MUTEX_LOCK(vars);\r
+       Voice_SetPanning_internal(voice,pan);\r
+       MUTEX_UNLOCK(vars);\r
+}\r
+\r
+MIKMODAPI ULONG Voice_GetPanning(SBYTE voice)\r
+{\r
+       ULONG result=PAN_CENTER;\r
+\r
+       MUTEX_LOCK(vars);\r
+       if((voice>=0)&&(voice<md_numchn))\r
+               result=VC_VoiceGetPanning(voice);\r
+       MUTEX_UNLOCK(vars);\r
+\r
+       return result;\r
+}\r
+\r
+void Voice_Play_internal(SBYTE voice,SAMPLE* s,ULONG start)\r
+{\r
+       ULONG  repend;\r
+\r
+       if((voice<0)||(voice>=md_numchn)) return;\r
+\r
+       md_sample[voice]=s;\r
+       repend=s->loopend;\r
+\r
+       if(s->flags&SF_LOOP)\r
+               /* repend can't be bigger than size */\r
+               if(repend>s->length) repend=s->length;\r
+\r
+       VC_VoicePlay(voice,s->handle,start,s->length,s->loopstart,repend,s->flags);\r
+}\r
+\r
+MIKMODAPI void Voice_Play(SBYTE voice,SAMPLE* s,ULONG start)\r
+{\r
+       if(start>s->length) return;\r
+\r
+       MUTEX_LOCK(vars);\r
+       Voice_Play_internal(voice,s,start);\r
+       MUTEX_UNLOCK(vars);\r
+}\r
+\r
+void Voice_Stop_internal(SBYTE voice)\r
+{\r
+       if((voice<0)||(voice>=md_numchn)) return;\r
+       if(voice>=md_sngchn)\r
+               /* It is a sound effects channel, so flag the voice as non-critical! */\r
+               sfxinfo[voice-md_sngchn]=0;\r
+       VC_VoiceStop(voice);\r
+}\r
+\r
+MIKMODAPI void Voice_Stop(SBYTE voice)\r
+{\r
+       MUTEX_LOCK(vars);\r
+       Voice_Stop_internal(voice);\r
+       MUTEX_UNLOCK(vars);\r
+}\r
+\r
+BOOL Voice_Stopped_internal(SBYTE voice)\r
+{\r
+       if((voice<0)||(voice>=md_numchn)) return 0;\r
+       return(VC_VoiceStopped(voice));\r
+}\r
+\r
+MIKMODAPI BOOL Voice_Stopped(SBYTE voice)\r
+{\r
+       BOOL result;\r
+\r
+       MUTEX_LOCK(vars);\r
+       result=Voice_Stopped_internal(voice);\r
+       MUTEX_UNLOCK(vars);\r
+\r
+       return result;\r
+}\r
+\r
+MIKMODAPI SLONG Voice_GetPosition(SBYTE voice)\r
+{\r
+       SLONG result=0;\r
+\r
+       MUTEX_LOCK(vars);\r
+       if((voice>=0)&&(voice<md_numchn)) {\r
+               /*if (VC_VoiceGetPosition)*/\r
+                       result=(VC_VoiceGetPosition(voice));\r
+               /*else\r
+                       result=-1;*/\r
+       }\r
+       MUTEX_UNLOCK(vars);\r
+\r
+       return result;\r
+}\r
+\r
+MIKMODAPI ULONG Voice_RealVolume(SBYTE voice)\r
+{\r
+       ULONG result=0;\r
+\r
+       MUTEX_LOCK(vars);\r
+       if((voice>=0)&&(voice<md_numchn)/*&& VC_VoiceRealVolume*/)\r
+               result=(VC_VoiceRealVolume(voice));\r
+       MUTEX_UNLOCK(vars);\r
+\r
+       return result;\r
+}\r
+\r
+static BOOL _mm_init(CHAR *cmdline)\r
+{\r
+       /* UWORD t; */\r
+       (void)cmdline;\r
+\r
+       _mm_critical = 1;\r
+/*\r
+       /* if md_device==0, try to find a device number *\r
+       if(!md_device) {\r
+               cmdline=NULL;\r
+\r
+               for(t=1,md_driver=firstdriver;md_driver;md_driver=md_driver->next,t++)\r
+                       if(md_driver->IsPresent()) break;\r
+\r
+               if(!md_driver) {\r
+                       _mm_errno = MMERR_DETECTING_DEVICE;\r
+                       if(_mm_errorhandler) _mm_errorhandler();\r
+                       md_driver = &drv_nos;\r
+                       return 1;\r
+               }\r
+\r
+               md_device = t;\r
+       } else {\r
+               /* if n>0, use that driver *\r
+               for(t=1,md_driver=firstdriver;(md_driver)&&(t!=md_device);md_driver=md_driver->next)\r
+                       t++;\r
+\r
+               if(!md_driver) {\r
+                       _mm_errno = MMERR_INVALID_DEVICE;\r
+                       if(_mm_errorhandler) _mm_errorhandler();\r
+                       md_driver = &drv_nos;\r
+                       return 1;\r
+               }\r
+\r
+               /* arguments here might be necessary for the presence check to succeed *\r
+               if(cmdline&&(md_driver->CommandLine))\r
+                       md_driver->CommandLine(cmdline);\r
+\r
+               if(!md_driver->IsPresent()) {\r
+                       _mm_errno = MMERR_DETECTING_DEVICE;\r
+                       if(_mm_errorhandler) _mm_errorhandler();\r
+                       md_driver = &drv_nos;\r
+                       return 1;\r
+               }\r
+       }\r
+\r
+       olddevice = md_device;\r
+*/\r
+       md_mode |= DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX;\r
+\r
+       if(VC_Init()) {\r
+               MikMod_Exit_internal();\r
+               if(_mm_errorhandler) _mm_errorhandler();\r
+               return 1;\r
+       }\r
+\r
+       initialized=1;\r
+       _mm_critical=0;\r
+\r
+       return 0;\r
+}\r
+\r
+MIKMODAPI BOOL MikMod_Init(CHAR *cmdline)\r
+{\r
+       BOOL result;\r
+\r
+       MUTEX_LOCK(vars);\r
+       MUTEX_LOCK(lists);\r
+       result=_mm_init(cmdline);\r
+       MUTEX_UNLOCK(lists);\r
+       MUTEX_UNLOCK(vars);\r
+\r
+       return result;\r
+}\r
+\r
+void MikMod_Exit_internal(void)\r
+{\r
+       MikMod_DisableOutput_internal();\r
+       VC_Exit();\r
+       md_numchn = md_sfxchn = md_sngchn = 0;\r
+       //md_driver = &drv_nos;\r
+\r
+       if(sfxinfo) free(sfxinfo);\r
+       if(md_sample) free(md_sample);\r
+       md_sample  = NULL;\r
+       sfxinfo    = NULL;\r
+\r
+       initialized = 0;\r
+}\r
+\r
+MIKMODAPI void MikMod_Exit(void)\r
+{\r
+       MUTEX_LOCK(vars);\r
+       MUTEX_LOCK(lists);\r
+       MikMod_Exit_internal();\r
+       MUTEX_UNLOCK(lists);\r
+       MUTEX_UNLOCK(vars);\r
+}\r
+\r
+/* Reset the driver using the new global variable settings. \r
+   If the driver has not been initialized, it will be now. */\r
+static BOOL _mm_reset(CHAR *cmdline)\r
+{\r
+       BOOL wasplaying = 0;\r
+\r
+       if(!initialized) return _mm_init(cmdline);\r
+       \r
+       if (isplaying) {\r
+               wasplaying = 1;\r
+               VC_PlayStop();\r
+       }\r
+/*\r
+       if((!md_driver->Reset)||(md_device != olddevice)) {\r
+               /* md_driver->Reset was NULL, or md_device was changed, so do a full\r
+                  reset of the driver. *\r
+               md_driver->Exit();\r
+               if(_mm_init(cmdline)) {\r
+                       MikMod_Exit_internal();\r
+                       if(_mm_errno)\r
+                               if(_mm_errorhandler) _mm_errorhandler();\r
+                       return 1;\r
+               }\r
+       } else {\r
+               if(md_driver->Reset()) {\r
+                       MikMod_Exit_internal();\r
+                       if(_mm_errno)\r
+                               if(_mm_errorhandler) _mm_errorhandler();\r
+                       return 1;\r
+               }\r
+       }\r
+*/\r
+       if (wasplaying) VC_PlayStart();\r
+       return 0;\r
+}\r
+\r
+MIKMODAPI BOOL MikMod_Reset(CHAR *cmdline)\r
+{\r
+       BOOL result;\r
+\r
+       MUTEX_LOCK(vars);\r
+       MUTEX_LOCK(lists);\r
+       result=_mm_reset(cmdline);\r
+       MUTEX_UNLOCK(lists);\r
+       MUTEX_UNLOCK(vars);\r
+\r
+       return result;\r
+}\r
+\r
+/* If either parameter is -1, the current set value will be retained. */\r
+BOOL MikMod_SetNumVoices_internal(int music, int sfx)\r
+{\r
+       BOOL resume = 0;\r
+       int t, oldchn = 0;\r
+\r
+       if((!music)&&(!sfx)) return 1;\r
+       _mm_critical = 1;\r
+       if(isplaying) {\r
+               MikMod_DisableOutput_internal();\r
+               oldchn = md_numchn;\r
+               resume = 1;\r
+       }\r
+\r
+       if(sfxinfo) free(sfxinfo);\r
+       if(md_sample) free(md_sample);\r
+       md_sample  = NULL;\r
+       sfxinfo    = NULL;\r
+\r
+       if(music!=-1) md_sngchn = music;\r
+       if(sfx!=-1)   md_sfxchn = sfx;\r
+       md_numchn = md_sngchn + md_sfxchn;\r
+/*\r
+       LimitHardVoices(md_driver->HardVoiceLimit);\r
+       LimitSoftVoices(md_driver->SoftVoiceLimit);\r
+*/\r
+       LimitHardVoices(0);\r
+       LimitSoftVoices(255);\r
+\r
+       if(VC_SetNumVoices()) {\r
+               MikMod_Exit_internal();\r
+               if(_mm_errno)\r
+                       if(_mm_errorhandler!=NULL) _mm_errorhandler();\r
+               md_numchn = md_softchn = md_hardchn = md_sfxchn = md_sngchn = 0;\r
+               return 1;\r
+       }\r
+\r
+       if(md_sngchn+md_sfxchn)\r
+               md_sample=(SAMPLE**)_mm_calloc(md_sngchn+md_sfxchn,sizeof(SAMPLE*));\r
+       if(md_sfxchn)\r
+               sfxinfo = (UBYTE *)_mm_calloc(md_sfxchn,sizeof(UBYTE));\r
+\r
+       /* make sure the player doesn't start with garbage */\r
+       for(t=oldchn;t<md_numchn;t++)  Voice_Stop_internal(t);\r
+\r
+       sfxpool = 0;\r
+       if(resume) MikMod_EnableOutput_internal();\r
+       _mm_critical = 0;\r
+\r
+       return 0;\r
+}\r
+\r
+MIKMODAPI BOOL MikMod_SetNumVoices(int music, int sfx)\r
+{\r
+       BOOL result;\r
+\r
+       MUTEX_LOCK(vars);\r
+       result=MikMod_SetNumVoices_internal(music,sfx);\r
+       MUTEX_UNLOCK(vars);\r
+\r
+       return result;\r
+}\r
+\r
+BOOL MikMod_EnableOutput_internal(void)\r
+{\r
+       _mm_critical = 1;\r
+       if(!isplaying) {\r
+               if(VC_PlayStart()) return 1;\r
+               isplaying = 1;\r
+       }\r
+       _mm_critical = 0;\r
+       return 0;\r
+}\r
+\r
+MIKMODAPI BOOL MikMod_EnableOutput(void)\r
+{\r
+       BOOL result;\r
+\r
+       MUTEX_LOCK(vars);\r
+       result=MikMod_EnableOutput_internal();\r
+       MUTEX_UNLOCK(vars);\r
+\r
+       return result;\r
+}\r
+\r
+void MikMod_DisableOutput_internal(void)\r
+{\r
+       if(isplaying/* && md_driver*/) {\r
+               isplaying = 0;\r
+               VC_PlayStop();\r
+       }\r
+}\r
+\r
+MIKMODAPI void MikMod_DisableOutput(void)\r
+{\r
+       MUTEX_LOCK(vars);\r
+       MikMod_DisableOutput_internal();\r
+       MUTEX_UNLOCK(vars);\r
+}\r
+\r
+BOOL MikMod_Active_internal(void)\r
+{\r
+       return isplaying;\r
+}\r
+\r
+MIKMODAPI BOOL MikMod_Active(void)\r
+{\r
+       BOOL result;\r
+\r
+       MUTEX_LOCK(vars);\r
+       result=MikMod_Active_internal();\r
+       MUTEX_UNLOCK(vars);\r
+\r
+       return result;\r
+}\r
+\r
+/* Plays a sound effects sample.  Picks a voice from the number of voices\r
+   allocated for use as sound effects (loops through voices, skipping all active\r
+   criticals).\r
+\r
+   Returns the voice that the sound is being played on.                       */\r
+SBYTE Sample_Play_internal(SAMPLE *s,ULONG start,UBYTE flags)\r
+{\r
+       int orig=sfxpool;/* for cases where all channels are critical */\r
+       int c;\r
+\r
+       if(!md_sfxchn) return -1;\r
+       if(s->volume>64) s->volume = 64;\r
+\r
+       /* check the first location after sfxpool */\r
+       do {\r
+               if(sfxinfo[sfxpool]&SFX_CRITICAL) {\r
+                       if(VC_VoiceStopped(c=sfxpool+md_sngchn)) {\r
+                               sfxinfo[sfxpool]=flags;\r
+                               Voice_Play_internal(c,s,start);\r
+                               VC_VoiceSetVolume(c,s->volume<<2);\r
+                               Voice_SetPanning_internal(c,s->panning);\r
+                               VC_VoiceSetFrequency(c,s->speed);\r
+                               sfxpool++;\r
+                               if(sfxpool>=md_sfxchn) sfxpool=0;\r
+                               return c;\r
+                       }\r
+               } else {\r
+                       sfxinfo[sfxpool]=flags;\r
+                       Voice_Play_internal(c=sfxpool+md_sngchn,s,start);\r
+                       VC_VoiceSetVolume(c,s->volume<<2);\r
+                       Voice_SetPanning_internal(c,s->panning);\r
+                       VC_VoiceSetFrequency(c,s->speed);\r
+                       sfxpool++;\r
+                       if(sfxpool>=md_sfxchn) sfxpool=0;\r
+                       return c;\r
+               }\r
+\r
+               sfxpool++;\r
+               if(sfxpool>=md_sfxchn) sfxpool = 0;\r
+       } while(sfxpool!=orig);\r
+\r
+       return -1;\r
+}\r
+\r
+MIKMODAPI SBYTE Sample_Play(SAMPLE *s,ULONG start,UBYTE flags)\r
+{\r
+       SBYTE result;\r
+\r
+       MUTEX_LOCK(vars);\r
+       result=Sample_Play_internal(s,start,flags);\r
+       MUTEX_UNLOCK(vars);\r
+\r
+       return result;\r
+}\r
+\r
+MIKMODAPI long MikMod_GetVersion(void)\r
+{\r
+       return LIBMIKMOD_VERSION;\r
+}\r
+\r
+/*========== MT-safe stuff */\r
+\r
+#ifdef HAVE_PTHREAD\r
+#define INIT_MUTEX(name) \\r
+       pthread_mutex_t _mm_mutex_##name=PTHREAD_MUTEX_INITIALIZER\r
+#elif defined(__OS2__)||defined(__EMX__)\r
+#define INIT_MUTEX(name) \\r
+       HMTX _mm_mutex_##name\r
+#elif defined(WIN32)\r
+#define INIT_MUTEX(name) \\r
+       HANDLE _mm_mutex_##name\r
+#else\r
+#define INIT_MUTEX(name) \\r
+       void *_mm_mutex_##name = NULL\r
+#endif\r
+\r
+INIT_MUTEX(vars);\r
+INIT_MUTEX(lists);\r
+\r
+MIKMODAPI BOOL MikMod_InitThreads(void)\r
+{\r
+       static int firstcall=1;\r
+       static int result=0;\r
+       \r
+       if (firstcall) {\r
+               firstcall=0;\r
+#ifdef HAVE_PTHREAD\r
+               result=1;\r
+#elif defined(__OS2__)||defined(__EMX__)\r
+               if(DosCreateMutexSem((PSZ)NULL,&_mm_mutex_lists,0,0) ||\r
+                  DosCreateMutexSem((PSZ)NULL,&_mm_mutex_vars,0,0)) {\r
+                       _mm_mutex_lists=_mm_mutex_vars=(HMTX)NULL;\r
+                       result=0;\r
+               } else\r
+                       result=1;\r
+#elif defined(WIN32)\r
+               if((!(_mm_mutex_lists=CreateMutex(NULL,FALSE,"libmikmod(lists)")))||\r
+                  (!(_mm_mutex_vars=CreateMutex(NULL,FALSE,"libmikmod(vars)"))))\r
+                       result=0;\r
+               else\r
+                       result=1;\r
+#endif\r
+       }\r
+       return result;\r
+}\r
+\r
+MIKMODAPI void MikMod_Unlock(void)\r
+{\r
+       MUTEX_UNLOCK(lists);\r
+       MUTEX_UNLOCK(vars);\r
+}\r
+\r
+MIKMODAPI void MikMod_Lock(void)\r
+{\r
+       MUTEX_LOCK(vars);\r
+       MUTEX_LOCK(lists);\r
+}\r
+\r
+/*========== Parameter extraction helper */\r
+\r
+CHAR *MD_GetAtom(CHAR *atomname,CHAR *cmdline,BOOL implicit)\r
+{\r
+       CHAR *ret=NULL;\r
+\r
+       if(cmdline) {\r
+               CHAR *buf=strstr(cmdline,atomname);\r
+\r
+               if((buf)&&((buf==cmdline)||(*(buf-1)==','))) {\r
+                       CHAR *ptr=buf+strlen(atomname);\r
+\r
+                       if(*ptr=='=') {\r
+                               for(buf=++ptr;(*ptr)&&((*ptr)!=',');ptr++);\r
+                               ret=_mm_malloc((1+ptr-buf)*sizeof(CHAR));\r
+                               if(ret)\r
+                                       strncpy(ret,buf,ptr-buf);\r
+                       } else if((*ptr==',')||(!*ptr)) {\r
+                               if(implicit) {\r
+                                       ret=_mm_malloc((1+ptr-buf)*sizeof(CHAR));\r
+                                       if(ret)\r
+                                               strncpy(ret,buf,ptr-buf);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       return ret;\r
+}\r
+\r
+#if defined unix || (defined __APPLE__ && defined __MACH__)\r
+\r
+/*========== Posix helper functions */\r
+\r
+/* Check if the file is a regular or nonexistant file (or a link to a such a\r
+   file), and that, should the calling program be setuid, the access rights are\r
+   reasonable. Returns 1 if it is safe to rewrite the file, 0 otherwise.\r
+   The goal is to prevent a setuid root libmikmod application from overriding\r
+   files like /etc/passwd with digital sound... */\r
+BOOL MD_Access(CHAR *filename)\r
+{\r
+       struct stat buf;\r
+\r
+       if(!stat(filename,&buf)) {\r
+               /* not a regular file ? */\r
+               if(!S_ISREG(buf.st_mode)) return 0;\r
+               /* more than one hard link to the file ? */\r
+               if(buf.st_nlink>1) return 0;\r
+               /* check access rights with the real user and group id */\r
+               if(getuid()==buf.st_uid) {\r
+                       if(!(buf.st_mode&S_IWUSR)) return 0;\r
+               } else if(getgid()==buf.st_gid) {\r
+                       if(!(buf.st_mode&S_IWGRP)) return 0;\r
+               } else\r
+                       if(!(buf.st_mode&S_IWOTH)) return 0;\r
+       }\r
+       \r
+       return 1;\r
+}\r
+\r
+/* Drop all root privileges we might have */\r
+BOOL MD_DropPrivileges(void)\r
+{\r
+       if(!geteuid()) {\r
+               if(getuid()) {\r
+                       /* we are setuid root -> drop setuid to become the real user */\r
+                       if(setuid(getuid())) return 1;\r
+               } else {\r
+                       /* we are run as root -> drop all and become user 'nobody' */\r
+                       struct passwd *nobody;\r
+                       int uid;\r
+\r
+                       if(!(nobody=getpwnam("nobody"))) return 1; /* no such user ? */\r
+                       uid=nobody->pw_uid;\r
+                       if (!uid) /* user 'nobody' has root privileges ? weird... */\r
+                               return 1;\r
+                       if (setuid(uid)) return 1;\r
+               }\r
+       }\r
+       return 0;\r
+}\r
+\r
+#endif\r
+\r
+/* ex:set ts=4: */\r