]> icculus.org git repositories - btb/d2x.git/blob - arch/dos/digimm.c
more header cleanup
[btb/d2x.git] / arch / dos / digimm.c
1 // SDL digital audio support
2
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6
7 #include "mm_drv.h"
8 #include "timer.h"
9
10 //#include "SDL.h"
11 //#include "SDL_audio.h"
12
13 #include "error.h"
14 #include "mono.h"
15 #include "fix.h"
16 #include "gr.h" // needed for piggy.h
17 #include "inferno.h"
18 #include "midiallg.h"
19
20
21 int digi_driver_board                                   = 0;
22 int digi_driver_port                                    = 0;
23 int digi_driver_irq                                     = 0;
24 int digi_driver_dma                                     = 0;
25 //int digi_midi_type                                    = 0;                    // Midi driver type
26 //int digi_midi_port                                    = 0;                    // Midi driver port
27 int digi_timer_rate                                     = 9943;                 // rate for the timer to go off to handle the driver system (120 Hz)
28
29 #ifndef ALLG_MIDI
30 /* stub vars/functions for midi */
31 int digi_midi_type                                      = 0;
32 int digi_midi_port                                     = 0;
33
34 void digi_set_midi_volume( int mvolume ) {}
35 void digi_play_midi_song( char * filename, char * melodic_bank,
36 char * drum_bank, int loop ) {}
37 void digi_midi_pause() {}
38 void digi_midi_resume() {}
39 void digi_midi_stop() {}
40 #endif
41
42
43 //added on 980905 by adb to add inline fixmul for mixer on i386
44 #ifdef __i386__
45 #define do_fixmul(x,y)                          \
46 ({                                              \
47         int _ax, _dx;                           \
48         asm("imull %2\n\tshrdl %3,%1,%0"        \
49             : "=a"(_ax), "=d"(_dx)              \
50             : "rm"(y), "i"(16), "0"(x));        \
51         _ax;                                    \
52 })
53 extern inline fix fixmul(fix x, fix y) { return do_fixmul(x,y); }
54 #endif
55 //end edit by adb
56
57 //changed on 980905 by adb to increase number of concurrent sounds
58 #define MAX_SOUND_SLOTS 32
59 //end changes by adb
60 #define SOUND_BUFFER_SIZE 512
61
62 #define MIN_VOLUME 10
63
64 /* This table is used to add two sound values together and pin
65  * the value to avoid overflow.  (used with permission from ARDI)
66  * DPH: Taken from SDL/src/SDL_mixer.c.
67  */
68 static const unsigned char mix8[] =
69 {
70   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
71   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
72   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
73   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
74   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
76   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
77   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
78   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
79   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
80   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
81   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
82   0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
83   0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
84   0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
85   0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
86   0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
87   0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
88   0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
89   0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B,
90   0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
91   0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
92   0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C,
93   0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
94   0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92,
95   0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,
96   0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
97   0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3,
98   0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE,
99   0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9,
100   0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4,
101   0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
102   0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA,
103   0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5,
104   0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0xFF,
105   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
106   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
107   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
108   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
109   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
110   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
111   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
112   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
113   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
114   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
115   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
116   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
117 };
118
119
120 //added/changed on 980905 by adb to make sfx volume work
121 #define SOUND_MAX_VOLUME (F1_0/2) // don't use real max like in original
122 int digi_volume = SOUND_MAX_VOLUME;
123 //end edit by adb
124
125 static int digi_initialised = 0;
126 static int timer_system_initialized = 0;
127
128 struct sound_slot {
129  int soundno;
130  int playing;   // Is there a sample playing on this channel?
131  int looped;    // Play this sample looped?
132  fix pan;       // 0 = far left, 1 = far right
133  fix volume;    // 0 = nothing, 1 = fully on
134  //changed on 980905 by adb from char * to unsigned char * 
135  unsigned char *samples;
136  //end changes by adb
137  unsigned int length; // Length of the sample
138  unsigned int position; // Position we are at at the moment.
139         int soundobj;   // Which soundobject is on this channel
140         int persistent; // This can't be pre-empted
141 } SoundSlots[MAX_SOUND_SLOTS];
142
143 //static SDL_AudioSpec WaveSpec;
144
145 static int digi_max_channels = 16;
146
147 static int next_channel = 0;
148
149 /* Audio mixing callback */
150 //changed on 980905 by adb to cleanup, add pan support and optimize mixer
151 ULONG VC_WriteBytes(SBYTE *stream, ULONG len)
152 {
153  unsigned char *streamend;
154  struct sound_slot *sl;
155
156 //    if (grd_curscreen)
157 //        grd_curscreen->sc_canvas.cv_bitmap.bm_data[8]++;
158  len &= ~1; /* stereo -> always write 2 byte pairs */
159  streamend = stream + len;
160
161 #if 0
162 {
163  static int n = 0;
164  while(stream < streamend) {
165     *(stream++) = (n & 256) ? 256 - (n & 255) : (n & 255);
166     n++;
167  }
168  return len;
169 }
170 #endif
171
172  memset(stream, 0x80, len);
173
174  for (sl = SoundSlots; sl < SoundSlots + MAX_SOUND_SLOTS; sl++)
175  {
176   if (sl->playing)
177   {
178    unsigned char *sldata = sl->samples + sl->position, *slend = sl->samples + sl->length;
179    unsigned char *sp = stream;
180    signed char v;
181    fix vl, vr;
182    int x;
183
184    if ((x = sl->pan) & 0x8000)
185    {
186     vl = 0x20000 - x * 2;
187     vr = 0x10000;
188    }
189    else
190    {
191     vl = 0x10000;
192     vr = x * 2;
193    }
194    vl = fixmul(vl, (x = sl->volume));
195    vr = fixmul(vr, x);
196    while (sp < streamend) 
197    {
198     if (sldata == slend)
199     {
200      if (!sl->looped)
201      {
202       sl->playing = 0;
203       break;
204      }
205      sldata = sl->samples;
206     }
207     v = *(sldata++) - 0x80;
208     *(sp++) = mix8[ *sp + fixmul(v, vl) + 0x80 ];
209     *(sp++) = mix8[ *sp + fixmul(v, vr) + 0x80 ];
210    }
211    sl->position = sldata - sl->samples;
212   }
213  }
214  return len;
215 }
216 //end changes by adb
217
218 extern MDRIVER drv_sb;
219 MDRIVER *drv = &drv_sb;
220 char allegro_error[128];
221
222 int md_mode = DMODE_STEREO;
223 int md_mixfreq = digi_sample_rate; //11025;
224 int md_dmabufsize = 1024;
225
226 void install_int_ex(void (*)(), long speed);
227 void remove_int(void(*)());
228
229 void mm_timer() {
230     drv->Update();
231 //    if (grd_curscreen)
232 //        (*grd_curscreen->sc_canvas.cv_bitmap.bm_data)++;
233 }
234
235 /* Initialise audio devices. */
236 int digi_init()
237 {
238  #if 0
239  WaveSpec.freq = digi_sample_rate; //11025;
240  WaveSpec.format = AUDIO_U8 | AUDIO_STEREO;
241  WaveSpec.samples = SOUND_BUFFER_SIZE;
242  WaveSpec.callback = audio_mixcallback;
243
244  if ( SDL_OpenAudio(&WaveSpec, NULL) < 0 ) {
245   printf("Couldn't open audio: %s\n", SDL_GetError());
246   exit(2);
247  }
248  SDL_PauseAudio(0);
249 #endif
250  if (!drv->Init()) {
251         printf("Couldn't open audio: %s", myerr);
252         return -1;
253  }
254  drv->PlayStart();
255
256  if (!timer_system_initialized)
257  {
258   #ifdef ALLG_MIDI
259   install_int_ex(mm_timer, digi_timer_rate);
260   #else
261   timer_set_function(mm_timer);
262   #endif
263   timer_system_initialized = 1;
264  }
265
266  #ifdef ALLG_MIDI
267  digi_midi_init();
268  #endif
269
270  atexit(digi_close);
271  digi_initialised = 1;
272  return 0;
273 }
274
275 /* Toggle audio */
276 void digi_reset() { }
277
278 /* Shut down audio */
279 void digi_close()
280 {
281  if (!digi_initialised) return;
282  digi_initialised = 0;
283  drv->PlayStop();
284  drv->Exit();
285  if (timer_system_initialized)
286  {
287   #ifdef ALLG_MIDI
288   remove_int(mm_timer);
289   #else
290   timer_set_function(NULL);
291   #endif
292   timer_system_initialized = 0;
293  }
294  #ifdef ALLG_MIDI
295  digi_midi_close();
296  #endif
297 }
298
299 void digi_stop_all_channels()
300 {
301         int i;
302
303         for (i = 0; i < MAX_SOUND_SLOTS; i++)
304                 digi_stop_sound(i);
305 }
306
307
308 extern void digi_end_soundobj(int channel);     
309 extern int SoundQ_channel;
310 extern void SoundQ_end();
311 int verify_sound_channel_free(int channel);
312
313 // Volume 0-F1_0
314 int digi_start_sound(short soundnum, fix volume, int pan, int looping, int loop_start, int loop_end, int soundobj)
315 {
316         int i, starting_channel;
317
318         if (!digi_initialised) return -1;
319
320         if (soundnum < 0) return -1;
321
322         Assert(GameSounds[soundnum].data != (void *)-1);
323
324         starting_channel = next_channel;
325
326         while(1)
327         {
328                 if (!SoundSlots[next_channel].playing)
329                         break;
330
331                 if (!SoundSlots[next_channel].persistent)
332                         break;  // use this channel!    
333
334                 next_channel++;
335                 if (next_channel >= digi_max_channels)
336                         next_channel = 0;
337                 if (next_channel == starting_channel)
338                 {
339                         mprintf((1, "OUT OF SOUND CHANNELS!!!\n"));
340                         return -1;
341                 }
342         }
343         if (SoundSlots[next_channel].playing)
344         {
345                 SoundSlots[next_channel].playing = 0;
346                 if (SoundSlots[next_channel].soundobj > -1)
347                 {
348                         digi_end_soundobj(SoundSlots[next_channel].soundobj);
349                 }
350                 if (SoundQ_channel == next_channel)
351                         SoundQ_end();
352         }
353
354 #ifndef NDEBUG
355         verify_sound_channel_free(next_channel);
356 #endif
357
358         SoundSlots[next_channel].soundno = soundnum;
359         SoundSlots[next_channel].samples = GameSounds[soundnum].data;
360         SoundSlots[next_channel].length = GameSounds[soundnum].length;
361         SoundSlots[next_channel].volume = fixmul(digi_volume, volume);
362         SoundSlots[next_channel].pan = pan;
363         SoundSlots[next_channel].position = 0;
364         SoundSlots[next_channel].looped = looping;
365         SoundSlots[next_channel].playing = 1;
366         SoundSlots[next_channel].soundobj = soundobj;
367         SoundSlots[next_channel].persistent = 0;
368         if ((soundobj > -1) || (looping) || (volume > F1_0))
369                 SoundSlots[next_channel].persistent = 1;
370
371         i = next_channel;
372         next_channel++;
373         if (next_channel >= digi_max_channels)
374                 next_channel = 0;
375
376         return i;
377 }
378
379 // Returns the channel a sound number is playing on, or
380 // -1 if none.
381 int digi_find_channel(int soundno)
382 {
383         if (!digi_initialised)
384                 return -1;
385
386         if (soundno < 0 )
387                 return -1;
388
389         if (GameSounds[soundno].data == NULL)
390         {
391                 Int3();
392                 return -1;
393         }
394
395         //FIXME: not implemented
396         return -1;
397 }
398
399
400 //added on 980905 by adb from original source to make sfx volume work
401 void digi_set_digi_volume( int dvolume )
402 {
403         dvolume = fixmuldiv( dvolume, SOUND_MAX_VOLUME, 0x7fff);
404         if ( dvolume > SOUND_MAX_VOLUME )
405                 digi_volume = SOUND_MAX_VOLUME;
406         else if ( dvolume < 0 )
407                 digi_volume = 0;
408         else
409                 digi_volume = dvolume;
410
411         if ( !digi_initialised ) return;
412
413         digi_sync_sounds();
414 }
415 //end edit by adb
416
417 void digi_set_volume( int dvolume, int mvolume )
418 {
419  digi_set_midi_volume(mvolume);
420  digi_set_digi_volume(dvolume);
421 }
422
423
424 int digi_is_sound_playing(int soundno)
425 {
426         int i;
427
428         soundno = digi_xlat_sound(soundno);
429
430         for (i = 0; i < MAX_SOUND_SLOTS; i++)
431                   //changed on 980905 by adb: added SoundSlots[i].playing &&
432                   if (SoundSlots[i].playing && SoundSlots[i].soundno == soundno)
433                   //end changes by adb
434                         return 1;
435         return 0;
436 }
437
438
439  //added on 980905 by adb to make sound channel setting work
440 void digi_set_max_channels(int n) { 
441         digi_max_channels       = n;
442
443         if ( digi_max_channels < 1 ) 
444                 digi_max_channels = 1;
445         if (digi_max_channels > MAX_SOUND_SLOTS)
446                 digi_max_channels = MAX_SOUND_SLOTS;
447
448         if ( !digi_initialised ) return;
449
450         digi_stop_all_channels();
451 }
452
453 int digi_get_max_channels() { 
454         return digi_max_channels; 
455 }
456 // end edit by adb
457
458 int digi_is_channel_playing(int channel)
459 {
460         if (!digi_initialised)
461                 return 0;
462
463         return SoundSlots[channel].playing;
464 }
465
466 void digi_set_channel_volume(int channel, int volume)
467 {
468         if (!digi_initialised)
469                 return;
470
471         if (!SoundSlots[channel].playing)
472                 return;
473
474         SoundSlots[channel].volume = fixmuldiv(volume, digi_volume, F1_0);
475 }
476
477 void digi_set_channel_pan(int channel, int pan)
478 {
479         if (!digi_initialised)
480                 return;
481
482         if (!SoundSlots[channel].playing)
483                 return;
484
485         SoundSlots[channel].pan = pan;
486 }
487
488 void digi_stop_sound(int channel)
489 {
490         SoundSlots[channel].playing=0;
491         SoundSlots[channel].soundobj = -1;
492         SoundSlots[channel].persistent = 0;
493 }
494
495 void digi_end_sound(int channel)
496 {
497         if (!digi_initialised)
498                 return;
499
500         if (!SoundSlots[channel].playing)
501                 return;
502
503         SoundSlots[channel].soundobj = -1;
504         SoundSlots[channel].persistent = 0;
505 }
506
507 #ifndef NDEBUG
508 void digi_debug()
509 {
510         int i;
511         int n_voices = 0;
512
513         if (!digi_initialised)
514                 return;
515
516         for (i = 0; i < digi_max_channels; i++)
517         {
518                 if (digi_is_channel_playing(i))
519                         n_voices++;
520         }
521
522         mprintf_at((0, 2, 0, "DIGI: Active Sound Channels: %d/%d (HMI says %d/32)      ", n_voices, digi_max_channels, -1));
523         //mprintf_at((0, 3, 0, "DIGI: Number locked sounds:  %d                          ", digi_total_locks ));
524 }
525 #endif
526
527
528 // mikmod stubs...
529 BOOL    VC_Init(void) { return 1; }
530 void    VC_Exit(void) { }
531 BOOL    VC_SetNumVoices(void) { return 0; }
532 ULONG   VC_SampleSpace(int type) { return 0; }
533 ULONG   VC_SampleLength(int type, SAMPLE *s) { return 0; }
534
535 BOOL    VC_PlayStart(void) { return 0; }
536 void    VC_PlayStop(void) { }
537
538 #if 0
539 SWORD   VC_SampleLoad(SAMPLOAD *sload, int type, FILE *fp) { return 0; }
540 #else
541 SWORD   VC_SampleLoad(FILE *fp,ULONG size,ULONG reppos,ULONG repend,UWORD flags) { return 0; }
542 #endif
543 void    VC_SampleUnload(SWORD handle) { }
544
545 void    VC_WriteSamples(SBYTE *buf,ULONG todo) { }
546 void    VC_SilenceBytes(SBYTE *buf,ULONG todo) { }
547
548 #if 0
549 void    VC_VoiceSetVolume(UBYTE voice, UWORD vol) { }
550 void    VC_VoiceSetPanning(UBYTE voice, ULONG pan) { }
551 #else
552 void    VC_VoiceSetVolume(UBYTE voice, UBYTE vol) { }
553 void    VC_VoiceSetPanning(UBYTE voice, UBYTE pan) { }
554 #endif
555 void    VC_VoiceSetFrequency(UBYTE voice, ULONG frq) { }
556 void    VC_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags) { }
557
558 void    VC_VoiceStop(UBYTE voice) { }
559 BOOL    VC_VoiceStopped(UBYTE voice) { return 0; }
560 void    VC_VoiceReleaseSustain(UBYTE voice) { }
561 SLONG   VC_VoiceGetPosition(UBYTE voice) { return 0; }
562 ULONG   VC_VoiceRealVolume(UBYTE voice) { return 0; }
563 char *myerr;