12
|
1 /*
|
|
2 * Name: MDRIVER.C
|
|
3 *
|
|
4 * Description: These routines are used to access the available soundcard
|
|
5 * drivers.
|
|
6 *
|
|
7 * Portability: All systems - all compilers
|
|
8 *
|
|
9 */
|
|
10
|
|
11 #include "mikmod.h"
|
|
12
|
|
13 MDRIVER *firstdriver = NULL, *md_driver = &drv_nos;
|
|
14
|
|
15 UWORD md_device = 0;
|
|
16 UWORD md_mixfreq = 44100;
|
|
17 UWORD md_mode = DMODE_STEREO | DMODE_16BITS | DMODE_SURROUND;
|
|
18 UWORD md_dmabufsize = 50;
|
|
19 UBYTE md_pansep = 128; /* 128 == 100% (full left/right) */
|
|
20
|
|
21 UBYTE md_reverb = 6; /* Reverb */
|
|
22
|
|
23 UBYTE md_volume = 96; /* Global sound volume (0-128) */
|
|
24 UBYTE md_musicvolume = 128; /* volume of song */
|
|
25 UBYTE md_sndfxvolume = 128; /* volume of sound effects */
|
|
26
|
|
27 UBYTE md_bpm = 125;
|
|
28
|
|
29
|
|
30 /* Do not modify the numchn variables yourself! use MD_SetVoices() */
|
|
31
|
|
32 UBYTE md_numchn = 0, md_sngchn = 0, md_sfxchn = 0;
|
|
33 UBYTE md_hardchn = 0, md_softchn = 0;
|
|
34
|
|
35
|
|
36 void (*md_player) (void) = Player_HandleTick;
|
|
37 static BOOL isplaying = 0, initialized = 0;
|
|
38 static UBYTE *sfxinfo;
|
|
39 static int sfxpool;
|
|
40
|
|
41 static SAMPLE **md_sample = NULL;
|
|
42
|
|
43 /*
|
|
44 * Backup variables. This way, the end programmer can fiddle with the main
|
|
45 * globals without mikmod blowing up.
|
|
46 */
|
|
47
|
|
48 static UWORD idevice, imixfreq, imode, idmabufsize;
|
|
49
|
|
50 /*
|
|
51 * Limits the number of hardware voices to the specified amount. This
|
|
52 * function should only be used by the low-level drivers.
|
|
53 */
|
|
54 static void
|
|
55 LimitHardVoices(int limit)
|
|
56 {
|
|
57 int t = 0;
|
|
58
|
|
59 if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn > limit))
|
|
60 md_sfxchn = limit;
|
|
61 if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn > limit))
|
|
62 md_sngchn = limit;
|
|
63
|
|
64 if (!(md_mode & DMODE_SOFT_SNDFX))
|
|
65 md_hardchn = md_sfxchn;
|
|
66 else
|
|
67 md_hardchn = 0;
|
|
68
|
|
69 if (!(md_mode & DMODE_SOFT_MUSIC))
|
|
70 md_hardchn += md_sngchn;
|
|
71
|
|
72 while (md_hardchn > limit) {
|
|
73 if (++t & 1)
|
|
74 if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn > 4))
|
|
75 md_sfxchn--;
|
|
76 else if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn > 8))
|
|
77 md_sngchn--;
|
|
78
|
|
79 if (!(md_mode & DMODE_SOFT_SNDFX))
|
|
80 md_hardchn = md_sfxchn;
|
|
81 else
|
|
82 md_hardchn = 0;
|
|
83
|
|
84 if (!(md_mode & DMODE_SOFT_MUSIC))
|
|
85 md_hardchn += md_sngchn;
|
|
86 }
|
|
87
|
|
88 md_numchn = md_hardchn + md_softchn;
|
|
89 }
|
|
90
|
|
91 /*
|
|
92 * Limits the number of hardware voices to the specified amount. This
|
|
93 * function should only be used by the low-level drivers.
|
|
94 */
|
|
95 static void
|
|
96 LimitSoftVoices(int limit)
|
|
97 {
|
|
98 int t = 0;
|
|
99
|
|
100 if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn > limit))
|
|
101 md_sfxchn = limit;
|
|
102 if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn > limit))
|
|
103 md_sngchn = limit;
|
|
104
|
|
105 if (md_mode & DMODE_SOFT_SNDFX)
|
|
106 md_softchn = md_sfxchn;
|
|
107 else
|
|
108 md_softchn = 0;
|
|
109
|
|
110 if (md_mode & DMODE_SOFT_MUSIC)
|
|
111 md_softchn += md_sngchn;
|
|
112
|
|
113 while (md_softchn > limit) {
|
|
114 if (++t & 1)
|
|
115 if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn > 4))
|
|
116 md_sfxchn--;
|
|
117 else if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn > 8))
|
|
118 md_sngchn--;
|
|
119
|
|
120 if (!(md_mode & DMODE_SOFT_SNDFX))
|
|
121 md_softchn = md_sfxchn;
|
|
122 else
|
|
123 md_softchn = 0;
|
|
124
|
|
125 if (!(md_mode & DMODE_SOFT_MUSIC))
|
|
126 md_softchn += md_sngchn;
|
|
127 }
|
|
128
|
|
129 md_numchn = md_hardchn + md_softchn;
|
|
130 }
|
|
131
|
|
132
|
|
133 /*
|
|
134 * Note: 'type' indicates whether the returned value should be for music or
|
|
135 * for sound effects.
|
|
136 */
|
|
137 ULONG
|
|
138 MD_SampleSpace(int type)
|
|
139 {
|
|
140 if (type == MD_MUSIC)
|
|
141 type = (md_mode & DMODE_SOFT_MUSIC) ? MD_SOFTWARE : MD_HARDWARE;
|
|
142 else if (type == MD_SNDFX)
|
|
143 type = (md_mode & DMODE_SOFT_SNDFX) ? MD_SOFTWARE : MD_HARDWARE;
|
|
144
|
|
145 return md_driver->FreeSampleSpace(type);
|
|
146 }
|
|
147
|
|
148
|
|
149 ULONG
|
|
150 MD_SampleLength(int type, SAMPLE * s)
|
|
151 {
|
|
152 if (type == MD_MUSIC)
|
|
153 type = (md_mode & DMODE_SOFT_MUSIC) ? MD_SOFTWARE : MD_HARDWARE;
|
|
154 else if (type == MD_SNDFX)
|
|
155 type = (md_mode & DMODE_SOFT_SNDFX) ? MD_SOFTWARE : MD_HARDWARE;
|
|
156
|
|
157 return md_driver->RealSampleLength(type, s);
|
|
158 }
|
|
159
|
|
160
|
|
161 /*
|
|
162 * Converts the given number of 1/10th seconds into the number of bytes of
|
|
163 * audio that a sample # 1/10th seconds long would require at the current md_*
|
|
164 * settings.
|
|
165 */
|
|
166 UWORD
|
|
167 MD_SetDMA(int secs)
|
|
168 {
|
|
169 ULONG result;
|
|
170
|
|
171 result = (md_mixfreq * ((md_mode & DMODE_STEREO) ? 2 : 1) *
|
|
172 ((md_mode & DMODE_16BITS) ? 2 : 1) * secs) * 10;
|
|
173
|
|
174 if (result > 32000)
|
|
175 result = 32000;
|
|
176 return (md_dmabufsize = (result & ~3)); /* round it off to an 8 byte
|
|
177 * boundry */
|
|
178 }
|
|
179
|
|
180
|
|
181 void
|
|
182 MD_InfoDriver(void)
|
|
183 {
|
|
184 int t;
|
|
185 MDRIVER *l;
|
|
186
|
|
187 /* list all registered devicedrivers: */
|
|
188 for (t = 1, l = firstdriver; l != NULL; l = l->next, t++)
|
|
189 printf("%d. %s\n", t, l->Version);
|
|
190 }
|
|
191
|
|
192
|
|
193 void
|
|
194 MD_RegisterDriver(MDRIVER * drv)
|
|
195 {
|
|
196 MDRIVER *cruise = firstdriver;
|
|
197
|
|
198 if (cruise != NULL) {
|
|
199 while (cruise->next != NULL)
|
|
200 cruise = cruise->next;
|
|
201 cruise->next = drv;
|
|
202 } else
|
|
203 firstdriver = drv;
|
|
204 }
|
|
205
|
|
206
|
|
207 /* type - sample type .. MD_MUSIC or MD_SNDFX */
|
|
208 SWORD
|
|
209 MD_SampleLoad(SAMPLOAD * s, int type, FILE * fp)
|
|
210 {
|
|
211 SWORD result;
|
|
212
|
|
213 if (type == MD_MUSIC)
|
|
214 type = (md_mode & DMODE_SOFT_MUSIC) ? MD_SOFTWARE : MD_HARDWARE;
|
|
215 else if (type == MD_SNDFX)
|
|
216 type = (md_mode & DMODE_SOFT_SNDFX) ? MD_SOFTWARE : MD_HARDWARE;
|
|
217
|
|
218 SL_Init(s);
|
|
219 result = md_driver->SampleLoad(s, type, fp);
|
|
220 SL_Exit(s);
|
|
221
|
|
222 return result;
|
|
223 }
|
|
224
|
|
225
|
|
226 void
|
|
227 MD_SampleUnLoad(SWORD handle)
|
|
228 {
|
|
229 md_driver->SampleUnLoad(handle);
|
|
230 }
|
|
231
|
|
232
|
|
233 void
|
|
234 MD_SetBPM(UBYTE bpm)
|
|
235 {
|
|
236 md_bpm = bpm;
|
|
237 }
|
|
238
|
|
239
|
|
240 void
|
|
241 MikMod_RegisterPlayer(void (*player) (void))
|
|
242 {
|
|
243 md_player = player;
|
|
244 }
|
|
245
|
|
246
|
|
247 void
|
|
248 MikMod_Update(void)
|
|
249 {
|
|
250 if (isplaying)
|
|
251 md_driver->Update();
|
|
252 }
|
|
253
|
|
254
|
|
255 void
|
|
256 Voice_SetVolume(int voice, UWORD vol)
|
|
257 {
|
|
258 ULONG tmp;
|
|
259
|
|
260 if ((voice < 0) || (voice >= md_numchn))
|
|
261 return;
|
|
262 tmp = (ULONG) vol *(ULONG) md_volume *
|
|
263 ((voice < md_sngchn) ? (ULONG) md_musicvolume : (ULONG) md_sndfxvolume);
|
|
264 md_driver->VoiceSetVolume(voice, tmp / 16384 UL);
|
|
265 }
|
|
266
|
|
267
|
|
268 void
|
|
269 Voice_SetFrequency(int voice, ULONG frq)
|
|
270 {
|
|
271 if ((voice < 0) || (voice >= md_numchn))
|
|
272 return;
|
|
273 if (md_sample[voice] != NULL && md_sample[voice]->divfactor != 0)
|
|
274 frq /= md_sample[voice]->divfactor;
|
|
275 md_driver->VoiceSetFrequency(voice, frq);
|
|
276 }
|
|
277
|
|
278
|
|
279 void
|
|
280 Voice_SetPanning(int voice, ULONG pan)
|
|
281 {
|
|
282 if ((voice < 0) || (voice >= md_numchn))
|
|
283 return;
|
|
284 if (pan != PAN_SURROUND) {
|
|
285 if (md_mode & DMODE_REVERSE)
|
|
286 pan = 255 - pan;
|
|
287 pan = (((SWORD) (pan - 128) * md_pansep) / 128) + 128;
|
|
288 }
|
|
289 md_driver->VoiceSetPanning(voice, pan);
|
|
290 }
|
|
291
|
|
292
|
|
293 void
|
|
294 Voice_Play(int voice, SAMPLE * s, ULONG start)
|
|
295 {
|
|
296 ULONG repend;
|
|
297
|
|
298 if ((voice < 0) || (voice >= md_numchn) || (start >= s->length))
|
|
299 return;
|
|
300
|
|
301 md_sample[voice] = s;
|
|
302 repend = s->loopend;
|
|
303
|
|
304 if (s->flags & SF_LOOP)
|
|
305 if (repend > s->length)
|
|
306 repend = s->length; /* repend can't be bigger than size */
|
|
307
|
|
308 md_driver->VoicePlay(voice, s->handle, start, s->length, s->loopstart, repend, s->flags);
|
|
309 }
|
|
310
|
|
311
|
|
312 void
|
|
313 Voice_Stop(int voice)
|
|
314 {
|
|
315 if ((voice < 0) || (voice >= md_numchn))
|
|
316 return;
|
|
317 if (voice >= md_sngchn) { /* It is a sound effects channel, so flag the
|
|
318 * voice as non-critical! */
|
|
319 sfxinfo[voice - md_sngchn] = 0;
|
|
320 }
|
|
321 md_driver->VoiceStop(voice);
|
|
322 }
|
|
323
|
|
324
|
|
325 BOOL
|
|
326 Voice_Stopped(int voice)
|
|
327 {
|
|
328 if ((voice < 0) || (voice >= md_numchn))
|
|
329 return 0;
|
|
330 return (md_driver->VoiceStopped(voice));
|
|
331 }
|
|
332
|
|
333
|
|
334 SLONG
|
|
335 Voice_GetPosition(int voice)
|
|
336 {
|
|
337 if ((voice < 0) || (voice >= md_numchn))
|
|
338 return 0;
|
|
339 return (md_driver->VoiceGetPosition(voice));
|
|
340 }
|
|
341
|
|
342
|
|
343 ULONG
|
|
344 Voice_RealVolume(int voice)
|
|
345 {
|
|
346 if ((voice < 0) || (voice >= md_numchn))
|
|
347 return 0;
|
|
348 return (md_driver->VoiceRealVolume(voice));
|
|
349 }
|
|
350
|
|
351
|
|
352 /*
|
|
353 * Functions prefixed with MikMod
|
|
354 */
|
|
355 BOOL
|
|
356 MikMod_Init(void)
|
|
357 {
|
|
358 UWORD t;
|
|
359
|
|
360 _mm_critical = 1;
|
|
361
|
|
362 /* if md_device==0, try to find a device number */
|
|
363
|
|
364 if (md_device == 0) {
|
|
365 for (t = 1, md_driver = firstdriver; md_driver != NULL; md_driver = md_driver->next, t++) {
|
|
366 if (md_driver->IsPresent())
|
|
367 break;
|
|
368 }
|
|
369
|
|
370 if (md_driver == NULL) {
|
|
371 _mm_errno = MMERR_DETECTING_DEVICE;
|
|
372 if (_mm_errorhandler != NULL)
|
|
373 _mm_errorhandler();
|
|
374 md_driver = &drv_nos;
|
|
375 return 1;
|
|
376 }
|
|
377 md_device = t;
|
|
378 } else { /* if n>0 use that driver */
|
|
379 for (t = 1, md_driver = firstdriver;
|
|
380 (md_driver != NULL) && (t != md_device);
|
|
381 md_driver = md_driver->next, t++)
|
|
382 ; /* Nothing */
|
|
383
|
|
384 if (md_driver == NULL) {
|
|
385 _mm_errno = MMERR_INVALID_DEVICE;
|
|
386 if (_mm_errorhandler != NULL)
|
|
387 _mm_errorhandler();
|
|
388 md_driver = &drv_nos;
|
|
389 return 1;
|
|
390 }
|
|
391 if (!md_driver->IsPresent()) {
|
|
392 _mm_errno = MMERR_DETECTING_DEVICE;
|
|
393 if (_mm_errorhandler != NULL)
|
|
394 _mm_errorhandler();
|
|
395 md_driver = &drv_nos;
|
|
396 return 1;
|
|
397 }
|
|
398 }
|
|
399
|
|
400 if (md_driver->Init()) {
|
|
401 MikMod_Exit();
|
|
402 if (_mm_errorhandler != NULL)
|
|
403 _mm_errorhandler();
|
|
404 return 1;
|
|
405 }
|
|
406 idevice = md_device;
|
|
407 imode = md_mode;
|
|
408 imixfreq = md_mixfreq;
|
|
409 idmabufsize = md_dmabufsize;
|
|
410 initialized = 1;
|
|
411 _mm_critical = 0;
|
|
412
|
|
413 return 0;
|
|
414 }
|
|
415
|
|
416
|
|
417 void
|
|
418 MikMod_Exit(void)
|
|
419 {
|
|
420 MikMod_DisableOutput();
|
|
421 md_driver->Exit();
|
|
422 md_numchn = md_sfxchn = md_sngchn = 0;
|
|
423 md_driver = &drv_nos;
|
|
424 initialized = 0;
|
|
425 }
|
|
426
|
|
427
|
|
428 /*
|
|
429 * Reset the driver using the new global variable settings. If the driver has
|
|
430 * not been initialized, it will be now.
|
|
431 */
|
|
432 BOOL
|
|
433 MikMod_Reset(void)
|
|
434 {
|
|
435 if (!initialized)
|
|
436 return MikMod_Init();
|
|
437 if ((md_driver->Reset == NULL) ||
|
|
438 (md_device != idevice)) { /* md_driver->Reset was
|
|
439 * NULL, or md_device
|
|
440 * was changed, so do a
|
|
441 * full reset of the
|
|
442 * driver. */
|
|
443 if (isplaying)
|
|
444 md_driver->PlayStop();
|
|
445 md_driver->Exit();
|
|
446 if (MikMod_Init()) {
|
|
447 MikMod_Exit();
|
|
448 if (_mm_errorhandler != NULL)
|
|
449 _mm_errorhandler();
|
|
450 return 1;
|
|
451 }
|
|
452 if (isplaying)
|
|
453 md_driver->PlayStart();
|
|
454 } else {
|
|
455 if (md_driver->Reset()) {
|
|
456 MikMod_Exit();
|
|
457 if (_mm_errorhandler != NULL)
|
|
458 _mm_errorhandler();
|
|
459 return 1;
|
|
460 }
|
|
461 }
|
|
462
|
|
463 return 0;
|
|
464 }
|
|
465
|
|
466
|
|
467 /* If either parameter is -1, the current set value will be retained. */
|
|
468 BOOL
|
|
469 MikMod_SetNumVoices(int music, int sfx)
|
|
470 {
|
|
471 BOOL resume = 0;
|
|
472 int t, oldchn = 0;
|
|
473
|
|
474 if ((music == 0) && (sfx == 0))
|
|
475 return 0;
|
|
476
|
|
477 _mm_critical = 1;
|
|
478
|
|
479 if (isplaying) {
|
|
480 MikMod_DisableOutput();
|
|
481 oldchn = md_numchn;
|
|
482 resume = 1;
|
|
483 }
|
|
484 if (sfxinfo != NULL)
|
|
485 free(sfxinfo);
|
|
486 if (md_sample != NULL)
|
|
487 free(md_sample);
|
|
488 md_sample = NULL;
|
|
489 sfxinfo = NULL;
|
|
490
|
|
491 /*
|
|
492 * md_softchn = md_hardchn = 0;
|
|
493 *
|
|
494 * if(md_mode & DMODE_SOFT_SNDFX) md_softchn = sfx; else md_hardchn = sfx;
|
|
495 *
|
|
496 * if(md_mode & DMODE_SOFT_MUSIC) md_softchn += music; else md_hardchn +=
|
|
497 * music;
|
|
498 */
|
|
499
|
|
500 if (music != -1)
|
|
501 md_sngchn = music;
|
|
502 if (sfx != -1)
|
|
503 md_sfxchn = sfx;
|
|
504
|
|
505 md_numchn = md_sngchn + md_sfxchn;
|
|
506
|
|
507 LimitHardVoices(md_driver->HardVoiceLimit);
|
|
508 LimitSoftVoices(md_driver->SoftVoiceLimit);
|
|
509
|
|
510 if (md_driver->SetNumVoices()) {
|
|
511 MikMod_Exit();
|
|
512 md_numchn = md_softchn = md_hardchn = md_sfxchn = md_sngchn = 0;
|
|
513 if (_mm_errorhandler != NULL)
|
|
514 _mm_errorhandler();
|
|
515 return 1;
|
|
516 }
|
|
517 if (md_sngchn || md_sfxchn)
|
|
518 md_sample = (SAMPLE **) _mm_calloc(md_sngchn + md_sfxchn, sizeof(SAMPLE *));
|
|
519 if (md_sfxchn)
|
|
520 sfxinfo = (UBYTE *) _mm_calloc(md_sfxchn, sizeof(UBYTE));
|
|
521
|
|
522 /* make sure the player doesn't start with garbage */
|
|
523 for (t = oldchn; t < md_numchn; t++)
|
|
524 Voice_Stop(t);
|
|
525
|
|
526 sfxpool = 0;
|
|
527
|
|
528 if (resume)
|
|
529 MikMod_EnableOutput();
|
|
530 _mm_critical = 0;
|
|
531
|
|
532 return 0;
|
|
533 }
|
|
534
|
|
535
|
|
536 BOOL
|
|
537 MikMod_EnableOutput(void)
|
|
538 {
|
|
539 /*
|
|
540 * safety valve, prevents entering playstart twice:
|
|
541 */
|
|
542 _mm_critical = 1;
|
|
543 if (!isplaying) {
|
|
544 if (md_driver->PlayStart())
|
|
545 return 1;
|
|
546 isplaying = 1;
|
|
547 }
|
|
548 _mm_critical = 0;
|
|
549 return 0;
|
|
550 }
|
|
551
|
|
552
|
|
553 void
|
|
554 MikMod_DisableOutput(void)
|
|
555 {
|
|
556 /*
|
|
557 * safety valve, prevents calling playStop when playstart hasn't been
|
|
558 * called:
|
|
559 */
|
|
560
|
|
561 if (isplaying && md_driver != NULL) {
|
|
562 isplaying = 0;
|
|
563 md_driver->PlayStop();
|
|
564 }
|
|
565 }
|
|
566
|
|
567
|
|
568 BOOL
|
|
569 MikMod_Active(void)
|
|
570 {
|
|
571 return isplaying;
|
|
572 }
|
|
573
|
|
574
|
|
575 /*
|
|
576 * Plays a sound effects sample. Picks a voice from the number of voices
|
|
577 * allocated for use as sound effects (loops through voices, skipping all
|
|
578 * active criticals).
|
|
579 *
|
|
580 * Returns the voice that the sound is being played on.
|
|
581 */
|
|
582 int
|
|
583 MikMod_PlaySample(SAMPLE * s, ULONG start, UBYTE flags)
|
|
584 {
|
|
585 int orig = sfxpool; /* for cases where all channels are
|
|
586 * critical */
|
|
587 int c;
|
|
588
|
|
589 if (md_sfxchn == 0)
|
|
590 return -1;
|
|
591 if (s->volume > 64)
|
|
592 s->volume = 64;
|
|
593
|
|
594 /* check the first location after sfxpool */
|
|
595 do {
|
|
596 if (sfxinfo[sfxpool] & SFX_CRITICAL) {
|
|
597 if (md_driver->VoiceStopped(c = sfxpool + md_sngchn)) {
|
|
598 sfxinfo[sfxpool] = flags;
|
|
599 Voice_Play(c, s, start);
|
|
600 md_driver->VoiceSetVolume(c, s->volume << 2);
|
|
601 md_driver->VoiceSetPanning(c, s->panning);
|
|
602 md_driver->VoiceSetFrequency(c, s->speed);
|
|
603 sfxpool++;
|
|
604 if (sfxpool >= md_sfxchn)
|
|
605 sfxpool = 0;
|
|
606 return c;
|
|
607 }
|
|
608 } else {
|
|
609 sfxinfo[sfxpool] = flags;
|
|
610 Voice_Play(c = sfxpool + md_sngchn, s, start);
|
|
611 md_driver->VoiceSetVolume(c, s->volume << 2);
|
|
612 md_driver->VoiceSetPanning(c, s->panning);
|
|
613 md_driver->VoiceSetFrequency(c, s->speed);
|
|
614 sfxpool++;
|
|
615 if (sfxpool >= md_sfxchn)
|
|
616 sfxpool = 0;
|
|
617 return c;
|
|
618 }
|
|
619
|
|
620 sfxpool++;
|
|
621 if (sfxpool >= md_sfxchn)
|
|
622 sfxpool = 0;
|
|
623 } while (sfxpool != orig);
|
|
624
|
|
625 return -1;
|
|
626 }
|