Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / playercode / mdriver.c
1 /*      MikMod sound library\r
2         (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS\r
3         for complete list.\r
4 \r
5         This library is free software; you can redistribute it and/or modify\r
6         it under the terms of the GNU Library General Public License as\r
7         published by the Free Software Foundation; either version 2 of\r
8         the License, or (at your option) any later version.\r
9  \r
10         This program is distributed in the hope that it will be useful,\r
11         but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13         GNU Library General Public License for more details.\r
14  \r
15         You should have received a copy of the GNU Library General Public\r
16         License along with this library; if not, write to the Free Software\r
17         Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\r
18         02111-1307, USA.\r
19 */\r
20 \r
21 /*==============================================================================\r
22 \r
23   $Id: mdriver.c,v 1.3 2004/02/18 13:29:19 raph Exp $\r
24 \r
25   These routines are used to access the available soundcard drivers.\r
26 \r
27 ==============================================================================*/\r
28 \r
29 #ifdef HAVE_CONFIG_H\r
30 #include "config.h"\r
31 #endif\r
32 \r
33 #ifdef HAVE_UNISTD_H\r
34 #include <unistd.h>\r
35 #endif\r
36 \r
37 #if defined unix || (defined __APPLE__ && defined __MACH__)\r
38 #include <pwd.h>\r
39 #include <sys/stat.h>\r
40 #endif\r
41 \r
42 #include <string.h>\r
43 #ifdef HAVE_STRINGS_H\r
44 #include <strings.h>\r
45 #endif\r
46 \r
47 #include "mikmod_internals.h"\r
48 \r
49 #ifdef SUNOS\r
50 extern int fprintf(FILE *, const char *, ...);\r
51 #endif\r
52 \r
53 static  MDRIVER *firstdriver=NULL;\r
54 \r
55 extern  MODULE *pf; /* modfile being played */\r
56 \r
57 /* Initial global settings */\r
58 MIKMODAPI       UWORD md_device         = 0;    /* autodetect */\r
59 MIKMODAPI       UWORD md_mixfreq        = 44100;\r
60 MIKMODAPI       UWORD md_mode           = DMODE_STEREO | DMODE_16BITS |\r
61                                                                           DMODE_SURROUND |DMODE_SOFT_MUSIC |\r
62                                                                           DMODE_SOFT_SNDFX;\r
63 MIKMODAPI       UBYTE md_pansep         = 128;  /* 128 == 100% (full left/right) */\r
64 MIKMODAPI       UBYTE md_reverb         = 0;    /* no reverb */\r
65 MIKMODAPI       UBYTE md_volume         = 128;  /* global sound volume (0-128) */\r
66 MIKMODAPI       UBYTE md_musicvolume    = 128;  /* volume of song */\r
67 MIKMODAPI       UBYTE md_sndfxvolume    = 128;  /* volume of sound effects */\r
68                         UWORD md_bpm            = 125;  /* tempo */\r
69 \r
70 /* Do not modify the numchn variables yourself!  use MD_SetVoices() */\r
71                         UBYTE md_numchn=0,md_sngchn=0,md_sfxchn=0;\r
72                         UBYTE md_hardchn=0,md_softchn=0;\r
73 \r
74                         void (*md_player)(void) = Player_HandleTick;\r
75 static          BOOL  isplaying=0, initialized = 0;\r
76 static          UBYTE *sfxinfo;\r
77 static          int sfxpool;\r
78 \r
79 static          SAMPLE **md_sample = NULL;\r
80 \r
81 /* Previous driver in use *\r
82 static          SWORD olddevice = -1;\r
83 */\r
84 /* Limits the number of hardware voices to the specified amount.\r
85    This function should only be used by the low-level drivers. */\r
86 static  void LimitHardVoices(int limit)\r
87 {\r
88         int t=0;\r
89 \r
90         if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>limit)) md_sfxchn=limit;\r
91         if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>limit)) md_sngchn=limit;\r
92 \r
93         if (!(md_mode & DMODE_SOFT_SNDFX))\r
94                 md_hardchn=md_sfxchn;\r
95         else\r
96                 md_hardchn=0;\r
97 \r
98         if (!(md_mode & DMODE_SOFT_MUSIC)) md_hardchn += md_sngchn;\r
99 \r
100         while (md_hardchn>limit) {\r
101                 if (++t & 1) {\r
102                         if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>4)) md_sfxchn--;\r
103                 } else {\r
104                         if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>8)) md_sngchn--;\r
105                 }\r
106 \r
107                 if (!(md_mode & DMODE_SOFT_SNDFX))\r
108                         md_hardchn=md_sfxchn;\r
109                 else\r
110                         md_hardchn=0;\r
111 \r
112                 if (!(md_mode & DMODE_SOFT_MUSIC))\r
113                         md_hardchn+=md_sngchn;\r
114         }\r
115         md_numchn=md_hardchn+md_softchn;\r
116 }\r
117 \r
118 /* Limits the number of hardware voices to the specified amount.\r
119    This function should only be used by the low-level drivers. */\r
120 static  void LimitSoftVoices(int limit)\r
121 {\r
122         int t=0;\r
123 \r
124         if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>limit)) md_sfxchn=limit;\r
125         if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>limit)) md_sngchn=limit;\r
126 \r
127         if (md_mode & DMODE_SOFT_SNDFX)\r
128                 md_softchn=md_sfxchn;\r
129         else\r
130                 md_softchn=0;\r
131 \r
132         if (md_mode & DMODE_SOFT_MUSIC) md_softchn+=md_sngchn;\r
133 \r
134         while (md_softchn>limit) {\r
135                 if (++t & 1) {\r
136                         if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>4)) md_sfxchn--;\r
137                 } else {\r
138                         if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>8)) md_sngchn--;\r
139                 }\r
140 \r
141                 if (!(md_mode & DMODE_SOFT_SNDFX))\r
142                         md_softchn=md_sfxchn;\r
143                 else\r
144                         md_softchn=0;\r
145 \r
146                 if (!(md_mode & DMODE_SOFT_MUSIC))\r
147                         md_softchn+=md_sngchn;\r
148         }\r
149         md_numchn=md_hardchn+md_softchn;\r
150 }\r
151 \r
152 /* Note: 'type' indicates whether the returned value should be for music or for\r
153    sound effects. */\r
154 ULONG MD_SampleSpace(int type)\r
155 {\r
156         if(type==MD_MUSIC)\r
157                 type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE;\r
158         else if(type==MD_SNDFX)\r
159                 type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE;\r
160 \r
161         return VC_SampleSpace(type);\r
162 }\r
163 \r
164 ULONG MD_SampleLength(int type,SAMPLE* s)\r
165 {\r
166         if(type==MD_MUSIC)\r
167                 type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE;\r
168         else\r
169           if(type==MD_SNDFX)\r
170                 type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE;\r
171 \r
172         return VC_SampleLength(type,s);\r
173 }\r
174 /*\r
175 MIKMODAPI CHAR* MikMod_InfoDriver(void)\r
176 {\r
177         int t,len=0;\r
178         MDRIVER *l;\r
179         CHAR *list=NULL;\r
180 \r
181         MUTEX_LOCK(lists);\r
182         /* compute size of buffer *\r
183         for(l=firstdriver;l;l=l->next)\r
184                 len+=4+(l->next?1:0)+strlen(l->Version);\r
185 \r
186         if(len)\r
187                 if((list=_mm_malloc(len*sizeof(CHAR)))) {\r
188                         list[0]=0;\r
189                         /* list all registered device drivers : *\r
190                         for(t=1,l=firstdriver;l;l=l->next,t++)\r
191                                 sprintf(list,(l->next)?"%s%2d %s\n":"%s%2d %s",\r
192                                     list,t,l->Version);\r
193                 }\r
194         MUTEX_UNLOCK(lists);\r
195         return list;\r
196 }\r
197 */\r
198 void _mm_registerdriver(struct MDRIVER* drv)\r
199 {\r
200         MDRIVER *cruise = firstdriver;\r
201 \r
202         /* don't register a MISSING() driver */\r
203         if ((drv->Name) && (drv->Version)) {\r
204                 if (cruise) {\r
205                         while (cruise->next) cruise = cruise->next;\r
206                         cruise->next = drv;\r
207                 } else\r
208                         firstdriver = drv; \r
209         }\r
210 }\r
211 \r
212 MIKMODAPI void MikMod_RegisterDriver(struct MDRIVER* drv)\r
213 {\r
214         /* if we try to register an invalid driver, or an already registered driver,\r
215            ignore this attempt */\r
216         if ((!drv)||(drv->next)||(!drv->Name))\r
217                 return;\r
218 \r
219         MUTEX_LOCK(lists);\r
220         _mm_registerdriver(drv);\r
221         MUTEX_UNLOCK(lists);\r
222 }\r
223 \r
224 MIKMODAPI int MikMod_DriverFromAlias(CHAR *alias)\r
225 {\r
226         int rank=1;\r
227         MDRIVER *cruise;\r
228 \r
229         MUTEX_LOCK(lists);\r
230         cruise=firstdriver;\r
231         while(cruise) {\r
232                 if (cruise->Alias) {\r
233                         if (!(strcasecmp(alias,cruise->Alias))) break;\r
234                         rank++;\r
235                 }\r
236                 cruise=cruise->next;\r
237         }\r
238         if(!cruise) rank=0;\r
239         MUTEX_UNLOCK(lists);\r
240 \r
241         return rank;\r
242 }\r
243 \r
244 MIKMODAPI MDRIVER *MikMod_DriverByOrdinal(int ordinal)\r
245 {\r
246         MDRIVER *cruise;\r
247 \r
248         /* Allow only driver ordinals > 0 */\r
249         if (!ordinal)\r
250                 return 0;\r
251 \r
252         MUTEX_LOCK(lists);\r
253         cruise = firstdriver;\r
254         while (cruise && --ordinal)\r
255                 cruise = cruise->next;\r
256         MUTEX_UNLOCK(lists);\r
257         return cruise;\r
258 }\r
259 \r
260 SWORD MD_SampleLoad(SAMPLOAD* s, int type)\r
261 {\r
262         SWORD result;\r
263 \r
264         if(type==MD_MUSIC)\r
265                 type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE;\r
266         else if(type==MD_SNDFX)\r
267                 type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE;\r
268 \r
269         SL_Init(s);\r
270         result=VC_SampleLoad(s,type);\r
271         SL_Exit(s);\r
272 \r
273         return result;\r
274 }\r
275 \r
276 void MD_SampleUnload(SWORD handle)\r
277 {\r
278         VC_SampleUnload(handle);\r
279 }\r
280 \r
281 MIKMODAPI MikMod_player_t MikMod_RegisterPlayer(MikMod_player_t player)\r
282 {\r
283         MikMod_player_t result;\r
284 \r
285         MUTEX_LOCK(vars);\r
286         result=md_player;\r
287         md_player=player;\r
288         MUTEX_UNLOCK(vars);\r
289 \r
290         return result;\r
291 }\r
292 \r
293 MIKMODAPI void MikMod_Update(void)\r
294 {\r
295         MUTEX_LOCK(vars);\r
296         /* FIXME - if I'm broken...\r
297         if(isplaying)\r
298         {\r
299                 if((!pf)||(!pf->forbid))\r
300                 {\r
301                         md_driver->Update();\r
302                 }\r
303                 else\r
304                 {\r
305                         if (md_driver->Pause)\r
306                                 md_driver->Pause();\r
307                 }\r
308         }\r
309         */\r
310         MUTEX_UNLOCK(vars);\r
311 }\r
312 \r
313 void Voice_SetVolume_internal(SBYTE voice,UWORD vol)\r
314 {\r
315         ULONG  tmp;\r
316 \r
317         if((voice<0)||(voice>=md_numchn)) return;\r
318 \r
319         /* range checks */\r
320         if(md_musicvolume>128) md_musicvolume=128;\r
321         if(md_sndfxvolume>128) md_sndfxvolume=128;\r
322         if(md_volume>128) md_volume=128;\r
323 \r
324         tmp=(ULONG)vol*(ULONG)md_volume*\r
325              ((voice<md_sngchn)?(ULONG)md_musicvolume:(ULONG)md_sndfxvolume);\r
326         VC_VoiceSetVolume(voice,tmp/16384UL);\r
327 }\r
328 \r
329 MIKMODAPI void Voice_SetVolume(SBYTE voice,UWORD vol)\r
330 {\r
331         MUTEX_LOCK(vars);\r
332         Voice_SetVolume_internal(voice,vol);\r
333         MUTEX_UNLOCK(vars);\r
334 }\r
335 \r
336 MIKMODAPI UWORD Voice_GetVolume(SBYTE voice)\r
337 {\r
338         UWORD result=0;\r
339 \r
340         MUTEX_LOCK(vars);\r
341         if((voice>=0)&&(voice<md_numchn))\r
342                 result=VC_VoiceGetVolume(voice);\r
343         MUTEX_UNLOCK(vars);\r
344 \r
345         return result;\r
346 }\r
347 \r
348 void Voice_SetFrequency_internal(SBYTE voice,ULONG frq)\r
349 {\r
350         if((voice<0)||(voice>=md_numchn)) return;\r
351         if((md_sample[voice])&&(md_sample[voice]->divfactor))\r
352                 frq/=md_sample[voice]->divfactor;\r
353         VC_VoiceSetFrequency(voice,frq);\r
354 }\r
355 \r
356 MIKMODAPI void Voice_SetFrequency(SBYTE voice,ULONG frq)\r
357 {\r
358         MUTEX_LOCK(vars);\r
359         Voice_SetFrequency_internal(voice,frq);\r
360         MUTEX_UNLOCK(vars);\r
361 }\r
362 \r
363 MIKMODAPI ULONG Voice_GetFrequency(SBYTE voice)\r
364 {\r
365         ULONG result=0;\r
366 \r
367         MUTEX_LOCK(vars);\r
368         if((voice>=0)&&(voice<md_numchn))\r
369                 result=VC_VoiceGetFrequency(voice);\r
370         MUTEX_UNLOCK(vars);\r
371 \r
372         return result;\r
373 }\r
374 \r
375 void Voice_SetPanning_internal(SBYTE voice,ULONG pan)\r
376 {\r
377         if((voice<0)||(voice>=md_numchn)) return;\r
378         if(pan!=PAN_SURROUND) {\r
379                 if(md_pansep>128) md_pansep=128;\r
380                 if(md_mode & DMODE_REVERSE) pan=255-pan;\r
381                 pan = (((SWORD)(pan-128)*md_pansep)/128)+128;\r
382         }\r
383         VC_VoiceSetPanning(voice, pan);\r
384 }\r
385 \r
386 MIKMODAPI void Voice_SetPanning(SBYTE voice,ULONG pan)\r
387 {\r
388 #ifdef MIKMOD_DEBUG\r
389         if((pan!=PAN_SURROUND)&&((pan<0)||(pan>255)))\r
390                 fprintf(stderr,"\rVoice_SetPanning called with pan=%ld\n",(long)pan);\r
391 #endif\r
392 \r
393         MUTEX_LOCK(vars);\r
394         Voice_SetPanning_internal(voice,pan);\r
395         MUTEX_UNLOCK(vars);\r
396 }\r
397 \r
398 MIKMODAPI ULONG Voice_GetPanning(SBYTE voice)\r
399 {\r
400         ULONG result=PAN_CENTER;\r
401 \r
402         MUTEX_LOCK(vars);\r
403         if((voice>=0)&&(voice<md_numchn))\r
404                 result=VC_VoiceGetPanning(voice);\r
405         MUTEX_UNLOCK(vars);\r
406 \r
407         return result;\r
408 }\r
409 \r
410 void Voice_Play_internal(SBYTE voice,SAMPLE* s,ULONG start)\r
411 {\r
412         ULONG  repend;\r
413 \r
414         if((voice<0)||(voice>=md_numchn)) return;\r
415 \r
416         md_sample[voice]=s;\r
417         repend=s->loopend;\r
418 \r
419         if(s->flags&SF_LOOP)\r
420                 /* repend can't be bigger than size */\r
421                 if(repend>s->length) repend=s->length;\r
422 \r
423         VC_VoicePlay(voice,s->handle,start,s->length,s->loopstart,repend,s->flags);\r
424 }\r
425 \r
426 MIKMODAPI void Voice_Play(SBYTE voice,SAMPLE* s,ULONG start)\r
427 {\r
428         if(start>s->length) return;\r
429 \r
430         MUTEX_LOCK(vars);\r
431         Voice_Play_internal(voice,s,start);\r
432         MUTEX_UNLOCK(vars);\r
433 }\r
434 \r
435 void Voice_Stop_internal(SBYTE voice)\r
436 {\r
437         if((voice<0)||(voice>=md_numchn)) return;\r
438         if(voice>=md_sngchn)\r
439                 /* It is a sound effects channel, so flag the voice as non-critical! */\r
440                 sfxinfo[voice-md_sngchn]=0;\r
441         VC_VoiceStop(voice);\r
442 }\r
443 \r
444 MIKMODAPI void Voice_Stop(SBYTE voice)\r
445 {\r
446         MUTEX_LOCK(vars);\r
447         Voice_Stop_internal(voice);\r
448         MUTEX_UNLOCK(vars);\r
449 }\r
450 \r
451 BOOL Voice_Stopped_internal(SBYTE voice)\r
452 {\r
453         if((voice<0)||(voice>=md_numchn)) return 0;\r
454         return(VC_VoiceStopped(voice));\r
455 }\r
456 \r
457 MIKMODAPI BOOL Voice_Stopped(SBYTE voice)\r
458 {\r
459         BOOL result;\r
460 \r
461         MUTEX_LOCK(vars);\r
462         result=Voice_Stopped_internal(voice);\r
463         MUTEX_UNLOCK(vars);\r
464 \r
465         return result;\r
466 }\r
467 \r
468 MIKMODAPI SLONG Voice_GetPosition(SBYTE voice)\r
469 {\r
470         SLONG result=0;\r
471 \r
472         MUTEX_LOCK(vars);\r
473         if((voice>=0)&&(voice<md_numchn)) {\r
474                 /*if (VC_VoiceGetPosition)*/\r
475                         result=(VC_VoiceGetPosition(voice));\r
476                 /*else\r
477                         result=-1;*/\r
478         }\r
479         MUTEX_UNLOCK(vars);\r
480 \r
481         return result;\r
482 }\r
483 \r
484 MIKMODAPI ULONG Voice_RealVolume(SBYTE voice)\r
485 {\r
486         ULONG result=0;\r
487 \r
488         MUTEX_LOCK(vars);\r
489         if((voice>=0)&&(voice<md_numchn)/*&& VC_VoiceRealVolume*/)\r
490                 result=(VC_VoiceRealVolume(voice));\r
491         MUTEX_UNLOCK(vars);\r
492 \r
493         return result;\r
494 }\r
495 \r
496 static BOOL _mm_init(CHAR *cmdline)\r
497 {\r
498         /* UWORD t; */\r
499         (void)cmdline;\r
500 \r
501         _mm_critical = 1;\r
502 /*\r
503         /* if md_device==0, try to find a device number *\r
504         if(!md_device) {\r
505                 cmdline=NULL;\r
506 \r
507                 for(t=1,md_driver=firstdriver;md_driver;md_driver=md_driver->next,t++)\r
508                         if(md_driver->IsPresent()) break;\r
509 \r
510                 if(!md_driver) {\r
511                         _mm_errno = MMERR_DETECTING_DEVICE;\r
512                         if(_mm_errorhandler) _mm_errorhandler();\r
513                         md_driver = &drv_nos;\r
514                         return 1;\r
515                 }\r
516 \r
517                 md_device = t;\r
518         } else {\r
519                 /* if n>0, use that driver *\r
520                 for(t=1,md_driver=firstdriver;(md_driver)&&(t!=md_device);md_driver=md_driver->next)\r
521                         t++;\r
522 \r
523                 if(!md_driver) {\r
524                         _mm_errno = MMERR_INVALID_DEVICE;\r
525                         if(_mm_errorhandler) _mm_errorhandler();\r
526                         md_driver = &drv_nos;\r
527                         return 1;\r
528                 }\r
529 \r
530                 /* arguments here might be necessary for the presence check to succeed *\r
531                 if(cmdline&&(md_driver->CommandLine))\r
532                         md_driver->CommandLine(cmdline);\r
533 \r
534                 if(!md_driver->IsPresent()) {\r
535                         _mm_errno = MMERR_DETECTING_DEVICE;\r
536                         if(_mm_errorhandler) _mm_errorhandler();\r
537                         md_driver = &drv_nos;\r
538                         return 1;\r
539                 }\r
540         }\r
541 \r
542         olddevice = md_device;\r
543 */\r
544         md_mode |= DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX;\r
545 \r
546         if(VC_Init()) {\r
547                 MikMod_Exit_internal();\r
548                 if(_mm_errorhandler) _mm_errorhandler();\r
549                 return 1;\r
550         }\r
551 \r
552         initialized=1;\r
553         _mm_critical=0;\r
554 \r
555         return 0;\r
556 }\r
557 \r
558 MIKMODAPI BOOL MikMod_Init(CHAR *cmdline)\r
559 {\r
560         BOOL result;\r
561 \r
562         MUTEX_LOCK(vars);\r
563         MUTEX_LOCK(lists);\r
564         result=_mm_init(cmdline);\r
565         MUTEX_UNLOCK(lists);\r
566         MUTEX_UNLOCK(vars);\r
567 \r
568         return result;\r
569 }\r
570 \r
571 void MikMod_Exit_internal(void)\r
572 {\r
573         MikMod_DisableOutput_internal();\r
574         VC_Exit();\r
575         md_numchn = md_sfxchn = md_sngchn = 0;\r
576         //md_driver = &drv_nos;\r
577 \r
578         if(sfxinfo) free(sfxinfo);\r
579         if(md_sample) free(md_sample);\r
580         md_sample  = NULL;\r
581         sfxinfo    = NULL;\r
582 \r
583         initialized = 0;\r
584 }\r
585 \r
586 MIKMODAPI void MikMod_Exit(void)\r
587 {\r
588         MUTEX_LOCK(vars);\r
589         MUTEX_LOCK(lists);\r
590         MikMod_Exit_internal();\r
591         MUTEX_UNLOCK(lists);\r
592         MUTEX_UNLOCK(vars);\r
593 }\r
594 \r
595 /* Reset the driver using the new global variable settings. \r
596    If the driver has not been initialized, it will be now. */\r
597 static BOOL _mm_reset(CHAR *cmdline)\r
598 {\r
599         BOOL wasplaying = 0;\r
600 \r
601         if(!initialized) return _mm_init(cmdline);\r
602         \r
603         if (isplaying) {\r
604                 wasplaying = 1;\r
605                 VC_PlayStop();\r
606         }\r
607 /*\r
608         if((!md_driver->Reset)||(md_device != olddevice)) {\r
609                 /* md_driver->Reset was NULL, or md_device was changed, so do a full\r
610                    reset of the driver. *\r
611                 md_driver->Exit();\r
612                 if(_mm_init(cmdline)) {\r
613                         MikMod_Exit_internal();\r
614                         if(_mm_errno)\r
615                                 if(_mm_errorhandler) _mm_errorhandler();\r
616                         return 1;\r
617                 }\r
618         } else {\r
619                 if(md_driver->Reset()) {\r
620                         MikMod_Exit_internal();\r
621                         if(_mm_errno)\r
622                                 if(_mm_errorhandler) _mm_errorhandler();\r
623                         return 1;\r
624                 }\r
625         }\r
626 */\r
627         if (wasplaying) VC_PlayStart();\r
628         return 0;\r
629 }\r
630 \r
631 MIKMODAPI BOOL MikMod_Reset(CHAR *cmdline)\r
632 {\r
633         BOOL result;\r
634 \r
635         MUTEX_LOCK(vars);\r
636         MUTEX_LOCK(lists);\r
637         result=_mm_reset(cmdline);\r
638         MUTEX_UNLOCK(lists);\r
639         MUTEX_UNLOCK(vars);\r
640 \r
641         return result;\r
642 }\r
643 \r
644 /* If either parameter is -1, the current set value will be retained. */\r
645 BOOL MikMod_SetNumVoices_internal(int music, int sfx)\r
646 {\r
647         BOOL resume = 0;\r
648         int t, oldchn = 0;\r
649 \r
650         if((!music)&&(!sfx)) return 1;\r
651         _mm_critical = 1;\r
652         if(isplaying) {\r
653                 MikMod_DisableOutput_internal();\r
654                 oldchn = md_numchn;\r
655                 resume = 1;\r
656         }\r
657 \r
658         if(sfxinfo) free(sfxinfo);\r
659         if(md_sample) free(md_sample);\r
660         md_sample  = NULL;\r
661         sfxinfo    = NULL;\r
662 \r
663         if(music!=-1) md_sngchn = music;\r
664         if(sfx!=-1)   md_sfxchn = sfx;\r
665         md_numchn = md_sngchn + md_sfxchn;\r
666 /*\r
667         LimitHardVoices(md_driver->HardVoiceLimit);\r
668         LimitSoftVoices(md_driver->SoftVoiceLimit);\r
669 */\r
670         LimitHardVoices(0);\r
671         LimitSoftVoices(255);\r
672 \r
673         if(VC_SetNumVoices()) {\r
674                 MikMod_Exit_internal();\r
675                 if(_mm_errno)\r
676                         if(_mm_errorhandler!=NULL) _mm_errorhandler();\r
677                 md_numchn = md_softchn = md_hardchn = md_sfxchn = md_sngchn = 0;\r
678                 return 1;\r
679         }\r
680 \r
681         if(md_sngchn+md_sfxchn)\r
682                 md_sample=(SAMPLE**)_mm_calloc(md_sngchn+md_sfxchn,sizeof(SAMPLE*));\r
683         if(md_sfxchn)\r
684                 sfxinfo = (UBYTE *)_mm_calloc(md_sfxchn,sizeof(UBYTE));\r
685 \r
686         /* make sure the player doesn't start with garbage */\r
687         for(t=oldchn;t<md_numchn;t++)  Voice_Stop_internal(t);\r
688 \r
689         sfxpool = 0;\r
690         if(resume) MikMod_EnableOutput_internal();\r
691         _mm_critical = 0;\r
692 \r
693         return 0;\r
694 }\r
695 \r
696 MIKMODAPI BOOL MikMod_SetNumVoices(int music, int sfx)\r
697 {\r
698         BOOL result;\r
699 \r
700         MUTEX_LOCK(vars);\r
701         result=MikMod_SetNumVoices_internal(music,sfx);\r
702         MUTEX_UNLOCK(vars);\r
703 \r
704         return result;\r
705 }\r
706 \r
707 BOOL MikMod_EnableOutput_internal(void)\r
708 {\r
709         _mm_critical = 1;\r
710         if(!isplaying) {\r
711                 if(VC_PlayStart()) return 1;\r
712                 isplaying = 1;\r
713         }\r
714         _mm_critical = 0;\r
715         return 0;\r
716 }\r
717 \r
718 MIKMODAPI BOOL MikMod_EnableOutput(void)\r
719 {\r
720         BOOL result;\r
721 \r
722         MUTEX_LOCK(vars);\r
723         result=MikMod_EnableOutput_internal();\r
724         MUTEX_UNLOCK(vars);\r
725 \r
726         return result;\r
727 }\r
728 \r
729 void MikMod_DisableOutput_internal(void)\r
730 {\r
731         if(isplaying/* && md_driver*/) {\r
732                 isplaying = 0;\r
733                 VC_PlayStop();\r
734         }\r
735 }\r
736 \r
737 MIKMODAPI void MikMod_DisableOutput(void)\r
738 {\r
739         MUTEX_LOCK(vars);\r
740         MikMod_DisableOutput_internal();\r
741         MUTEX_UNLOCK(vars);\r
742 }\r
743 \r
744 BOOL MikMod_Active_internal(void)\r
745 {\r
746         return isplaying;\r
747 }\r
748 \r
749 MIKMODAPI BOOL MikMod_Active(void)\r
750 {\r
751         BOOL result;\r
752 \r
753         MUTEX_LOCK(vars);\r
754         result=MikMod_Active_internal();\r
755         MUTEX_UNLOCK(vars);\r
756 \r
757         return result;\r
758 }\r
759 \r
760 /* Plays a sound effects sample.  Picks a voice from the number of voices\r
761    allocated for use as sound effects (loops through voices, skipping all active\r
762    criticals).\r
763 \r
764    Returns the voice that the sound is being played on.                       */\r
765 SBYTE Sample_Play_internal(SAMPLE *s,ULONG start,UBYTE flags)\r
766 {\r
767         int orig=sfxpool;/* for cases where all channels are critical */\r
768         int c;\r
769 \r
770         if(!md_sfxchn) return -1;\r
771         if(s->volume>64) s->volume = 64;\r
772 \r
773         /* check the first location after sfxpool */\r
774         do {\r
775                 if(sfxinfo[sfxpool]&SFX_CRITICAL) {\r
776                         if(VC_VoiceStopped(c=sfxpool+md_sngchn)) {\r
777                                 sfxinfo[sfxpool]=flags;\r
778                                 Voice_Play_internal(c,s,start);\r
779                                 VC_VoiceSetVolume(c,s->volume<<2);\r
780                                 Voice_SetPanning_internal(c,s->panning);\r
781                                 VC_VoiceSetFrequency(c,s->speed);\r
782                                 sfxpool++;\r
783                                 if(sfxpool>=md_sfxchn) sfxpool=0;\r
784                                 return c;\r
785                         }\r
786                 } else {\r
787                         sfxinfo[sfxpool]=flags;\r
788                         Voice_Play_internal(c=sfxpool+md_sngchn,s,start);\r
789                         VC_VoiceSetVolume(c,s->volume<<2);\r
790                         Voice_SetPanning_internal(c,s->panning);\r
791                         VC_VoiceSetFrequency(c,s->speed);\r
792                         sfxpool++;\r
793                         if(sfxpool>=md_sfxchn) sfxpool=0;\r
794                         return c;\r
795                 }\r
796 \r
797                 sfxpool++;\r
798                 if(sfxpool>=md_sfxchn) sfxpool = 0;\r
799         } while(sfxpool!=orig);\r
800 \r
801         return -1;\r
802 }\r
803 \r
804 MIKMODAPI SBYTE Sample_Play(SAMPLE *s,ULONG start,UBYTE flags)\r
805 {\r
806         SBYTE result;\r
807 \r
808         MUTEX_LOCK(vars);\r
809         result=Sample_Play_internal(s,start,flags);\r
810         MUTEX_UNLOCK(vars);\r
811 \r
812         return result;\r
813 }\r
814 \r
815 MIKMODAPI long MikMod_GetVersion(void)\r
816 {\r
817         return LIBMIKMOD_VERSION;\r
818 }\r
819 \r
820 /*========== MT-safe stuff */\r
821 \r
822 #ifdef HAVE_PTHREAD\r
823 #define INIT_MUTEX(name) \\r
824         pthread_mutex_t _mm_mutex_##name=PTHREAD_MUTEX_INITIALIZER\r
825 #elif defined(__OS2__)||defined(__EMX__)\r
826 #define INIT_MUTEX(name) \\r
827         HMTX _mm_mutex_##name\r
828 #elif defined(WIN32)\r
829 #define INIT_MUTEX(name) \\r
830         HANDLE _mm_mutex_##name\r
831 #else\r
832 #define INIT_MUTEX(name) \\r
833         void *_mm_mutex_##name = NULL\r
834 #endif\r
835 \r
836 INIT_MUTEX(vars);\r
837 INIT_MUTEX(lists);\r
838 \r
839 MIKMODAPI BOOL MikMod_InitThreads(void)\r
840 {\r
841         static int firstcall=1;\r
842         static int result=0;\r
843         \r
844         if (firstcall) {\r
845                 firstcall=0;\r
846 #ifdef HAVE_PTHREAD\r
847                 result=1;\r
848 #elif defined(__OS2__)||defined(__EMX__)\r
849                 if(DosCreateMutexSem((PSZ)NULL,&_mm_mutex_lists,0,0) ||\r
850                    DosCreateMutexSem((PSZ)NULL,&_mm_mutex_vars,0,0)) {\r
851                         _mm_mutex_lists=_mm_mutex_vars=(HMTX)NULL;\r
852                         result=0;\r
853                 } else\r
854                         result=1;\r
855 #elif defined(WIN32)\r
856                 if((!(_mm_mutex_lists=CreateMutex(NULL,FALSE,"libmikmod(lists)")))||\r
857                    (!(_mm_mutex_vars=CreateMutex(NULL,FALSE,"libmikmod(vars)"))))\r
858                         result=0;\r
859                 else\r
860                         result=1;\r
861 #endif\r
862         }\r
863         return result;\r
864 }\r
865 \r
866 MIKMODAPI void MikMod_Unlock(void)\r
867 {\r
868         MUTEX_UNLOCK(lists);\r
869         MUTEX_UNLOCK(vars);\r
870 }\r
871 \r
872 MIKMODAPI void MikMod_Lock(void)\r
873 {\r
874         MUTEX_LOCK(vars);\r
875         MUTEX_LOCK(lists);\r
876 }\r
877 \r
878 /*========== Parameter extraction helper */\r
879 \r
880 CHAR *MD_GetAtom(CHAR *atomname,CHAR *cmdline,BOOL implicit)\r
881 {\r
882         CHAR *ret=NULL;\r
883 \r
884         if(cmdline) {\r
885                 CHAR *buf=strstr(cmdline,atomname);\r
886 \r
887                 if((buf)&&((buf==cmdline)||(*(buf-1)==','))) {\r
888                         CHAR *ptr=buf+strlen(atomname);\r
889 \r
890                         if(*ptr=='=') {\r
891                                 for(buf=++ptr;(*ptr)&&((*ptr)!=',');ptr++);\r
892                                 ret=_mm_malloc((1+ptr-buf)*sizeof(CHAR));\r
893                                 if(ret)\r
894                                         strncpy(ret,buf,ptr-buf);\r
895                         } else if((*ptr==',')||(!*ptr)) {\r
896                                 if(implicit) {\r
897                                         ret=_mm_malloc((1+ptr-buf)*sizeof(CHAR));\r
898                                         if(ret)\r
899                                                 strncpy(ret,buf,ptr-buf);\r
900                                 }\r
901                         }\r
902                 }\r
903         }\r
904         return ret;\r
905 }\r
906 \r
907 #if defined unix || (defined __APPLE__ && defined __MACH__)\r
908 \r
909 /*========== Posix helper functions */\r
910 \r
911 /* Check if the file is a regular or nonexistant file (or a link to a such a\r
912    file), and that, should the calling program be setuid, the access rights are\r
913    reasonable. Returns 1 if it is safe to rewrite the file, 0 otherwise.\r
914    The goal is to prevent a setuid root libmikmod application from overriding\r
915    files like /etc/passwd with digital sound... */\r
916 BOOL MD_Access(CHAR *filename)\r
917 {\r
918         struct stat buf;\r
919 \r
920         if(!stat(filename,&buf)) {\r
921                 /* not a regular file ? */\r
922                 if(!S_ISREG(buf.st_mode)) return 0;\r
923                 /* more than one hard link to the file ? */\r
924                 if(buf.st_nlink>1) return 0;\r
925                 /* check access rights with the real user and group id */\r
926                 if(getuid()==buf.st_uid) {\r
927                         if(!(buf.st_mode&S_IWUSR)) return 0;\r
928                 } else if(getgid()==buf.st_gid) {\r
929                         if(!(buf.st_mode&S_IWGRP)) return 0;\r
930                 } else\r
931                         if(!(buf.st_mode&S_IWOTH)) return 0;\r
932         }\r
933         \r
934         return 1;\r
935 }\r
936 \r
937 /* Drop all root privileges we might have */\r
938 BOOL MD_DropPrivileges(void)\r
939 {\r
940         if(!geteuid()) {\r
941                 if(getuid()) {\r
942                         /* we are setuid root -> drop setuid to become the real user */\r
943                         if(setuid(getuid())) return 1;\r
944                 } else {\r
945                         /* we are run as root -> drop all and become user 'nobody' */\r
946                         struct passwd *nobody;\r
947                         int uid;\r
948 \r
949                         if(!(nobody=getpwnam("nobody"))) return 1; /* no such user ? */\r
950                         uid=nobody->pw_uid;\r
951                         if (!uid) /* user 'nobody' has root privileges ? weird... */\r
952                                 return 1;\r
953                         if (setuid(uid)) return 1;\r
954                 }\r
955         }\r
956         return 0;\r
957 }\r
958 \r
959 #endif\r
960 \r
961 /* ex:set ts=4: */\r