A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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
45extern int fprintf(FILE *, const char *, ...);
46#endif
47
48extern MODULE *pf; /* modfile being played */
49
50/* EXPORTED GLOBALS */
51MIKMODAPI MDRIVER *md_driver = NULL;
52
53/* Initial global settings */
54MIKMODAPI UWORD md_device = 0; /* autodetect */
55MIKMODAPI ULONG md_mixfreq = 44100;
56MIKMODAPI UWORD md_mode = DMODE_STEREO | DMODE_16BITS |
57 DMODE_SURROUND |
58 DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX;
59MIKMODAPI UBYTE md_pansep = 128; /* 128 == 100% (full left/right) */
60MIKMODAPI UBYTE md_reverb = 0; /* no reverb */
61MIKMODAPI UBYTE md_volume = 128; /* global sound volume (0-128) */
62MIKMODAPI UBYTE md_musicvolume = 128; /* volume of song */
63MIKMODAPI UBYTE md_sndfxvolume = 128; /* volume of sound effects */
64
65/* INTERNAL GLOBALS */
66UWORD md_bpm = 125; /* tempo */
67
68/* Do not modify the numchn variables yourself! use MikMod_SetNumVoices() */
69UBYTE md_numchn = 0, md_sngchn = 0, md_sfxchn = 0;
70UBYTE md_hardchn = 0, md_softchn= 0;
71
72MikMod_player_t md_player = Player_HandleTick;
73
74MikMod_callback_t vc_callback = NULL;
75
76/* PRIVATE VARS */
77static MDRIVER *firstdriver = NULL;
78
79static volatile int isplaying = 0, initialized = 0;
80
81static UBYTE *sfxinfo;
82static int sfxpool;
83
84static SAMPLE **md_sample = NULL;
85
86/* Previous driver in use */
87static 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. */
91static 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. */
125static 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. */
159ULONG 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
169ULONG 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
180MIKMODAPI 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 + 1 + 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\n", t, l->Version);
199 if (!l->next) list_end[-1] = 0;
200 }
201 }
202 MUTEX_UNLOCK(lists);
203 return list;
204}
205
206void _mm_registerdriver(struct MDRIVER* drv)
207{
208 MDRIVER *cruise = firstdriver;
209
210 /* don't register a MISSING() driver */
211 if ((drv->Name) && (drv->Version)) {
212 if (cruise) {
213 if ( cruise == drv )
214 return;
215 while(cruise->next) {
216 cruise = cruise->next;
217 if ( cruise == drv )
218 return;
219 }
220 cruise->next = drv;
221 } else
222 firstdriver = drv;
223 }
224}
225
226MIKMODAPI void MikMod_RegisterDriver(struct MDRIVER* drv)
227{
228 /* if we try to register an invalid driver, or an already registered driver,
229 ignore this attempt */
230 if ((!drv)||(drv->next)||(!drv->Name))
231 return;
232
233 MUTEX_LOCK(lists);
234 _mm_registerdriver(drv);
235 MUTEX_UNLOCK(lists);
236}
237
238MIKMODAPI int MikMod_DriverFromAlias(const CHAR *alias)
239{
240 int rank=1;
241 MDRIVER *cruise;
242
243 MUTEX_LOCK(lists);
244 cruise=firstdriver;
245 while(cruise) {
246 if (cruise->Alias) {
247 if (!(strcasecmp(alias,cruise->Alias))) break;
248 rank++;
249 }
250 cruise=cruise->next;
251 }
252 if(!cruise) rank=0;
253 MUTEX_UNLOCK(lists);
254
255 return rank;
256}
257
258MIKMODAPI MDRIVER *MikMod_DriverByOrdinal(int ordinal)
259{
260 MDRIVER *cruise;
261
262 /* Allow only driver ordinals > 0 */
263 if (!ordinal) return NULL;
264
265 MUTEX_LOCK(lists);
266 cruise = firstdriver;
267 while (cruise && --ordinal)
268 cruise = cruise->next;
269 MUTEX_UNLOCK(lists);
270 return cruise;
271}
272
273SWORD MD_SampleLoad(SAMPLOAD* s, int type)
274{
275 SWORD result;
276
277 if(type==MD_MUSIC)
278 type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE;
279 else if(type==MD_SNDFX)
280 type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE;
281
282 SL_Init(s);
283 result=md_driver->SampleLoad(s,type);
284 SL_Exit(s);
285
286 return result;
287}
288
289void MD_SampleUnload(SWORD handle)
290{
291 md_driver->SampleUnload(handle);
292}
293
294MIKMODAPI MikMod_player_t MikMod_RegisterPlayer(MikMod_player_t player)
295{
296 MikMod_player_t result;
297
298 MUTEX_LOCK(vars);
299 result=md_player;
300 md_player=player;
301 MUTEX_UNLOCK(vars);
302
303 return result;
304}
305
306MIKMODAPI void MikMod_Update(void)
307{
308 MUTEX_LOCK(vars);
309 if(isplaying) {
310 if((!pf)||(!pf->forbid))
311 md_driver->Update();
312 else {
313 if (md_driver->Pause)
314 md_driver->Pause();
315 }
316 }
317 MUTEX_UNLOCK(vars);
318}
319
320void Voice_SetVolume_internal(SBYTE voice,UWORD vol)
321{
322 ULONG tmp;
323
324 if((voice<0)||(voice>=md_numchn)) return;
325
326 /* range checks */
327 if(md_musicvolume>128) md_musicvolume=128;
328 if(md_sndfxvolume>128) md_sndfxvolume=128;
329 if(md_volume>128) md_volume=128;
330
331 tmp=(ULONG)vol*(ULONG)md_volume*
332 ((voice<md_sngchn)?(ULONG)md_musicvolume:(ULONG)md_sndfxvolume);
333 md_driver->VoiceSetVolume(voice,tmp/16384UL);
334}
335
336MIKMODAPI void Voice_SetVolume(SBYTE voice,UWORD vol)
337{
338 MUTEX_LOCK(vars);
339 Voice_SetVolume_internal(voice,vol);
340 MUTEX_UNLOCK(vars);
341}
342
343MIKMODAPI UWORD Voice_GetVolume(SBYTE voice)
344{
345 UWORD result=0;
346
347 MUTEX_LOCK(vars);
348 if((voice>=0)&&(voice<md_numchn))
349 result=md_driver->VoiceGetVolume(voice);
350 MUTEX_UNLOCK(vars);
351
352 return result;
353}
354
355void Voice_SetFrequency_internal(SBYTE voice,ULONG frq)
356{
357 if((voice<0)||(voice>=md_numchn)) return;
358 if((md_sample[voice])&&(md_sample[voice]->divfactor))
359 frq/=md_sample[voice]->divfactor;
360 md_driver->VoiceSetFrequency(voice,frq);
361}
362
363MIKMODAPI void Voice_SetFrequency(SBYTE voice,ULONG frq)
364{
365 MUTEX_LOCK(vars);
366 Voice_SetFrequency_internal(voice,frq);
367 MUTEX_UNLOCK(vars);
368}
369
370MIKMODAPI ULONG Voice_GetFrequency(SBYTE voice)
371{
372 ULONG result=0;
373
374 MUTEX_LOCK(vars);
375 if((voice>=0)&&(voice<md_numchn))
376 result=md_driver->VoiceGetFrequency(voice);
377 MUTEX_UNLOCK(vars);
378
379 return result;
380}
381
382void Voice_SetPanning_internal(SBYTE voice,ULONG pan)
383{
384 if((voice<0)||(voice>=md_numchn)) return;
385 if(pan!=PAN_SURROUND) {
386 if(md_pansep>128) md_pansep=128;
387 if(md_mode & DMODE_REVERSE) pan=255-pan;
388 pan = (((SWORD)(pan-128)*md_pansep)/128)+128;
389 }
390 md_driver->VoiceSetPanning(voice, pan);
391}
392
393MIKMODAPI void Voice_SetPanning(SBYTE voice,ULONG pan)
394{
395#ifdef MIKMOD_DEBUG
396 if((pan!=PAN_SURROUND)&&((pan<0)||(pan>255)))
397 fprintf(stderr,"\rVoice_SetPanning called with pan=%ld\n",(long)pan);
398#endif
399
400 MUTEX_LOCK(vars);
401 Voice_SetPanning_internal(voice,pan);
402 MUTEX_UNLOCK(vars);
403}
404
405MIKMODAPI ULONG Voice_GetPanning(SBYTE voice)
406{
407 ULONG result=PAN_CENTER;
408
409 MUTEX_LOCK(vars);
410 if((voice>=0)&&(voice<md_numchn))
411 result=md_driver->VoiceGetPanning(voice);
412 MUTEX_UNLOCK(vars);
413
414 return result;
415}
416
417void Voice_Play_internal(SBYTE voice,SAMPLE* s,ULONG start)
418{
419 ULONG repend;
420
421 if((voice<0)||(voice>=md_numchn)) return;
422
423 md_sample[voice]=s;
424 repend=s->loopend;
425
426 if(s->flags&SF_LOOP)
427 /* repend can't be bigger than size */
428 if(repend>s->length) repend=s->length;
429
430 md_driver->VoicePlay(voice,s->handle,start,s->length,s->loopstart,repend,s->flags);
431}
432
433MIKMODAPI void Voice_Play(SBYTE voice,SAMPLE* s,ULONG start)
434{
435 if(start>s->length) return;
436
437 MUTEX_LOCK(vars);
438 Voice_Play_internal(voice,s,start);
439 MUTEX_UNLOCK(vars);
440}
441
442void Voice_Stop_internal(SBYTE voice)
443{
444 if((voice<0)||(voice>=md_numchn)) return;
445 if(voice>=md_sngchn)
446 /* It is a sound effects channel, so flag the voice as non-critical! */
447 sfxinfo[voice-md_sngchn]=0;
448 md_driver->VoiceStop(voice);
449}
450
451MIKMODAPI void Voice_Stop(SBYTE voice)
452{
453 MUTEX_LOCK(vars);
454 Voice_Stop_internal(voice);
455 MUTEX_UNLOCK(vars);
456}
457
458int Voice_Stopped_internal(SBYTE voice)
459{
460 if((voice<0)||(voice>=md_numchn)) return 0;
461 return(md_driver->VoiceStopped(voice));
462}
463
464MIKMODAPI int Voice_Stopped(SBYTE voice)
465{
466 int result;
467
468 MUTEX_LOCK(vars);
469 result=Voice_Stopped_internal(voice);
470 MUTEX_UNLOCK(vars);
471
472 return result;
473}
474
475MIKMODAPI SLONG Voice_GetPosition(SBYTE voice)
476{
477 SLONG result=0;
478
479 MUTEX_LOCK(vars);
480 if((voice>=0)&&(voice<md_numchn)) {
481 if (md_driver->VoiceGetPosition)
482 result=(md_driver->VoiceGetPosition(voice));
483 else
484 result=-1;
485 }
486 MUTEX_UNLOCK(vars);
487
488 return result;
489}
490
491MIKMODAPI ULONG Voice_RealVolume(SBYTE voice)
492{
493 ULONG result=0;
494
495 MUTEX_LOCK(vars);
496 if((voice>=0)&&(voice<md_numchn)&& md_driver->VoiceRealVolume)
497 result=(md_driver->VoiceRealVolume(voice));
498 MUTEX_UNLOCK(vars);
499
500 return result;
501}
502
503MIKMODAPI void VC_SetCallback(MikMod_callback_t callback)
504{
505 vc_callback = callback;
506}
507
508static int _mm_init(const CHAR *cmdline)
509{
510 UWORD t;
511
512 _mm_critical = 1;
513
514 /* if md_device==0, try to find a device number */
515 if(!md_device) {
516 cmdline=NULL;
517
518 for(t=1,md_driver=firstdriver;md_driver;md_driver=md_driver->next,t++)
519 if(md_driver->IsPresent()) break;
520
521 if(!md_driver) {
522 _mm_errno = MMERR_DETECTING_DEVICE;
523 if(_mm_errorhandler) _mm_errorhandler();
524 md_driver = &drv_nos;
525 return 1;
526 }
527
528 md_device = t;
529 } else {
530 /* if n>0, use that driver */
531 for(t=1,md_driver=firstdriver;(md_driver)&&(t!=md_device);md_driver=md_driver->next)
532 t++;
533
534 if(!md_driver) {
535 _mm_errno = MMERR_INVALID_DEVICE;
536 if(_mm_errorhandler) _mm_errorhandler();
537 md_driver = &drv_nos;
538 return 1;
539 }
540
541 /* arguments here might be necessary for the presence check to succeed */
542 if(cmdline&&(md_driver->CommandLine))
543 md_driver->CommandLine(cmdline);
544
545 if(!md_driver->IsPresent()) {
546 _mm_errno = MMERR_DETECTING_DEVICE;
547 if(_mm_errorhandler) _mm_errorhandler();
548 md_driver = &drv_nos;
549 return 1;
550 }
551 }
552
553 olddevice = md_device;
554 if(md_driver->Init()) {
555 MikMod_Exit_internal();
556 if(_mm_errorhandler) _mm_errorhandler();
557 return 1;
558 }
559
560 initialized=1;
561 _mm_critical=0;
562
563 return 0;
564}
565
566MIKMODAPI int MikMod_Init(const CHAR *cmdline)
567{
568 int result;
569
570 MUTEX_LOCK(vars);
571 MUTEX_LOCK(lists);
572 result=_mm_init(cmdline);
573 MUTEX_UNLOCK(lists);
574 MUTEX_UNLOCK(vars);
575
576 return result;
577}
578
579void MikMod_Exit_internal(void)
580{
581 MikMod_DisableOutput_internal();
582 md_driver->Exit();
583 md_numchn = md_sfxchn = md_sngchn = 0;
584 md_driver = &drv_nos;
585
586 MikMod_free(sfxinfo);
587 MikMod_free(md_sample);
588 md_sample = NULL;
589 sfxinfo = NULL;
590
591 initialized = 0;
592}
593
594MIKMODAPI void MikMod_Exit(void)
595{
596 MUTEX_LOCK(vars);
597 MUTEX_LOCK(lists);
598 MikMod_Exit_internal();
599 MUTEX_UNLOCK(lists);
600 MUTEX_UNLOCK(vars);
601}
602
603/* Reset the driver using the new global variable settings.
604 If the driver has not been initialized, it will be now. */
605static int _mm_reset(const CHAR *cmdline)
606{
607 int wasplaying = 0;
608
609 if(!initialized) return _mm_init(cmdline);
610
611 if (isplaying) {
612 wasplaying = 1;
613 md_driver->PlayStop();
614 }
615
616 if((!md_driver->Reset)||(md_device != olddevice)) {
617 /* md_driver->Reset was NULL, or md_device was changed, so do a full
618 reset of the driver. */
619 md_driver->Exit();
620 if(_mm_init(cmdline)) {
621 MikMod_Exit_internal();
622 if(_mm_errno)
623 if(_mm_errorhandler) _mm_errorhandler();
624 return 1;
625 }
626 } else {
627 if(md_driver->Reset()) {
628 MikMod_Exit_internal();
629 if(_mm_errno)
630 if(_mm_errorhandler) _mm_errorhandler();
631 return 1;
632 }
633 }
634
635 if (wasplaying) return md_driver->PlayStart();
636 return 0;
637}
638
639MIKMODAPI int MikMod_Reset(const CHAR *cmdline)
640{
641 int result;
642
643 MUTEX_LOCK(vars);
644 MUTEX_LOCK(lists);
645 result=_mm_reset(cmdline);
646 MUTEX_UNLOCK(lists);
647 MUTEX_UNLOCK(vars);
648
649 return result;
650}
651
652/* If either parameter is -1, the current set value will be retained. */
653int MikMod_SetNumVoices_internal(int music, int sfx)
654{
655 int resume = 0;
656 int t, oldchn = 0;
657
658 if((!music)&&(!sfx)) return 1;
659 _mm_critical = 1;
660 if(isplaying) {
661 MikMod_DisableOutput_internal();
662 oldchn = md_numchn;
663 resume = 1;
664 }
665
666 MikMod_free(sfxinfo);
667 MikMod_free(md_sample);
668 md_sample = NULL;
669 sfxinfo = NULL;
670
671 if(music!=-1) md_sngchn = music;
672 if(sfx!=-1) md_sfxchn = sfx;
673 md_numchn = md_sngchn + md_sfxchn;
674
675 LimitHardVoices(md_driver->HardVoiceLimit);
676 LimitSoftVoices(md_driver->SoftVoiceLimit);
677
678 if(md_driver->SetNumVoices()) {
679 MikMod_Exit_internal();
680 if(_mm_errno)
681 if(_mm_errorhandler!=NULL) _mm_errorhandler();
682 md_numchn = md_softchn = md_hardchn = md_sfxchn = md_sngchn = 0;
683 return 1;
684 }
685
686 if(md_sngchn+md_sfxchn)
687 md_sample=(SAMPLE**)MikMod_calloc(md_sngchn+md_sfxchn,sizeof(SAMPLE*));
688 if(md_sfxchn)
689 sfxinfo = (UBYTE *)MikMod_calloc(md_sfxchn,sizeof(UBYTE));
690
691 /* make sure the player doesn't start with garbage */
692 for(t=oldchn;t<md_numchn;t++) Voice_Stop_internal(t);
693
694 sfxpool = 0;
695 if(resume) MikMod_EnableOutput_internal();
696 _mm_critical = 0;
697
698 return 0;
699}
700
701MIKMODAPI int MikMod_SetNumVoices(int music, int sfx)
702{
703 int result;
704
705 MUTEX_LOCK(vars);
706 result=MikMod_SetNumVoices_internal(music,sfx);
707 MUTEX_UNLOCK(vars);
708
709 return result;
710}
711
712int MikMod_EnableOutput_internal(void)
713{
714 _mm_critical = 1;
715 if(!isplaying) {
716 if(md_driver->PlayStart()) return 1;
717 isplaying = 1;
718 }
719 _mm_critical = 0;
720 return 0;
721}
722
723MIKMODAPI int MikMod_EnableOutput(void)
724{
725 int result;
726
727 MUTEX_LOCK(vars);
728 result=MikMod_EnableOutput_internal();
729 MUTEX_UNLOCK(vars);
730
731 return result;
732}
733
734void MikMod_DisableOutput_internal(void)
735{
736 if(isplaying && md_driver) {
737 isplaying = 0;
738 md_driver->PlayStop();
739 }
740}
741
742MIKMODAPI void MikMod_DisableOutput(void)
743{
744 MUTEX_LOCK(vars);
745 MikMod_DisableOutput_internal();
746 MUTEX_UNLOCK(vars);
747}
748
749int MikMod_Active_internal(void)
750{
751 return isplaying;
752}
753
754MIKMODAPI int MikMod_Active(void)
755{
756 int result;
757
758 MUTEX_LOCK(vars);
759 result=MikMod_Active_internal();
760 MUTEX_UNLOCK(vars);
761
762 return result;
763}
764
765/* Plays a sound effects sample. Picks a voice from the number of voices
766 allocated for use as sound effects (loops through voices, skipping all active
767 criticals).
768
769 Returns the voice that the sound is being played on. */
770static SBYTE Sample_Play_internal(SAMPLE *s,ULONG start,UBYTE flags)
771{
772 int orig=sfxpool;/* for cases where all channels are critical */
773 int c;
774
775 if(!md_sfxchn) return -1;
776 if(s->volume>64) s->volume = 64;
777
778 /* check the first location after sfxpool */
779 do {
780 if(sfxinfo[sfxpool]&SFX_CRITICAL) {
781 if(md_driver->VoiceStopped(c=sfxpool+md_sngchn)) {
782 sfxinfo[sfxpool]=flags;
783 Voice_Play_internal(c,s,start);
784 md_driver->VoiceSetVolume(c,s->volume<<2);
785 Voice_SetPanning_internal(c,s->panning);
786 md_driver->VoiceSetFrequency(c,s->speed);
787 sfxpool++;
788 if(sfxpool>=md_sfxchn) sfxpool=0;
789 return c;
790 }
791 } else {
792 sfxinfo[sfxpool]=flags;
793 Voice_Play_internal(c=sfxpool+md_sngchn,s,start);
794 md_driver->VoiceSetVolume(c,s->volume<<2);
795 Voice_SetPanning_internal(c,s->panning);
796 md_driver->VoiceSetFrequency(c,s->speed);
797 sfxpool++;
798 if(sfxpool>=md_sfxchn) sfxpool=0;
799 return c;
800 }
801
802 sfxpool++;
803 if(sfxpool>=md_sfxchn) sfxpool = 0;
804 } while(sfxpool!=orig);
805
806 return -1;
807}
808
809MIKMODAPI SBYTE Sample_Play(SAMPLE *s,ULONG start,UBYTE flags)
810{
811 SBYTE result;
812
813 MUTEX_LOCK(vars);
814 result=Sample_Play_internal(s,start,flags);
815 MUTEX_UNLOCK(vars);
816
817 return result;
818}
819
820MIKMODAPI long MikMod_GetVersion(void)
821{
822 return LIBMIKMOD_VERSION;
823}
824
825/*========== MT-safe stuff */
826
827#ifdef HAVE_PTHREAD
828#define INIT_MUTEX(name) \
829 pthread_mutex_t _mm_mutex_##name=PTHREAD_MUTEX_INITIALIZER
830
831#elif defined(__OS2__)||defined(__EMX__)
832#define INIT_MUTEX(name) \
833 HMTX _mm_mutex_##name
834
835#elif defined(_WIN32)
836#define INIT_MUTEX(name) \
837 HANDLE _mm_mutex_##name
838
839#else
840#define INIT_MUTEX(name) \
841 void *_mm_mutex_##name = NULL
842#endif
843
844INIT_MUTEX(vars);
845INIT_MUTEX(lists);
846
847MIKMODAPI int MikMod_InitThreads(void)
848{
849 static int firstcall=1;
850 static int result = 0;
851
852 if (firstcall) {
853 firstcall=0;
854#ifdef HAVE_PTHREAD
855 result=1;
856#elif defined(__OS2__)||defined(__EMX__)
857 if(DosCreateMutexSem((PSZ)NULL,&_mm_mutex_lists,0,0) ||
858 DosCreateMutexSem((PSZ)NULL,&_mm_mutex_vars,0,0)) {
859 _mm_mutex_lists=_mm_mutex_vars=(HMTX)NULL;
860 result=0;
861 } else
862 result=1;
863#elif defined(_WIN32)
864 if((!(_mm_mutex_lists=CreateMutex(NULL,FALSE,TEXT("libmikmod(lists)"))))||
865 (!(_mm_mutex_vars=CreateMutex(NULL,FALSE,TEXT("libmikmod(vars)")))))
866 result=0;
867 else
868 result=1;
869#endif
870 }
871 return result;
872}
873
874MIKMODAPI void MikMod_Unlock(void)
875{
876 MUTEX_UNLOCK(lists);
877 MUTEX_UNLOCK(vars);
878}
879
880MIKMODAPI void MikMod_Lock(void)
881{
882 MUTEX_LOCK(vars);
883 MUTEX_LOCK(lists);
884}
885
886/*========== Parameter extraction helper */
887
888CHAR *MD_GetAtom(const CHAR *atomname, const CHAR *cmdline, int implicit)
889{
890 CHAR *ret=NULL;
891
892 if(cmdline) {
893 const CHAR *buf=strstr(cmdline,atomname);
894
895 if((buf)&&((buf==cmdline)||(*(buf-1)==','))) {
896 const CHAR *ptr=buf+strlen(atomname);
897
898 if(*ptr=='=') {
899 for(buf=++ptr;(*ptr)&&((*ptr)!=',');ptr++);
900 ret=(CHAR *)MikMod_malloc((1+ptr-buf)*sizeof(CHAR));
901 if(ret)
902 strncpy(ret,buf,ptr-buf);
903 } else if((*ptr==',')||(!*ptr)) {
904 if(implicit) {
905 ret=(CHAR *)MikMod_malloc((1+ptr-buf)*sizeof(CHAR));
906 if(ret)
907 strncpy(ret,buf,ptr-buf);
908 }
909 }
910 }
911 }
912 return ret;
913}
914
915#if (MIKMOD_UNIX)
916
917/*========== Posix helper functions */
918
919/* Check if the file is a regular or nonexistant file (or a link to a such a
920 file), and that, should the calling program be setuid, the access rights are
921 reasonable. Returns 1 if it is safe to rewrite the file, 0 otherwise.
922 The goal is to prevent a setuid root libmikmod application from overriding
923 files like /etc/passwd with digital sound... */
924int MD_Access(const CHAR * filename)
925{
926 struct stat buf;
927
928 if(!stat(filename,&buf)) {
929 /* not a regular file ? */
930 if(!S_ISREG(buf.st_mode)) return 0;
931 /* more than one hard link to the file ? */
932 if(buf.st_nlink>1) return 0;
933 /* check access rights with the real user and group id */
934 if(getuid()==buf.st_uid) {
935 if(!(buf.st_mode&S_IWUSR)) return 0;
936 } else if(getgid()==buf.st_gid) {
937 if(!(buf.st_mode&S_IWGRP)) return 0;
938 } else
939 if(!(buf.st_mode&S_IWOTH)) return 0;
940 }
941
942 return 1;
943}
944
945/* Drop all root privileges we might have */
946int MD_DropPrivileges(void)
947{
948 if(!geteuid()) {
949 if(getuid()) {
950 /* we are setuid root -> drop setuid to become the real user */
951 if(setuid(getuid())) return 1;
952 } else {
953 /* we are run as root -> drop all and become user 'nobody' */
954 struct passwd *nobody;
955 int uid;
956
957 if(!(nobody=getpwnam("nobody"))) return 1; /* no such user ? */
958 uid=nobody->pw_uid;
959 if (!uid) /* user 'nobody' has root privileges ? weird... */
960 return 1;
961 if (setuid(uid)) return 1;
962 }
963 }
964 return 0;
965}
966
967#endif
968
969/* ex:set ts=8: */