initial import
[dosrtxon] / libs / mikmod / playercode / mdriver.c
1 /*      MikMod sound library
2         (c) 1998-2014 Miodrag Vallat and others - see file AUTHORS
3         for a complete list.
4
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.
9
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.
14
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
18         02111-1307, USA.
19 */
20
21 /*==============================================================================
22
23   These routines are used to access the available soundcard drivers.
24
25 ==============================================================================*/
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34
35 #include <string.h>
36
37 #include "mikmod_internals.h"
38
39 #if (MIKMOD_UNIX)
40 #include <pwd.h>
41 #include <sys/stat.h>
42 #endif
43
44 #ifdef SUNOS
45 extern int fprintf(FILE *, const char *, ...);
46 #endif
47
48 extern  MODULE *pf; /* modfile being played */
49
50 /* EXPORTED GLOBALS */
51 MIKMODAPI MDRIVER *md_driver    = NULL;
52
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 |
57                                   DMODE_SURROUND |
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 */
64
65 /* INTERNAL GLOBALS */
66 UWORD md_bpm = 125;     /* tempo */
67
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;
71
72 void (*md_player)(void) = Player_HandleTick;
73
74 MikMod_callback_t vc_callback = NULL;
75
76 /* PRIVATE VARS */
77 static MDRIVER *firstdriver = NULL;
78
79 static volatile BOOL isplaying = 0, initialized = 0;
80
81 static UBYTE *sfxinfo;
82 static int sfxpool;
83
84 static SAMPLE **md_sample = NULL;
85
86 /* Previous driver in use */
87 static SWORD olddevice = -1;
88
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)
92 {
93         int t=0;
94
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;
97
98         if (!(md_mode & DMODE_SOFT_SNDFX))
99                 md_hardchn=md_sfxchn;
100         else
101                 md_hardchn=0;
102
103         if (!(md_mode & DMODE_SOFT_MUSIC)) md_hardchn += md_sngchn;
104
105         while (md_hardchn>limit) {
106                 if (++t & 1) {
107                         if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>4)) md_sfxchn--;
108                 } else {
109                         if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>8)) md_sngchn--;
110                 }
111
112                 if (!(md_mode & DMODE_SOFT_SNDFX))
113                         md_hardchn=md_sfxchn;
114                 else
115                         md_hardchn=0;
116
117                 if (!(md_mode & DMODE_SOFT_MUSIC))
118                         md_hardchn+=md_sngchn;
119         }
120         md_numchn=md_hardchn+md_softchn;
121 }
122
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)
126 {
127         int t=0;
128
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;
131
132         if (md_mode & DMODE_SOFT_SNDFX)
133                 md_softchn=md_sfxchn;
134         else
135                 md_softchn=0;
136
137         if (md_mode & DMODE_SOFT_MUSIC) md_softchn+=md_sngchn;
138
139         while (md_softchn>limit) {
140                 if (++t & 1) {
141                         if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>4)) md_sfxchn--;
142                 } else {
143                         if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>8)) md_sngchn--;
144                 }
145
146                 if (!(md_mode & DMODE_SOFT_SNDFX))
147                         md_softchn=md_sfxchn;
148                 else
149                         md_softchn=0;
150
151                 if (!(md_mode & DMODE_SOFT_MUSIC))
152                         md_softchn+=md_sngchn;
153         }
154         md_numchn=md_hardchn+md_softchn;
155 }
156
157 /* Note: 'type' indicates whether the returned value should be for music or for
158    sound effects. */
159 ULONG MD_SampleSpace(int type)
160 {
161         if(type==MD_MUSIC)
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;
165
166         return md_driver->FreeSampleSpace(type);
167 }
168
169 ULONG MD_SampleLength(int type,SAMPLE* s)
170 {
171         if(type==MD_MUSIC)
172                 type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE;
173         else
174           if(type==MD_SNDFX)
175                 type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE;
176
177         return md_driver->RealSampleLength(type,s);
178 }
179
180 MIKMODAPI CHAR* MikMod_InfoDriver(void)
181 {
182         int t;
183         size_t len=0;
184         MDRIVER *l;
185         CHAR *list=NULL;
186
187         MUTEX_LOCK(lists);
188         /* compute size of buffer */
189         for(l = firstdriver; l; l = l->next)
190                 len += 4 + (l->next ? 1 : 0) + strlen(l->Version);
191
192         if(len)
193           if((list=(CHAR*)MikMod_malloc(len*sizeof(CHAR))) != NULL) {
194                 CHAR *list_end = list;
195                 list[0] = 0;
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" : "");
199                 }
200         }
201         MUTEX_UNLOCK(lists);
202         return list;
203 }
204
205 void _mm_registerdriver(struct MDRIVER* drv)
206 {
207         MDRIVER *cruise = firstdriver;
208
209         /* don't register a MISSING() driver */
210         if ((drv->Name) && (drv->Version)) {
211                 if (cruise) {
212                         if ( cruise == drv )
213                                 return;
214                         while(cruise->next) {
215                                 cruise = cruise->next;
216                                 if ( cruise == drv )
217                                         return;
218                         }
219                         cruise->next = drv;
220                 } else
221                         firstdriver = drv;
222         }
223 }
224
225 MIKMODAPI void MikMod_RegisterDriver(struct MDRIVER* drv)
226 {
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))
230                 return;
231
232         MUTEX_LOCK(lists);
233         _mm_registerdriver(drv);
234         MUTEX_UNLOCK(lists);
235 }
236
237 MIKMODAPI int MikMod_DriverFromAlias(const CHAR *alias)
238 {
239         int rank=1;
240         MDRIVER *cruise;
241
242         MUTEX_LOCK(lists);
243         cruise=firstdriver;
244         while(cruise) {
245                 if (cruise->Alias) {
246                         if (!(_mm_strcasecmp(alias,cruise->Alias))) break;
247                         rank++;
248                 }
249                 cruise=cruise->next;
250         }
251         if(!cruise) rank=0;
252         MUTEX_UNLOCK(lists);
253
254         return rank;
255 }
256
257 MIKMODAPI MDRIVER *MikMod_DriverByOrdinal(int ordinal)
258 {
259         MDRIVER *cruise;
260
261         /* Allow only driver ordinals > 0 */
262         if (!ordinal) return NULL;
263
264         MUTEX_LOCK(lists);
265         cruise = firstdriver;
266         while (cruise && --ordinal)
267                 cruise = cruise->next;
268         MUTEX_UNLOCK(lists);
269         return cruise;
270 }
271
272 SWORD MD_SampleLoad(SAMPLOAD* s, int type)
273 {
274         SWORD result;
275
276         if(type==MD_MUSIC)
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;
280
281         SL_Init(s);
282         result=md_driver->SampleLoad(s,type);
283         SL_Exit(s);
284
285         return result;
286 }
287
288 void MD_SampleUnload(SWORD handle)
289 {
290         md_driver->SampleUnload(handle);
291 }
292
293 MIKMODAPI MikMod_player_t MikMod_RegisterPlayer(MikMod_player_t player)
294 {
295         MikMod_player_t result;
296
297         MUTEX_LOCK(vars);
298         result=md_player;
299         md_player=player;
300         MUTEX_UNLOCK(vars);
301
302         return result;
303 }
304
305 MIKMODAPI void MikMod_Update(void)
306 {
307         MUTEX_LOCK(vars);
308         if(isplaying) {
309                 if((!pf)||(!pf->forbid))
310                         md_driver->Update();
311                 else {
312                         if (md_driver->Pause)
313                                 md_driver->Pause();
314                 }
315         }
316         MUTEX_UNLOCK(vars);
317 }
318
319 void Voice_SetVolume_internal(SBYTE voice,UWORD vol)
320 {
321         ULONG  tmp;
322
323         if((voice<0)||(voice>=md_numchn)) return;
324
325         /* range checks */
326         if(md_musicvolume>128) md_musicvolume=128;
327         if(md_sndfxvolume>128) md_sndfxvolume=128;
328         if(md_volume>128) md_volume=128;
329
330         tmp=(ULONG)vol*(ULONG)md_volume*
331              ((voice<md_sngchn)?(ULONG)md_musicvolume:(ULONG)md_sndfxvolume);
332         md_driver->VoiceSetVolume(voice,tmp/16384UL);
333 }
334
335 MIKMODAPI void Voice_SetVolume(SBYTE voice,UWORD vol)
336 {
337         MUTEX_LOCK(vars);
338         Voice_SetVolume_internal(voice,vol);
339         MUTEX_UNLOCK(vars);
340 }
341
342 MIKMODAPI UWORD Voice_GetVolume(SBYTE voice)
343 {
344         UWORD result=0;
345
346         MUTEX_LOCK(vars);
347         if((voice>=0)&&(voice<md_numchn))
348                 result=md_driver->VoiceGetVolume(voice);
349         MUTEX_UNLOCK(vars);
350
351         return result;
352 }
353
354 void Voice_SetFrequency_internal(SBYTE voice,ULONG frq)
355 {
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);
360 }
361
362 MIKMODAPI void Voice_SetFrequency(SBYTE voice,ULONG frq)
363 {
364         MUTEX_LOCK(vars);
365         Voice_SetFrequency_internal(voice,frq);
366         MUTEX_UNLOCK(vars);
367 }
368
369 MIKMODAPI ULONG Voice_GetFrequency(SBYTE voice)
370 {
371         ULONG result=0;
372
373         MUTEX_LOCK(vars);
374         if((voice>=0)&&(voice<md_numchn))
375                 result=md_driver->VoiceGetFrequency(voice);
376         MUTEX_UNLOCK(vars);
377
378         return result;
379 }
380
381 void Voice_SetPanning_internal(SBYTE voice,ULONG pan)
382 {
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;
388         }
389         md_driver->VoiceSetPanning(voice, pan);
390 }
391
392 MIKMODAPI void Voice_SetPanning(SBYTE voice,ULONG pan)
393 {
394 #ifdef MIKMOD_DEBUG
395         if((pan!=PAN_SURROUND)&&((pan<0)||(pan>255)))
396                 fprintf(stderr,"\rVoice_SetPanning called with pan=%ld\n",(long)pan);
397 #endif
398
399         MUTEX_LOCK(vars);
400         Voice_SetPanning_internal(voice,pan);
401         MUTEX_UNLOCK(vars);
402 }
403
404 MIKMODAPI ULONG Voice_GetPanning(SBYTE voice)
405 {
406         ULONG result=PAN_CENTER;
407
408         MUTEX_LOCK(vars);
409         if((voice>=0)&&(voice<md_numchn))
410                 result=md_driver->VoiceGetPanning(voice);
411         MUTEX_UNLOCK(vars);
412
413         return result;
414 }
415
416 void Voice_Play_internal(SBYTE voice,SAMPLE* s,ULONG start)
417 {
418         ULONG  repend;
419
420         if((voice<0)||(voice>=md_numchn)) return;
421
422         md_sample[voice]=s;
423         repend=s->loopend;
424
425         if(s->flags&SF_LOOP)
426                 /* repend can't be bigger than size */
427                 if(repend>s->length) repend=s->length;
428
429         md_driver->VoicePlay(voice,s->handle,start,s->length,s->loopstart,repend,s->flags);
430 }
431
432 MIKMODAPI void Voice_Play(SBYTE voice,SAMPLE* s,ULONG start)
433 {
434         if(start>s->length) return;
435
436         MUTEX_LOCK(vars);
437         Voice_Play_internal(voice,s,start);
438         MUTEX_UNLOCK(vars);
439 }
440
441 void Voice_Stop_internal(SBYTE voice)
442 {
443         if((voice<0)||(voice>=md_numchn)) return;
444         if(voice>=md_sngchn)
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);
448 }
449
450 MIKMODAPI void Voice_Stop(SBYTE voice)
451 {
452         MUTEX_LOCK(vars);
453         Voice_Stop_internal(voice);
454         MUTEX_UNLOCK(vars);
455 }
456
457 BOOL Voice_Stopped_internal(SBYTE voice)
458 {
459         if((voice<0)||(voice>=md_numchn)) return 0;
460         return(md_driver->VoiceStopped(voice));
461 }
462
463 MIKMODAPI BOOL Voice_Stopped(SBYTE voice)
464 {
465         BOOL result;
466
467         MUTEX_LOCK(vars);
468         result=Voice_Stopped_internal(voice);
469         MUTEX_UNLOCK(vars);
470
471         return result;
472 }
473
474 MIKMODAPI SLONG Voice_GetPosition(SBYTE voice)
475 {
476         SLONG result=0;
477
478         MUTEX_LOCK(vars);
479         if((voice>=0)&&(voice<md_numchn)) {
480                 if (md_driver->VoiceGetPosition)
481                         result=(md_driver->VoiceGetPosition(voice));
482                 else
483                         result=-1;
484         }
485         MUTEX_UNLOCK(vars);
486
487         return result;
488 }
489
490 MIKMODAPI ULONG Voice_RealVolume(SBYTE voice)
491 {
492         ULONG result=0;
493
494         MUTEX_LOCK(vars);
495         if((voice>=0)&&(voice<md_numchn)&& md_driver->VoiceRealVolume)
496                 result=(md_driver->VoiceRealVolume(voice));
497         MUTEX_UNLOCK(vars);
498
499         return result;
500 }
501
502 MIKMODAPI void VC_SetCallback(MikMod_callback_t callback)
503 {
504         vc_callback = callback;
505 }
506
507 static int _mm_init(const CHAR *cmdline)
508 {
509         UWORD t;
510
511         _mm_critical = 1;
512
513         /* if md_device==0, try to find a device number */
514         if(!md_device) {
515                 cmdline=NULL;
516
517                 for(t=1,md_driver=firstdriver;md_driver;md_driver=md_driver->next,t++)
518                         if(md_driver->IsPresent()) break;
519
520                 if(!md_driver) {
521                         _mm_errno = MMERR_DETECTING_DEVICE;
522                         if(_mm_errorhandler) _mm_errorhandler();
523                         md_driver = &drv_nos;
524                         return 1;
525                 }
526
527                 md_device = t;
528         } else {
529                 /* if n>0, use that driver */
530                 for(t=1,md_driver=firstdriver;(md_driver)&&(t!=md_device);md_driver=md_driver->next)
531                         t++;
532
533                 if(!md_driver) {
534                         _mm_errno = MMERR_INVALID_DEVICE;
535                         if(_mm_errorhandler) _mm_errorhandler();
536                         md_driver = &drv_nos;
537                         return 1;
538                 }
539
540                 /* arguments here might be necessary for the presence check to succeed */
541                 if(cmdline&&(md_driver->CommandLine))
542                         md_driver->CommandLine(cmdline);
543
544                 if(!md_driver->IsPresent()) {
545                         _mm_errno = MMERR_DETECTING_DEVICE;
546                         if(_mm_errorhandler) _mm_errorhandler();
547                         md_driver = &drv_nos;
548                         return 1;
549                 }
550         }
551
552         olddevice = md_device;
553         if(md_driver->Init()) {
554                 MikMod_Exit_internal();
555                 if(_mm_errorhandler) _mm_errorhandler();
556                 return 1;
557         }
558
559         initialized=1;
560         _mm_critical=0;
561
562         return 0;
563 }
564
565 MIKMODAPI int MikMod_Init(const CHAR *cmdline)
566 {
567         int result;
568
569         MUTEX_LOCK(vars);
570         MUTEX_LOCK(lists);
571         result=_mm_init(cmdline);
572         MUTEX_UNLOCK(lists);
573         MUTEX_UNLOCK(vars);
574
575         return result;
576 }
577
578 void MikMod_Exit_internal(void)
579 {
580         MikMod_DisableOutput_internal();
581         md_driver->Exit();
582         md_numchn = md_sfxchn = md_sngchn = 0;
583         md_driver = &drv_nos;
584
585         MikMod_free(sfxinfo);
586         MikMod_free(md_sample);
587         md_sample  = NULL;
588         sfxinfo    = NULL;
589
590         initialized = 0;
591 }
592
593 MIKMODAPI void MikMod_Exit(void)
594 {
595         MUTEX_LOCK(vars);
596         MUTEX_LOCK(lists);
597         MikMod_Exit_internal();
598         MUTEX_UNLOCK(lists);
599         MUTEX_UNLOCK(vars);
600 }
601
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)
605 {
606         BOOL wasplaying = 0;
607
608         if(!initialized) return _mm_init(cmdline);
609
610         if (isplaying) {
611                 wasplaying = 1;
612                 md_driver->PlayStop();
613         }
614
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. */
618                 md_driver->Exit();
619                 if(_mm_init(cmdline)) {
620                         MikMod_Exit_internal();
621                         if(_mm_errno)
622                                 if(_mm_errorhandler) _mm_errorhandler();
623                         return 1;
624                 }
625         } else {
626                 if(md_driver->Reset()) {
627                         MikMod_Exit_internal();
628                         if(_mm_errno)
629                                 if(_mm_errorhandler) _mm_errorhandler();
630                         return 1;
631                 }
632         }
633
634         if (wasplaying) return md_driver->PlayStart();
635         return 0;
636 }
637
638 MIKMODAPI int MikMod_Reset(const CHAR *cmdline)
639 {
640         int result;
641
642         MUTEX_LOCK(vars);
643         MUTEX_LOCK(lists);
644         result=_mm_reset(cmdline);
645         MUTEX_UNLOCK(lists);
646         MUTEX_UNLOCK(vars);
647
648         return result;
649 }
650
651 /* If either parameter is -1, the current set value will be retained. */
652 int MikMod_SetNumVoices_internal(int music, int sfx)
653 {
654         BOOL resume = 0;
655         int t, oldchn = 0;
656
657         if((!music)&&(!sfx)) return 1;
658         _mm_critical = 1;
659         if(isplaying) {
660                 MikMod_DisableOutput_internal();
661                 oldchn = md_numchn;
662                 resume = 1;
663         }
664
665         MikMod_free(sfxinfo);
666         MikMod_free(md_sample);
667         md_sample  = NULL;
668         sfxinfo    = NULL;
669
670         if(music!=-1) md_sngchn = music;
671         if(sfx!=-1)   md_sfxchn = sfx;
672         md_numchn = md_sngchn + md_sfxchn;
673
674         LimitHardVoices(md_driver->HardVoiceLimit);
675         LimitSoftVoices(md_driver->SoftVoiceLimit);
676
677         if(md_driver->SetNumVoices()) {
678                 MikMod_Exit_internal();
679                 if(_mm_errno)
680                         if(_mm_errorhandler!=NULL) _mm_errorhandler();
681                 md_numchn = md_softchn = md_hardchn = md_sfxchn = md_sngchn = 0;
682                 return 1;
683         }
684
685         if(md_sngchn+md_sfxchn)
686                 md_sample=(SAMPLE**)MikMod_calloc(md_sngchn+md_sfxchn,sizeof(SAMPLE*));
687         if(md_sfxchn)
688                 sfxinfo = (UBYTE *)MikMod_calloc(md_sfxchn,sizeof(UBYTE));
689
690         /* make sure the player doesn't start with garbage */
691         for(t=oldchn;t<md_numchn;t++)  Voice_Stop_internal(t);
692
693         sfxpool = 0;
694         if(resume) MikMod_EnableOutput_internal();
695         _mm_critical = 0;
696
697         return 0;
698 }
699
700 MIKMODAPI int MikMod_SetNumVoices(int music, int sfx)
701 {
702         int result;
703
704         MUTEX_LOCK(vars);
705         result=MikMod_SetNumVoices_internal(music,sfx);
706         MUTEX_UNLOCK(vars);
707
708         return result;
709 }
710
711 int MikMod_EnableOutput_internal(void)
712 {
713         _mm_critical = 1;
714         if(!isplaying) {
715                 if(md_driver->PlayStart()) return 1;
716                 isplaying = 1;
717         }
718         _mm_critical = 0;
719         return 0;
720 }
721
722 MIKMODAPI int MikMod_EnableOutput(void)
723 {
724         int result;
725
726         MUTEX_LOCK(vars);
727         result=MikMod_EnableOutput_internal();
728         MUTEX_UNLOCK(vars);
729
730         return result;
731 }
732
733 void MikMod_DisableOutput_internal(void)
734 {
735         if(isplaying && md_driver) {
736                 isplaying = 0;
737                 md_driver->PlayStop();
738         }
739 }
740
741 MIKMODAPI void MikMod_DisableOutput(void)
742 {
743         MUTEX_LOCK(vars);
744         MikMod_DisableOutput_internal();
745         MUTEX_UNLOCK(vars);
746 }
747
748 BOOL MikMod_Active_internal(void)
749 {
750         return isplaying;
751 }
752
753 MIKMODAPI BOOL MikMod_Active(void)
754 {
755         BOOL result;
756
757         MUTEX_LOCK(vars);
758         result=MikMod_Active_internal();
759         MUTEX_UNLOCK(vars);
760
761         return result;
762 }
763
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
766    criticals).
767
768    Returns the voice that the sound is being played on. */
769 static SBYTE Sample_Play_internal(SAMPLE *s,ULONG start,UBYTE flags)
770 {
771         int orig=sfxpool;/* for cases where all channels are critical */
772         int c;
773
774         if(!md_sfxchn) return -1;
775         if(s->volume>64) s->volume = 64;
776
777         /* check the first location after sfxpool */
778         do {
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);
786                                 sfxpool++;
787                                 if(sfxpool>=md_sfxchn) sfxpool=0;
788                                 return c;
789                         }
790                 } else {
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);
796                         sfxpool++;
797                         if(sfxpool>=md_sfxchn) sfxpool=0;
798                         return c;
799                 }
800
801                 sfxpool++;
802                 if(sfxpool>=md_sfxchn) sfxpool = 0;
803         } while(sfxpool!=orig);
804
805         return -1;
806 }
807
808 MIKMODAPI SBYTE Sample_Play(SAMPLE *s,ULONG start,UBYTE flags)
809 {
810         SBYTE result;
811
812         MUTEX_LOCK(vars);
813         result=Sample_Play_internal(s,start,flags);
814         MUTEX_UNLOCK(vars);
815
816         return result;
817 }
818
819 MIKMODAPI long MikMod_GetVersion(void)
820 {
821         return LIBMIKMOD_VERSION;
822 }
823
824 /*========== MT-safe stuff */
825
826 #ifdef HAVE_PTHREAD
827 #define INIT_MUTEX(name) \
828         pthread_mutex_t _mm_mutex_##name=PTHREAD_MUTEX_INITIALIZER
829
830 #elif defined(__OS2__)||defined(__EMX__)
831 #define INIT_MUTEX(name) \
832         HMTX _mm_mutex_##name
833
834 #elif defined(_WIN32)
835 #define INIT_MUTEX(name) \
836         HANDLE _mm_mutex_##name
837
838 #else
839 #define INIT_MUTEX(name) \
840         void *_mm_mutex_##name = NULL
841 #endif
842
843 INIT_MUTEX(vars);
844 INIT_MUTEX(lists);
845
846 MIKMODAPI BOOL MikMod_InitThreads(void)
847 {
848         static int firstcall=1;
849         static BOOL result = 0;
850
851         if (firstcall) {
852                 firstcall=0;
853 #ifdef HAVE_PTHREAD
854                 result=1;
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;
859                         result=0;
860                 } else
861                         result=1;
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)")))))
865                         result=0;
866                 else
867                         result=1;
868 #endif
869         }
870         return result;
871 }
872
873 MIKMODAPI void MikMod_Unlock(void)
874 {
875         MUTEX_UNLOCK(lists);
876         MUTEX_UNLOCK(vars);
877 }
878
879 MIKMODAPI void MikMod_Lock(void)
880 {
881         MUTEX_LOCK(vars);
882         MUTEX_LOCK(lists);
883 }
884
885 /*========== Parameter extraction helper */
886
887 CHAR *MD_GetAtom(const CHAR *atomname, const CHAR *cmdline, BOOL implicit)
888 {
889         CHAR *ret=NULL;
890
891         if(cmdline) {
892                 const CHAR *buf=strstr(cmdline,atomname);
893
894                 if((buf)&&((buf==cmdline)||(*(buf-1)==','))) {
895                         const CHAR *ptr=buf+strlen(atomname);
896
897                         if(*ptr=='=') {
898                                 for(buf=++ptr;(*ptr)&&((*ptr)!=',');ptr++);
899                                 ret=(CHAR *)MikMod_malloc((1+ptr-buf)*sizeof(CHAR));
900                                 if(ret)
901                                         strncpy(ret,buf,ptr-buf);
902                         } else if((*ptr==',')||(!*ptr)) {
903                                 if(implicit) {
904                                         ret=(CHAR *)MikMod_malloc((1+ptr-buf)*sizeof(CHAR));
905                                         if(ret)
906                                                 strncpy(ret,buf,ptr-buf);
907                                 }
908                         }
909                 }
910         }
911         return ret;
912 }
913
914 #if (MIKMOD_UNIX)
915
916 /*========== Posix helper functions */
917
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)
924 {
925         struct stat buf;
926
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;
937                 } else
938                         if(!(buf.st_mode&S_IWOTH)) return 0;
939         }
940
941         return 1;
942 }
943
944 /* Drop all root privileges we might have */
945 int MD_DropPrivileges(void)
946 {
947         if(!geteuid()) {
948                 if(getuid()) {
949                         /* we are setuid root -> drop setuid to become the real user */
950                         if(setuid(getuid())) return 1;
951                 } else {
952                         /* we are run as root -> drop all and become user 'nobody' */
953                         struct passwd *nobody;
954                         int uid;
955
956                         if(!(nobody=getpwnam("nobody"))) return 1; /* no such user ? */
957                         uid=nobody->pw_uid;
958                         if (!uid) /* user 'nobody' has root privileges ? weird... */
959                                 return 1;
960                         if (setuid(uid)) return 1;
961                 }
962         }
963         return 0;
964 }
965
966 #endif
967
968 /* ex:set ts=8: */