1 /* MikMod sound library
2 (c) 1998, 1999, 2000, 2001 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 $Id: mdriver.c,v 1.4 2007/12/03 20:59:05 denis111 Exp $
25 These routines are used to access the available soundcard drivers.
27 ==============================================================================*/
38 #if defined unix || (defined __APPLE__ && defined __MACH__)
49 #include "mikmod_internals.h"
52 extern int fprintf(FILE *, const char *, ...);
55 static MDRIVER
*firstdriver
=NULL
;
56 MIKMODAPI MDRIVER
*md_driver
=NULL
;
57 extern MODULE
*pf
; /* modfile being played */
59 /* Initial global settings */
60 MIKMODAPI UWORD md_device
= 0; /* autodetect */
61 MIKMODAPI UWORD md_mixfreq
= 44100;
62 MIKMODAPI UWORD md_mode
= DMODE_STEREO
| DMODE_16BITS
|
63 DMODE_SURROUND
|DMODE_SOFT_MUSIC
|
65 MIKMODAPI UBYTE md_pansep
= 128; /* 128 == 100% (full left/right) */
66 MIKMODAPI UBYTE md_reverb
= 0; /* no reverb */
67 MIKMODAPI UBYTE md_volume
= 128; /* global sound volume (0-128) */
68 MIKMODAPI UBYTE md_musicvolume
= 128; /* volume of song */
69 MIKMODAPI UBYTE md_sndfxvolume
= 128; /* volume of sound effects */
70 UWORD md_bpm
= 125; /* tempo */
72 /* Do not modify the numchn variables yourself! use MD_SetVoices() */
73 UBYTE md_numchn
=0,md_sngchn
=0,md_sfxchn
=0;
74 UBYTE md_hardchn
=0,md_softchn
=0;
76 void (*md_player
)(void) = Player_HandleTick
;
77 static volatile int isplaying
=0, initialized
= 0;
78 static UBYTE
*sfxinfo
;
81 static SAMPLE
**md_sample
= NULL
;
83 /* Previous driver in use */
84 static SWORD olddevice
= -1;
86 /* Limits the number of hardware voices to the specified amount.
87 This function should only be used by the low-level drivers. */
88 static void LimitHardVoices(int limit
)
92 if (!(md_mode
& DMODE_SOFT_SNDFX
) && (md_sfxchn
>limit
)) md_sfxchn
=limit
;
93 if (!(md_mode
& DMODE_SOFT_MUSIC
) && (md_sngchn
>limit
)) md_sngchn
=limit
;
95 if (!(md_mode
& DMODE_SOFT_SNDFX
))
100 if (!(md_mode
& DMODE_SOFT_MUSIC
)) md_hardchn
+= md_sngchn
;
102 while (md_hardchn
>limit
) {
104 if (!(md_mode
& DMODE_SOFT_SNDFX
) && (md_sfxchn
>4)) md_sfxchn
--;
106 if (!(md_mode
& DMODE_SOFT_MUSIC
) && (md_sngchn
>8)) md_sngchn
--;
109 if (!(md_mode
& DMODE_SOFT_SNDFX
))
110 md_hardchn
=md_sfxchn
;
114 if (!(md_mode
& DMODE_SOFT_MUSIC
))
115 md_hardchn
+=md_sngchn
;
117 md_numchn
=md_hardchn
+md_softchn
;
120 /* Limits the number of hardware voices to the specified amount.
121 This function should only be used by the low-level drivers. */
122 static void LimitSoftVoices(int limit
)
126 if ((md_mode
& DMODE_SOFT_SNDFX
) && (md_sfxchn
>limit
)) md_sfxchn
=limit
;
127 if ((md_mode
& DMODE_SOFT_MUSIC
) && (md_sngchn
>limit
)) md_sngchn
=limit
;
129 if (md_mode
& DMODE_SOFT_SNDFX
)
130 md_softchn
=md_sfxchn
;
134 if (md_mode
& DMODE_SOFT_MUSIC
) md_softchn
+=md_sngchn
;
136 while (md_softchn
>limit
) {
138 if ((md_mode
& DMODE_SOFT_SNDFX
) && (md_sfxchn
>4)) md_sfxchn
--;
140 if ((md_mode
& DMODE_SOFT_MUSIC
) && (md_sngchn
>8)) md_sngchn
--;
143 if (!(md_mode
& DMODE_SOFT_SNDFX
))
144 md_softchn
=md_sfxchn
;
148 if (!(md_mode
& DMODE_SOFT_MUSIC
))
149 md_softchn
+=md_sngchn
;
151 md_numchn
=md_hardchn
+md_softchn
;
154 /* Note: 'type' indicates whether the returned value should be for music or for
156 ULONG
MD_SampleSpace(int type
)
159 type
=(md_mode
& DMODE_SOFT_MUSIC
)?MD_SOFTWARE
:MD_HARDWARE
;
160 else if(type
==MD_SNDFX
)
161 type
=(md_mode
& DMODE_SOFT_SNDFX
)?MD_SOFTWARE
:MD_HARDWARE
;
163 return md_driver
->FreeSampleSpace(type
);
166 ULONG
MD_SampleLength(int type
,SAMPLE
* s
)
169 type
=(md_mode
& DMODE_SOFT_MUSIC
)?MD_SOFTWARE
:MD_HARDWARE
;
172 type
=(md_mode
& DMODE_SOFT_SNDFX
)?MD_SOFTWARE
:MD_HARDWARE
;
174 return md_driver
->RealSampleLength(type
,s
);
177 MIKMODAPI CHAR
* MikMod_InfoDriver(void)
185 /* compute size of buffer */
186 for(l
=firstdriver
;l
;l
=l
->next
)
187 len
+=4+(l
->next
?1:0)+strlen(l
->Version
);
190 if((list
=MikMod_malloc(len
*sizeof(CHAR
)))) {
192 /* list all registered device drivers : */
193 for(t
=1,l
=firstdriver
;l
;l
=l
->next
,t
++)
194 sprintf(list
,(l
->next
)?"%s%2d %s\n":"%s%2d %s",
201 void _mm_registerdriver(struct MDRIVER
* drv
)
203 MDRIVER
*cruise
= firstdriver
;
205 /* don't register a MISSING() driver */
206 if ((drv
->Name
) && (drv
->Version
)) {
210 while(cruise
->next
) {
211 cruise
= cruise
->next
;
221 MIKMODAPI
void MikMod_RegisterDriver(struct MDRIVER
* drv
)
223 /* if we try to register an invalid driver, or an already registered driver,
224 ignore this attempt */
225 if ((!drv
)||(drv
->next
)||(!drv
->Name
))
229 _mm_registerdriver(drv
);
233 MIKMODAPI
int MikMod_DriverFromAlias(CHAR
*alias
)
242 if (!(strcasecmp(alias
,cruise
->Alias
))) break;
253 MIKMODAPI MDRIVER
*MikMod_DriverByOrdinal(int ordinal
)
257 /* Allow only driver ordinals > 0 */
262 cruise
= firstdriver
;
263 while (cruise
&& --ordinal
)
264 cruise
= cruise
->next
;
269 SWORD
MD_SampleLoad(SAMPLOAD
* s
, int type
)
274 type
=(md_mode
& DMODE_SOFT_MUSIC
)?MD_SOFTWARE
:MD_HARDWARE
;
275 else if(type
==MD_SNDFX
)
276 type
=(md_mode
& DMODE_SOFT_SNDFX
)?MD_SOFTWARE
:MD_HARDWARE
;
279 result
=md_driver
->SampleLoad(s
,type
);
285 void MD_SampleUnload(SWORD handle
)
287 md_driver
->SampleUnload(handle
);
290 MIKMODAPI MikMod_player_t
MikMod_RegisterPlayer(MikMod_player_t player
)
292 MikMod_player_t result
;
302 MIKMODAPI
void MikMod_Update(void)
306 if((!pf
)||(!pf
->forbid
))
309 if (md_driver
->Pause
)
316 void Voice_SetVolume_internal(SBYTE voice
,UWORD vol
)
320 if((voice
<0)||(voice
>=md_numchn
)) return;
323 if(md_musicvolume
>128) md_musicvolume
=128;
324 if(md_sndfxvolume
>128) md_sndfxvolume
=128;
325 if(md_volume
>128) md_volume
=128;
327 tmp
=(ULONG
)vol
*(ULONG
)md_volume
*
328 ((voice
<md_sngchn
)?(ULONG
)md_musicvolume
:(ULONG
)md_sndfxvolume
);
329 md_driver
->VoiceSetVolume(voice
,tmp
/16384UL);
332 MIKMODAPI
void Voice_SetVolume(SBYTE voice
,UWORD vol
)
335 Voice_SetVolume_internal(voice
,vol
);
339 MIKMODAPI UWORD
Voice_GetVolume(SBYTE voice
)
344 if((voice
>=0)&&(voice
<md_numchn
))
345 result
=md_driver
->VoiceGetVolume(voice
);
351 void Voice_SetFrequency_internal(SBYTE voice
,ULONG frq
)
353 if((voice
<0)||(voice
>=md_numchn
)) return;
354 if((md_sample
[voice
])&&(md_sample
[voice
]->divfactor
))
355 frq
/=md_sample
[voice
]->divfactor
;
356 md_driver
->VoiceSetFrequency(voice
,frq
);
359 MIKMODAPI
void Voice_SetFrequency(SBYTE voice
,ULONG frq
)
362 Voice_SetFrequency_internal(voice
,frq
);
366 MIKMODAPI ULONG
Voice_GetFrequency(SBYTE voice
)
371 if((voice
>=0)&&(voice
<md_numchn
))
372 result
=md_driver
->VoiceGetFrequency(voice
);
378 void Voice_SetPanning_internal(SBYTE voice
,ULONG pan
)
380 if((voice
<0)||(voice
>=md_numchn
)) return;
381 if(pan
!=PAN_SURROUND
) {
382 if(md_pansep
>128) md_pansep
=128;
383 if(md_mode
& DMODE_REVERSE
) pan
=255-pan
;
384 pan
= (((SWORD
)(pan
-128)*md_pansep
)/128)+128;
386 md_driver
->VoiceSetPanning(voice
, pan
);
389 MIKMODAPI
void Voice_SetPanning(SBYTE voice
,ULONG pan
)
392 if((pan
!=PAN_SURROUND
)&&((pan
<0)||(pan
>255)))
393 fprintf(stderr
,"\rVoice_SetPanning called with pan=%ld\n",(long)pan
);
397 Voice_SetPanning_internal(voice
,pan
);
401 MIKMODAPI ULONG
Voice_GetPanning(SBYTE voice
)
403 ULONG result
=PAN_CENTER
;
406 if((voice
>=0)&&(voice
<md_numchn
))
407 result
=md_driver
->VoiceGetPanning(voice
);
413 void Voice_Play_internal(SBYTE voice
,SAMPLE
* s
,ULONG start
)
417 if((voice
<0)||(voice
>=md_numchn
)) return;
423 /* repend can't be bigger than size */
424 if(repend
>s
->length
) repend
=s
->length
;
426 md_driver
->VoicePlay(voice
,s
->handle
,start
,s
->length
,s
->loopstart
,repend
,s
->flags
);
429 MIKMODAPI
void Voice_Play(SBYTE voice
,SAMPLE
* s
,ULONG start
)
431 if(start
>s
->length
) return;
434 Voice_Play_internal(voice
,s
,start
);
438 void Voice_Stop_internal(SBYTE voice
)
440 if((voice
<0)||(voice
>=md_numchn
)) return;
442 /* It is a sound effects channel, so flag the voice as non-critical! */
443 sfxinfo
[voice
-md_sngchn
]=0;
444 md_driver
->VoiceStop(voice
);
447 MIKMODAPI
void Voice_Stop(SBYTE voice
)
450 Voice_Stop_internal(voice
);
454 int Voice_Stopped_internal(SBYTE voice
)
456 if((voice
<0)||(voice
>=md_numchn
)) return 0;
457 return(md_driver
->VoiceStopped(voice
));
460 MIKMODAPI
int Voice_Stopped(SBYTE voice
)
465 result
=Voice_Stopped_internal(voice
);
471 MIKMODAPI SLONG
Voice_GetPosition(SBYTE voice
)
476 if((voice
>=0)&&(voice
<md_numchn
)) {
477 if (md_driver
->VoiceGetPosition
)
478 result
=(md_driver
->VoiceGetPosition(voice
));
487 MIKMODAPI ULONG
Voice_RealVolume(SBYTE voice
)
492 if((voice
>=0)&&(voice
<md_numchn
)&& md_driver
->VoiceRealVolume
)
493 result
=(md_driver
->VoiceRealVolume(voice
));
499 extern MikMod_callback_t vc_callback
;
501 MIKMODAPI
void VC_SetCallback(MikMod_callback_t callback
)
503 vc_callback
= callback
;
506 static int _mm_init(CHAR
*cmdline
)
512 /* if md_device==0, try to find a device number */
516 for(t
=1,md_driver
=firstdriver
;md_driver
;md_driver
=md_driver
->next
,t
++)
517 if(md_driver
->IsPresent()) break;
520 _mm_errno
= MMERR_DETECTING_DEVICE
;
521 if(_mm_errorhandler
) _mm_errorhandler();
522 md_driver
= &drv_nos
;
528 /* if n>0, use that driver */
529 for(t
=1,md_driver
=firstdriver
;(md_driver
)&&(t
!=md_device
);md_driver
=md_driver
->next
)
533 _mm_errno
= MMERR_INVALID_DEVICE
;
534 if(_mm_errorhandler
) _mm_errorhandler();
535 md_driver
= &drv_nos
;
539 /* arguments here might be necessary for the presence check to succeed */
540 if(cmdline
&&(md_driver
->CommandLine
))
541 md_driver
->CommandLine(cmdline
);
543 if(!md_driver
->IsPresent()) {
544 _mm_errno
= MMERR_DETECTING_DEVICE
;
545 if(_mm_errorhandler
) _mm_errorhandler();
546 md_driver
= &drv_nos
;
551 olddevice
= md_device
;
552 if(md_driver
->Init()) {
553 MikMod_Exit_internal();
554 if(_mm_errorhandler
) _mm_errorhandler();
564 MIKMODAPI
int MikMod_Init(CHAR
*cmdline
)
570 result
=_mm_init(cmdline
);
577 void MikMod_Exit_internal(void)
579 MikMod_DisableOutput_internal();
581 md_numchn
= md_sfxchn
= md_sngchn
= 0;
582 md_driver
= &drv_nos
;
584 if(sfxinfo
) MikMod_free(sfxinfo
);
585 if(md_sample
) MikMod_free(md_sample
);
592 MIKMODAPI
void MikMod_Exit(void)
596 MikMod_Exit_internal();
601 /* Reset the driver using the new global variable settings.
602 If the driver has not been initialized, it will be now. */
603 static int _mm_reset(CHAR
*cmdline
)
607 if(!initialized
) return _mm_init(cmdline
);
611 md_driver
->PlayStop();
614 if((!md_driver
->Reset
)||(md_device
!= olddevice
)) {
615 /* md_driver->Reset was NULL, or md_device was changed, so do a full
616 reset of the driver. */
618 if(_mm_init(cmdline
)) {
619 MikMod_Exit_internal();
621 if(_mm_errorhandler
) _mm_errorhandler();
625 if(md_driver
->Reset()) {
626 MikMod_Exit_internal();
628 if(_mm_errorhandler
) _mm_errorhandler();
633 if (wasplaying
) md_driver
->PlayStart();
637 MIKMODAPI
int MikMod_Reset(CHAR
*cmdline
)
643 result
=_mm_reset(cmdline
);
650 /* If either parameter is -1, the current set value will be retained. */
651 int MikMod_SetNumVoices_internal(int music
, int sfx
)
656 if((!music
)&&(!sfx
)) return 1;
659 MikMod_DisableOutput_internal();
664 if(sfxinfo
) MikMod_free(sfxinfo
);
665 if(md_sample
) MikMod_free(md_sample
);
669 if(music
!=-1) md_sngchn
= music
;
670 if(sfx
!=-1) md_sfxchn
= sfx
;
671 md_numchn
= md_sngchn
+ md_sfxchn
;
673 LimitHardVoices(md_driver
->HardVoiceLimit
);
674 LimitSoftVoices(md_driver
->SoftVoiceLimit
);
676 if(md_driver
->SetNumVoices()) {
677 MikMod_Exit_internal();
679 if(_mm_errorhandler
!=NULL
) _mm_errorhandler();
680 md_numchn
= md_softchn
= md_hardchn
= md_sfxchn
= md_sngchn
= 0;
684 if(md_sngchn
+md_sfxchn
)
685 md_sample
=(SAMPLE
**)MikMod_calloc(md_sngchn
+md_sfxchn
,sizeof(SAMPLE
*));
687 sfxinfo
= (UBYTE
*)MikMod_calloc(md_sfxchn
,sizeof(UBYTE
));
689 /* make sure the player doesn't start with garbage */
690 for(t
=oldchn
;t
<md_numchn
;t
++) Voice_Stop_internal(t
);
693 if(resume
) MikMod_EnableOutput_internal();
699 MIKMODAPI
int MikMod_SetNumVoices(int music
, int sfx
)
704 result
=MikMod_SetNumVoices_internal(music
,sfx
);
710 int MikMod_EnableOutput_internal(void)
714 if(md_driver
->PlayStart()) return 1;
721 MIKMODAPI
int MikMod_EnableOutput(void)
726 result
=MikMod_EnableOutput_internal();
732 void MikMod_DisableOutput_internal(void)
734 if(isplaying
&& md_driver
) {
736 md_driver
->PlayStop();
740 MIKMODAPI
void MikMod_DisableOutput(void)
743 MikMod_DisableOutput_internal();
747 int MikMod_Active_internal(void)
752 MIKMODAPI
int MikMod_Active(void)
757 result
=MikMod_Active_internal();
763 /* Plays a sound effects sample. Picks a voice from the number of voices
764 allocated for use as sound effects (loops through voices, skipping all active
767 Returns the voice that the sound is being played on. */
768 SBYTE
Sample_Play_internal(SAMPLE
*s
,ULONG start
,UBYTE flags
)
770 int orig
=sfxpool
;/* for cases where all channels are critical */
773 if(!md_sfxchn
) return -1;
774 if(s
->volume
>64) s
->volume
= 64;
776 /* check the first location after sfxpool */
778 if(sfxinfo
[sfxpool
]&SFX_CRITICAL
) {
779 if(md_driver
->VoiceStopped(c
=sfxpool
+md_sngchn
)) {
780 sfxinfo
[sfxpool
]=flags
;
781 Voice_Play_internal(c
,s
,start
);
782 md_driver
->VoiceSetVolume(c
,s
->volume
<<2);
783 Voice_SetPanning_internal(c
,s
->panning
);
784 md_driver
->VoiceSetFrequency(c
,s
->speed
);
786 if(sfxpool
>=md_sfxchn
) sfxpool
=0;
790 sfxinfo
[sfxpool
]=flags
;
791 Voice_Play_internal(c
=sfxpool
+md_sngchn
,s
,start
);
792 md_driver
->VoiceSetVolume(c
,s
->volume
<<2);
793 Voice_SetPanning_internal(c
,s
->panning
);
794 md_driver
->VoiceSetFrequency(c
,s
->speed
);
796 if(sfxpool
>=md_sfxchn
) sfxpool
=0;
801 if(sfxpool
>=md_sfxchn
) sfxpool
= 0;
802 } while(sfxpool
!=orig
);
807 MIKMODAPI SBYTE
Sample_Play(SAMPLE
*s
,ULONG start
,UBYTE flags
)
812 result
=Sample_Play_internal(s
,start
,flags
);
818 MIKMODAPI
long MikMod_GetVersion(void)
820 return LIBMIKMOD_VERSION
;
823 /*========== MT-safe stuff */
826 #define INIT_MUTEX(name) \
827 pthread_mutex_t _mm_mutex_##name=PTHREAD_MUTEX_INITIALIZER
828 #elif defined(__OS2__)||defined(__EMX__)
829 #define INIT_MUTEX(name) \
830 HMTX _mm_mutex_##name
832 #define INIT_MUTEX(name) \
833 HANDLE _mm_mutex_##name
835 #define INIT_MUTEX(name) \
836 void *_mm_mutex_##name = NULL
842 MIKMODAPI
int MikMod_InitThreads(void)
844 static int firstcall
=1;
851 #elif defined(__OS2__)||defined(__EMX__)
852 if(DosCreateMutexSem((PSZ
)NULL
,&_mm_mutex_lists
,0,0) ||
853 DosCreateMutexSem((PSZ
)NULL
,&_mm_mutex_vars
,0,0)) {
854 _mm_mutex_lists
=_mm_mutex_vars
=(HMTX
)NULL
;
859 if((!(_mm_mutex_lists
=CreateMutex(NULL
,FALSE
,"libmikmod(lists)")))||
860 (!(_mm_mutex_vars
=CreateMutex(NULL
,FALSE
,"libmikmod(vars)"))))
869 MIKMODAPI
void MikMod_Unlock(void)
875 MIKMODAPI
void MikMod_Lock(void)
881 /*========== Parameter extraction helper */
883 CHAR
*MD_GetAtom(CHAR
*atomname
,CHAR
*cmdline
,int implicit
)
888 CHAR
*buf
=strstr(cmdline
,atomname
);
890 if((buf
)&&((buf
==cmdline
)||(*(buf
-1)==','))) {
891 CHAR
*ptr
=buf
+strlen(atomname
);
894 for(buf
=++ptr
;(*ptr
)&&((*ptr
)!=',');ptr
++);
895 ret
=MikMod_malloc((1+ptr
-buf
)*sizeof(CHAR
));
897 strncpy(ret
,buf
,ptr
-buf
);
898 } else if((*ptr
==',')||(!*ptr
)) {
900 ret
=MikMod_malloc((1+ptr
-buf
)*sizeof(CHAR
));
902 strncpy(ret
,buf
,ptr
-buf
);
911 #if defined unix || (defined __APPLE__ && defined __MACH__)
913 /*========== Posix helper functions */
915 /* Check if the file is a regular or nonexistant file (or a link to a such a
916 file), and that, should the calling program be setuid, the access rights are
917 reasonable. Returns 1 if it is safe to rewrite the file, 0 otherwise.
918 The goal is to prevent a setuid root libmikmod application from overriding
919 files like /etc/passwd with digital sound... */
920 int MD_Access(CHAR
*filename
)
924 if(!stat(filename
,&buf
)) {
925 /* not a regular file ? */
926 if(!S_ISREG(buf
.st_mode
)) return 0;
927 /* more than one hard link to the file ? */
928 if(buf
.st_nlink
>1) return 0;
929 /* check access rights with the real user and group id */
930 if(getuid()==buf
.st_uid
) {
931 if(!(buf
.st_mode
&S_IWUSR
)) return 0;
932 } else if(getgid()==buf
.st_gid
) {
933 if(!(buf
.st_mode
&S_IWGRP
)) return 0;
935 if(!(buf
.st_mode
&S_IWOTH
)) return 0;
941 /* Drop all root privileges we might have */
942 int MD_DropPrivileges(void)
946 /* we are setuid root -> drop setuid to become the real user */
947 if(setuid(getuid())) return 1;
949 /* we are run as root -> drop all and become user 'nobody' */
950 struct passwd
*nobody
;
953 if(!(nobody
=getpwnam("nobody"))) return 1; /* no such user ? */
955 if (!uid
) /* user 'nobody' has root privileges ? weird... */
957 if (setuid(uid
)) return 1;