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