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