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