1 /* MikMod sound library
2 (c) 1998-2014 Miodrag Vallat and others - see file AUTHORS
5 This library is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of
8 the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 /*==============================================================================
23 These routines are used to access the available soundcard drivers.
25 ==============================================================================*/
37 #include "mikmod_internals.h"
45 extern int fprintf(FILE *, const char *, ...);
48 extern MODULE *pf; /* modfile being played */
50 /* EXPORTED GLOBALS */
51 MIKMODAPI MDRIVER *md_driver = NULL;
53 /* Initial global settings */
54 MIKMODAPI UWORD md_device = 0; /* autodetect */
55 MIKMODAPI UWORD md_mixfreq = 44100;
56 MIKMODAPI UWORD md_mode = DMODE_STEREO | DMODE_16BITS |
58 DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX;
59 MIKMODAPI UBYTE md_pansep = 128; /* 128 == 100% (full left/right) */
60 MIKMODAPI UBYTE md_reverb = 0; /* no reverb */
61 MIKMODAPI UBYTE md_volume = 128; /* global sound volume (0-128) */
62 MIKMODAPI UBYTE md_musicvolume = 128; /* volume of song */
63 MIKMODAPI UBYTE md_sndfxvolume = 128; /* volume of sound effects */
65 /* INTERNAL GLOBALS */
66 UWORD md_bpm = 125; /* tempo */
68 /* Do not modify the numchn variables yourself! use MikMod_SetNumVoices() */
69 UBYTE md_numchn = 0, md_sngchn = 0, md_sfxchn = 0;
70 UBYTE md_hardchn = 0, md_softchn= 0;
72 void (*md_player)(void) = Player_HandleTick;
74 MikMod_callback_t vc_callback = NULL;
77 static MDRIVER *firstdriver = NULL;
79 static volatile BOOL isplaying = 0, initialized = 0;
81 static UBYTE *sfxinfo;
84 static SAMPLE **md_sample = NULL;
86 /* Previous driver in use */
87 static SWORD olddevice = -1;
89 /* Limits the number of hardware voices to the specified amount.
90 This function should only be used by the low-level drivers. */
91 static void LimitHardVoices(int limit)
95 if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>limit)) md_sfxchn=limit;
96 if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>limit)) md_sngchn=limit;
98 if (!(md_mode & DMODE_SOFT_SNDFX))
103 if (!(md_mode & DMODE_SOFT_MUSIC)) md_hardchn += md_sngchn;
105 while (md_hardchn>limit) {
107 if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>4)) md_sfxchn--;
109 if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>8)) md_sngchn--;
112 if (!(md_mode & DMODE_SOFT_SNDFX))
113 md_hardchn=md_sfxchn;
117 if (!(md_mode & DMODE_SOFT_MUSIC))
118 md_hardchn+=md_sngchn;
120 md_numchn=md_hardchn+md_softchn;
123 /* Limits the number of hardware voices to the specified amount.
124 This function should only be used by the low-level drivers. */
125 static void LimitSoftVoices(int limit)
129 if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>limit)) md_sfxchn=limit;
130 if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>limit)) md_sngchn=limit;
132 if (md_mode & DMODE_SOFT_SNDFX)
133 md_softchn=md_sfxchn;
137 if (md_mode & DMODE_SOFT_MUSIC) md_softchn+=md_sngchn;
139 while (md_softchn>limit) {
141 if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>4)) md_sfxchn--;
143 if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>8)) md_sngchn--;
146 if (!(md_mode & DMODE_SOFT_SNDFX))
147 md_softchn=md_sfxchn;
151 if (!(md_mode & DMODE_SOFT_MUSIC))
152 md_softchn+=md_sngchn;
154 md_numchn=md_hardchn+md_softchn;
157 /* Note: 'type' indicates whether the returned value should be for music or for
159 ULONG MD_SampleSpace(int type)
162 type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE;
163 else if(type==MD_SNDFX)
164 type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE;
166 return md_driver->FreeSampleSpace(type);
169 ULONG MD_SampleLength(int type,SAMPLE* s)
172 type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE;
175 type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE;
177 return md_driver->RealSampleLength(type,s);
180 MIKMODAPI CHAR* MikMod_InfoDriver(void)
188 /* compute size of buffer */
189 for(l = firstdriver; l; l = l->next)
190 len += 4 + (l->next ? 1 : 0) + strlen(l->Version);
193 if((list=(CHAR*)MikMod_malloc(len*sizeof(CHAR))) != NULL) {
194 CHAR *list_end = list;
196 /* list all registered device drivers : */
197 for(t = 1, l = firstdriver; l; l = l->next, t++) {
198 list_end += sprintf(list_end, "%2d %s%s", t, l->Version, (l->next)? "\n" : "");
205 void _mm_registerdriver(struct MDRIVER* drv)
207 MDRIVER *cruise = firstdriver;
209 /* don't register a MISSING() driver */
210 if ((drv->Name) && (drv->Version)) {
214 while(cruise->next) {
215 cruise = cruise->next;
225 MIKMODAPI void MikMod_RegisterDriver(struct MDRIVER* drv)
227 /* if we try to register an invalid driver, or an already registered driver,
228 ignore this attempt */
229 if ((!drv)||(drv->next)||(!drv->Name))
233 _mm_registerdriver(drv);
237 MIKMODAPI int MikMod_DriverFromAlias(const CHAR *alias)
246 if (!(_mm_strcasecmp(alias,cruise->Alias))) break;
257 MIKMODAPI MDRIVER *MikMod_DriverByOrdinal(int ordinal)
261 /* Allow only driver ordinals > 0 */
262 if (!ordinal) return NULL;
265 cruise = firstdriver;
266 while (cruise && --ordinal)
267 cruise = cruise->next;
272 SWORD MD_SampleLoad(SAMPLOAD* s, int type)
277 type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE;
278 else if(type==MD_SNDFX)
279 type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE;
282 result=md_driver->SampleLoad(s,type);
288 void MD_SampleUnload(SWORD handle)
290 md_driver->SampleUnload(handle);
293 MIKMODAPI MikMod_player_t MikMod_RegisterPlayer(MikMod_player_t player)
295 MikMod_player_t result;
305 MIKMODAPI void MikMod_Update(void)
309 if((!pf)||(!pf->forbid))
312 if (md_driver->Pause)
319 void Voice_SetVolume_internal(SBYTE voice,UWORD vol)
323 if((voice<0)||(voice>=md_numchn)) return;
326 if(md_musicvolume>128) md_musicvolume=128;
327 if(md_sndfxvolume>128) md_sndfxvolume=128;
328 if(md_volume>128) md_volume=128;
330 tmp=(ULONG)vol*(ULONG)md_volume*
331 ((voice<md_sngchn)?(ULONG)md_musicvolume:(ULONG)md_sndfxvolume);
332 md_driver->VoiceSetVolume(voice,tmp/16384UL);
335 MIKMODAPI void Voice_SetVolume(SBYTE voice,UWORD vol)
338 Voice_SetVolume_internal(voice,vol);
342 MIKMODAPI UWORD Voice_GetVolume(SBYTE voice)
347 if((voice>=0)&&(voice<md_numchn))
348 result=md_driver->VoiceGetVolume(voice);
354 void Voice_SetFrequency_internal(SBYTE voice,ULONG frq)
356 if((voice<0)||(voice>=md_numchn)) return;
357 if((md_sample[voice])&&(md_sample[voice]->divfactor))
358 frq/=md_sample[voice]->divfactor;
359 md_driver->VoiceSetFrequency(voice,frq);
362 MIKMODAPI void Voice_SetFrequency(SBYTE voice,ULONG frq)
365 Voice_SetFrequency_internal(voice,frq);
369 MIKMODAPI ULONG Voice_GetFrequency(SBYTE voice)
374 if((voice>=0)&&(voice<md_numchn))
375 result=md_driver->VoiceGetFrequency(voice);
381 void Voice_SetPanning_internal(SBYTE voice,ULONG pan)
383 if((voice<0)||(voice>=md_numchn)) return;
384 if(pan!=PAN_SURROUND) {
385 if(md_pansep>128) md_pansep=128;
386 if(md_mode & DMODE_REVERSE) pan=255-pan;
387 pan = (((SWORD)(pan-128)*md_pansep)/128)+128;
389 md_driver->VoiceSetPanning(voice, pan);
392 MIKMODAPI void Voice_SetPanning(SBYTE voice,ULONG pan)
395 if((pan!=PAN_SURROUND)&&((pan<0)||(pan>255)))
396 fprintf(stderr,"\rVoice_SetPanning called with pan=%ld\n",(long)pan);
400 Voice_SetPanning_internal(voice,pan);
404 MIKMODAPI ULONG Voice_GetPanning(SBYTE voice)
406 ULONG result=PAN_CENTER;
409 if((voice>=0)&&(voice<md_numchn))
410 result=md_driver->VoiceGetPanning(voice);
416 void Voice_Play_internal(SBYTE voice,SAMPLE* s,ULONG start)
420 if((voice<0)||(voice>=md_numchn)) return;
426 /* repend can't be bigger than size */
427 if(repend>s->length) repend=s->length;
429 md_driver->VoicePlay(voice,s->handle,start,s->length,s->loopstart,repend,s->flags);
432 MIKMODAPI void Voice_Play(SBYTE voice,SAMPLE* s,ULONG start)
434 if(start>s->length) return;
437 Voice_Play_internal(voice,s,start);
441 void Voice_Stop_internal(SBYTE voice)
443 if((voice<0)||(voice>=md_numchn)) return;
445 /* It is a sound effects channel, so flag the voice as non-critical! */
446 sfxinfo[voice-md_sngchn]=0;
447 md_driver->VoiceStop(voice);
450 MIKMODAPI void Voice_Stop(SBYTE voice)
453 Voice_Stop_internal(voice);
457 BOOL Voice_Stopped_internal(SBYTE voice)
459 if((voice<0)||(voice>=md_numchn)) return 0;
460 return(md_driver->VoiceStopped(voice));
463 MIKMODAPI BOOL Voice_Stopped(SBYTE voice)
468 result=Voice_Stopped_internal(voice);
474 MIKMODAPI SLONG Voice_GetPosition(SBYTE voice)
479 if((voice>=0)&&(voice<md_numchn)) {
480 if (md_driver->VoiceGetPosition)
481 result=(md_driver->VoiceGetPosition(voice));
490 MIKMODAPI ULONG Voice_RealVolume(SBYTE voice)
495 if((voice>=0)&&(voice<md_numchn)&& md_driver->VoiceRealVolume)
496 result=(md_driver->VoiceRealVolume(voice));
502 MIKMODAPI void VC_SetCallback(MikMod_callback_t callback)
504 vc_callback = callback;
507 static int _mm_init(const CHAR *cmdline)
513 /* if md_device==0, try to find a device number */
517 for(t=1,md_driver=firstdriver;md_driver;md_driver=md_driver->next,t++)
518 if(md_driver->IsPresent()) break;
521 _mm_errno = MMERR_DETECTING_DEVICE;
522 if(_mm_errorhandler) _mm_errorhandler();
523 md_driver = &drv_nos;
529 /* if n>0, use that driver */
530 for(t=1,md_driver=firstdriver;(md_driver)&&(t!=md_device);md_driver=md_driver->next)
534 _mm_errno = MMERR_INVALID_DEVICE;
535 if(_mm_errorhandler) _mm_errorhandler();
536 md_driver = &drv_nos;
540 /* arguments here might be necessary for the presence check to succeed */
541 if(cmdline&&(md_driver->CommandLine))
542 md_driver->CommandLine(cmdline);
544 if(!md_driver->IsPresent()) {
545 _mm_errno = MMERR_DETECTING_DEVICE;
546 if(_mm_errorhandler) _mm_errorhandler();
547 md_driver = &drv_nos;
552 olddevice = md_device;
553 if(md_driver->Init()) {
554 MikMod_Exit_internal();
555 if(_mm_errorhandler) _mm_errorhandler();
565 MIKMODAPI int MikMod_Init(const CHAR *cmdline)
571 result=_mm_init(cmdline);
578 void MikMod_Exit_internal(void)
580 MikMod_DisableOutput_internal();
582 md_numchn = md_sfxchn = md_sngchn = 0;
583 md_driver = &drv_nos;
585 MikMod_free(sfxinfo);
586 MikMod_free(md_sample);
593 MIKMODAPI void MikMod_Exit(void)
597 MikMod_Exit_internal();
602 /* Reset the driver using the new global variable settings.
603 If the driver has not been initialized, it will be now. */
604 static int _mm_reset(const CHAR *cmdline)
608 if(!initialized) return _mm_init(cmdline);
612 md_driver->PlayStop();
615 if((!md_driver->Reset)||(md_device != olddevice)) {
616 /* md_driver->Reset was NULL, or md_device was changed, so do a full
617 reset of the driver. */
619 if(_mm_init(cmdline)) {
620 MikMod_Exit_internal();
622 if(_mm_errorhandler) _mm_errorhandler();
626 if(md_driver->Reset()) {
627 MikMod_Exit_internal();
629 if(_mm_errorhandler) _mm_errorhandler();
634 if (wasplaying) return md_driver->PlayStart();
638 MIKMODAPI int MikMod_Reset(const CHAR *cmdline)
644 result=_mm_reset(cmdline);
651 /* If either parameter is -1, the current set value will be retained. */
652 int MikMod_SetNumVoices_internal(int music, int sfx)
657 if((!music)&&(!sfx)) return 1;
660 MikMod_DisableOutput_internal();
665 MikMod_free(sfxinfo);
666 MikMod_free(md_sample);
670 if(music!=-1) md_sngchn = music;
671 if(sfx!=-1) md_sfxchn = sfx;
672 md_numchn = md_sngchn + md_sfxchn;
674 LimitHardVoices(md_driver->HardVoiceLimit);
675 LimitSoftVoices(md_driver->SoftVoiceLimit);
677 if(md_driver->SetNumVoices()) {
678 MikMod_Exit_internal();
680 if(_mm_errorhandler!=NULL) _mm_errorhandler();
681 md_numchn = md_softchn = md_hardchn = md_sfxchn = md_sngchn = 0;
685 if(md_sngchn+md_sfxchn)
686 md_sample=(SAMPLE**)MikMod_calloc(md_sngchn+md_sfxchn,sizeof(SAMPLE*));
688 sfxinfo = (UBYTE *)MikMod_calloc(md_sfxchn,sizeof(UBYTE));
690 /* make sure the player doesn't start with garbage */
691 for(t=oldchn;t<md_numchn;t++) Voice_Stop_internal(t);
694 if(resume) MikMod_EnableOutput_internal();
700 MIKMODAPI int MikMod_SetNumVoices(int music, int sfx)
705 result=MikMod_SetNumVoices_internal(music,sfx);
711 int MikMod_EnableOutput_internal(void)
715 if(md_driver->PlayStart()) return 1;
722 MIKMODAPI int MikMod_EnableOutput(void)
727 result=MikMod_EnableOutput_internal();
733 void MikMod_DisableOutput_internal(void)
735 if(isplaying && md_driver) {
737 md_driver->PlayStop();
741 MIKMODAPI void MikMod_DisableOutput(void)
744 MikMod_DisableOutput_internal();
748 BOOL MikMod_Active_internal(void)
753 MIKMODAPI BOOL MikMod_Active(void)
758 result=MikMod_Active_internal();
764 /* Plays a sound effects sample. Picks a voice from the number of voices
765 allocated for use as sound effects (loops through voices, skipping all active
768 Returns the voice that the sound is being played on. */
769 static SBYTE Sample_Play_internal(SAMPLE *s,ULONG start,UBYTE flags)
771 int orig=sfxpool;/* for cases where all channels are critical */
774 if(!md_sfxchn) return -1;
775 if(s->volume>64) s->volume = 64;
777 /* check the first location after sfxpool */
779 if(sfxinfo[sfxpool]&SFX_CRITICAL) {
780 if(md_driver->VoiceStopped(c=sfxpool+md_sngchn)) {
781 sfxinfo[sfxpool]=flags;
782 Voice_Play_internal(c,s,start);
783 md_driver->VoiceSetVolume(c,s->volume<<2);
784 Voice_SetPanning_internal(c,s->panning);
785 md_driver->VoiceSetFrequency(c,s->speed);
787 if(sfxpool>=md_sfxchn) sfxpool=0;
791 sfxinfo[sfxpool]=flags;
792 Voice_Play_internal(c=sfxpool+md_sngchn,s,start);
793 md_driver->VoiceSetVolume(c,s->volume<<2);
794 Voice_SetPanning_internal(c,s->panning);
795 md_driver->VoiceSetFrequency(c,s->speed);
797 if(sfxpool>=md_sfxchn) sfxpool=0;
802 if(sfxpool>=md_sfxchn) sfxpool = 0;
803 } while(sfxpool!=orig);
808 MIKMODAPI SBYTE Sample_Play(SAMPLE *s,ULONG start,UBYTE flags)
813 result=Sample_Play_internal(s,start,flags);
819 MIKMODAPI long MikMod_GetVersion(void)
821 return LIBMIKMOD_VERSION;
824 /*========== MT-safe stuff */
827 #define INIT_MUTEX(name) \
828 pthread_mutex_t _mm_mutex_##name=PTHREAD_MUTEX_INITIALIZER
830 #elif defined(__OS2__)||defined(__EMX__)
831 #define INIT_MUTEX(name) \
832 HMTX _mm_mutex_##name
834 #elif defined(_WIN32)
835 #define INIT_MUTEX(name) \
836 HANDLE _mm_mutex_##name
839 #define INIT_MUTEX(name) \
840 void *_mm_mutex_##name = NULL
846 MIKMODAPI BOOL MikMod_InitThreads(void)
848 static int firstcall=1;
849 static BOOL result = 0;
855 #elif defined(__OS2__)||defined(__EMX__)
856 if(DosCreateMutexSem((PSZ)NULL,&_mm_mutex_lists,0,0) ||
857 DosCreateMutexSem((PSZ)NULL,&_mm_mutex_vars,0,0)) {
858 _mm_mutex_lists=_mm_mutex_vars=(HMTX)NULL;
862 #elif defined(_WIN32)
863 if((!(_mm_mutex_lists=CreateMutex(NULL,FALSE,TEXT("libmikmod(lists)"))))||
864 (!(_mm_mutex_vars=CreateMutex(NULL,FALSE,TEXT("libmikmod(vars)")))))
873 MIKMODAPI void MikMod_Unlock(void)
879 MIKMODAPI void MikMod_Lock(void)
885 /*========== Parameter extraction helper */
887 CHAR *MD_GetAtom(const CHAR *atomname, const CHAR *cmdline, BOOL implicit)
892 const CHAR *buf=strstr(cmdline,atomname);
894 if((buf)&&((buf==cmdline)||(*(buf-1)==','))) {
895 const CHAR *ptr=buf+strlen(atomname);
898 for(buf=++ptr;(*ptr)&&((*ptr)!=',');ptr++);
899 ret=(CHAR *)MikMod_malloc((1+ptr-buf)*sizeof(CHAR));
901 strncpy(ret,buf,ptr-buf);
902 } else if((*ptr==',')||(!*ptr)) {
904 ret=(CHAR *)MikMod_malloc((1+ptr-buf)*sizeof(CHAR));
906 strncpy(ret,buf,ptr-buf);
916 /*========== Posix helper functions */
918 /* Check if the file is a regular or nonexistant file (or a link to a such a
919 file), and that, should the calling program be setuid, the access rights are
920 reasonable. Returns 1 if it is safe to rewrite the file, 0 otherwise.
921 The goal is to prevent a setuid root libmikmod application from overriding
922 files like /etc/passwd with digital sound... */
923 BOOL MD_Access(const CHAR * filename)
927 if(!stat(filename,&buf)) {
928 /* not a regular file ? */
929 if(!S_ISREG(buf.st_mode)) return 0;
930 /* more than one hard link to the file ? */
931 if(buf.st_nlink>1) return 0;
932 /* check access rights with the real user and group id */
933 if(getuid()==buf.st_uid) {
934 if(!(buf.st_mode&S_IWUSR)) return 0;
935 } else if(getgid()==buf.st_gid) {
936 if(!(buf.st_mode&S_IWGRP)) return 0;
938 if(!(buf.st_mode&S_IWOTH)) return 0;
944 /* Drop all root privileges we might have */
945 int MD_DropPrivileges(void)
949 /* we are setuid root -> drop setuid to become the real user */
950 if(setuid(getuid())) return 1;
952 /* we are run as root -> drop all and become user 'nobody' */
953 struct passwd *nobody;
956 if(!(nobody=getpwnam("nobody"))) return 1; /* no such user ? */
958 if (!uid) /* user 'nobody' has root privileges ? weird... */
960 if (setuid(uid)) return 1;