1 //
2 // ID Engine
3 // ID_SD.c - Sound Manager for Wolfenstein 3D
4 // v1.2
5 // By Jason Blochowiak
6 //
7
8 //
9 // This module handles dealing with generating sound on the appropriate
10 // hardware
11 //
12 // Depends on: User Mgr (for parm checking)
13 //
14 // Globals:
15 // For User Mgr:
16 // SoundBlasterPresent - SoundBlaster card present?
17 // AdLibPresent - AdLib card present?
18 // SoundMode - What device is used for sound effects
19 // (Use SM_SetSoundMode() to set)
20 // MusicMode - What device is used for music
21 // (Use SM_SetMusicMode() to set)
22 // DigiMode - What device is used for digitized sound effects
23 // (Use SM_SetDigiDevice() to set)
24 //
25 // For Cache Mgr:
26 // NeedsDigitized - load digitized sounds?
27 // NeedsMusic - load music?
28 //
29 #include "wl_def.h"
30 #include <SDL_mixer.h>
31 #include "w_wad.h"
32 #include "zstring.h"
33 #include "sndinfo.h"
34 #include "sndseq.h"
35 #ifdef USE_GPL
36 #include "dosbox/dbopl.h"
37 #else
38 #include "mame/fmopl.h"
39 #endif
40 #include "wl_main.h"
41 #include "id_sd.h"
42
43 // For AdLib sounds & music:
44 #define MUSIC_RATE 700 // Must be a multiple of SOUND_RATE
45 #define SOUND_RATE 140 // Also affects PC Speaker sounds
46 #define SOUND_TICKS (MUSIC_RATE/SOUND_RATE)
47
48 // Linear falloff
49 //#define TO_SDL_POSITION(pos) (((15 - (pos)) << 4) + 15)
50 // Volume is based on the square of the distance to the source. (More like vanilla)
51 #define TO_SDL_POSITION(pos) (((64 - ((pos) * (pos))) * 3) + 63)
52
53 #define MIN_TICKS_BETWEEN_DIGI_REPEATS 10
54
55 // Mutex for thread-safe audio:
56 SDL_mutex *audioMutex;
57
58 globalsoundpos channelSoundPos[MIX_CHANNELS];
59
60 // Global variables
61 bool AdLibPresent,
62 SoundBlasterPresent,SBProPresent,
63 SoundPositioned;
64 SDMode SoundMode;
65 SMMode MusicMode;
66 SDSMode DigiMode;
67 int AdlibVolume=MAX_VOLUME;
68 int MusicVolume=MAX_VOLUME;
69 int SoundVolume=MAX_VOLUME;
70
71 // Internal variables
72 static bool SD_Started;
73 static bool nextsoundpos;
74 FString SoundPlaying;
75 static word SoundPriority;
76 static word DigiPriority;
77 static int LeftPosition;
78 static int RightPosition;
79
80 static bool DigiPlaying;
81
82 // PC Sound variables
83 static volatile byte pcLastSample;
84 static byte * volatile pcSound;
85 static longword pcLengthLeft;
86
87 // AdLib variables
88 static byte * volatile alSound;
89 static byte alBlock;
90 static longword alLengthLeft;
91 static longword alTimeCount;
92 static Instrument alZeroInst;
93
94 // Sequencer variables
95 static volatile bool sqActive;
96 static word *sqHack;
97 static word *sqHackFreeable=NULL;
98 static word *sqHackPtr;
99 static int sqHackLen;
100 static int sqHackSeqLen;
101 static longword sqHackTime;
102
103 static int musicchunk=-1;
104 Mix_Music *music=NULL;
105 byte* chunkmem = NULL;
106
musicFinished(void)107 void musicFinished(void)
108 {
109 if (music != NULL)
110 {
111 Mix_HaltMusic();
112 Mix_FreeMusic(music);
113 music = NULL;
114
115 delete [] chunkmem;
116 chunkmem = NULL;
117
118 musicchunk = -1;
119 }
120 }
121
SD_UpdateMusicVolume(int which)122 bool SD_UpdateMusicVolume(int which)
123 {
124 Mix_VolumeMusic(static_cast<int> (ceil(128.0*MULTIPLY_VOLUME(MusicVolume))));
125 return 0;
126 }
127
128 #ifdef USE_GPL
129
130 DBOPL::Chip oplChip;
131
YM3812Init(int numChips,int clock,int rate)132 static inline bool YM3812Init(int numChips, int clock, int rate)
133 {
134 oplChip.Setup(rate);
135 return false;
136 }
137
YM3812Write(DBOPL::Chip & which,Bit32u reg,Bit8u val,const int & volume)138 static inline void YM3812Write(DBOPL::Chip &which, Bit32u reg, Bit8u val, const int &volume)
139 {
140 which.SetVolume(volume);
141 which.WriteReg(reg, val);
142 }
143
YM3812UpdateOne(DBOPL::Chip & which,int16_t * stream,int length)144 static inline void YM3812UpdateOne(DBOPL::Chip &which, int16_t *stream, int length)
145 {
146 Bit32s buffer[512 * 2];
147 int i;
148
149 // length is at maximum samplesPerMusicTick = param_samplerate / 700
150 // so 512 is sufficient for a sample rate of 358.4 kHz (default 44.1 kHz)
151 if(length > 512)
152 length = 512;
153
154 if(which.opl3Active)
155 {
156 which.GenerateBlock3(length, buffer);
157
158 // GenerateBlock3 generates a number of "length" 32-bit stereo samples
159 // so we only need to convert them to 16-bit samples
160 for(i = 0; i < length * 2; i++) // * 2 for left/right channel
161 {
162 // Multiply by 4 to match loudness of MAME emulator.
163 Bit32s sample = buffer[i] << 2;
164 if(sample > 32767) sample = 32767;
165 else if(sample < -32768) sample = -32768;
166 stream[i] = LittleShort(sample);
167 }
168 }
169 else
170 {
171 which.GenerateBlock2(length, buffer);
172
173 // GenerateBlock3 generates a number of "length" 32-bit mono samples
174 // so we need to convert them to 32-bit stereo samples
175 for(i = 0; i < length; i++)
176 {
177 // Multiply by 4 to match loudness of MAME emulator.
178 // Then upconvert to stereo.
179 Bit32s sample = buffer[i] << 2;
180 if(sample > 32767) sample = 32767;
181 else if(sample < -32768) sample = -32768;
182 stream[i * 2] = stream[i * 2 + 1] = (int16_t) LittleShort(sample);
183 }
184 }
185 }
186
187 #else
188
189 static const int oplChip = 0;
190
191 #endif
192
193 #ifndef ECWOLF_MIXER
Mix_SetMusicPCMPosition(Uint64 position)194 static int Mix_SetMusicPCMPosition(Uint64 position) { return 0; }
Mix_GetMusicPCMPosition()195 static Uint64 Mix_GetMusicPCMPosition() { return 0; }
196 #endif
197
SDL_SoundFinished(void)198 static void SDL_SoundFinished(void)
199 {
200 SoundPlaying = FString();
201 SoundPriority = 0;
202 }
203
204 /*
205 =============================================================================
206
207 PC SPEAKER EMULATOR -- by K1n9_Duk3
208
209 -----------------------------------------------------------------------------
210
211 This emulator was designed to be FAST! The sampled audio data created by this
212 code might not be 100% true to the output of a real PC Speaker, but it is
213 close enough.
214
215 The emulator generates a square wave:
216 _____ _____ _____ _____ _____ _____
217 __| | | | | | | | | | | |___
218 |_____| |_____| |_____| |_____| |_____|
219
220 =============================================================================
221 */
222
223 static short pcVolume = 5000; // for 16-bit mixing (8-bit should use 20)
224 static longword pcPhaseTick = 0;
225 static longword pcPhaseLength;
226 static bool pcActive = false;
227 static longword pcSamplesPerTick;
228 static longword pcNumReadySamples = 0;
229
230 #define PC_BASE_TIMER 1193181
231
232 // Function prototype is for menu listener
SD_UpdatePCSpeakerVolume(int)233 bool SD_UpdatePCSpeakerVolume(int)
234 {
235 SDL_LockMutex(audioMutex);
236
237 if(pcVolume > 0)
238 pcVolume = AdlibVolume*250;
239 else
240 pcVolume = -AdlibVolume*250;
241
242 SDL_UnlockMutex(audioMutex);
243
244 return true;
245 }
246
247 // Note: The inline functions do not lock or unlock 'audioMutex'. Make sure
248 // that the code calling these functions locks/unlocks the mutex before/after
249 // calling these functions!
250
_SDL_turnOnPCSpeaker(byte pcSample)251 inline void _SDL_turnOnPCSpeaker(byte pcSample)
252 {
253 // Note: You could use a lookup table to make this even faster. Only
254 // 256 table entries are required since the PC samples are just byte
255 // values.
256 pcPhaseLength = (pcSample*60*param_samplerate)/(2*PC_BASE_TIMER);
257 #ifdef PC_VIBRATO
258 //if(pcVolume<0) pcVolume = -pcVolume;
259 pcPhaseTick = 0;
260 #endif
261 pcActive = true;
262 }
263
_SDL_turnOffPCSpeaker()264 inline void _SDL_turnOffPCSpeaker()
265 {
266 pcActive = false;
267 pcPhaseTick = 0; // Only required in case PC_VIBRATO is not defined
268 }
269
_SDL_PCService()270 inline void _SDL_PCService()
271 {
272 if(pcSound)
273 {
274 if(*pcSound!=pcLastSample)
275 {
276 pcLastSample=*pcSound;
277 if(pcLastSample)
278 _SDL_turnOnPCSpeaker(pcLastSample); // don't multiply by 60, just pass the byte value
279 else
280 _SDL_turnOffPCSpeaker();
281 }
282 pcSound++;
283 pcLengthLeft--;
284 if(!pcLengthLeft)
285 {
286 pcSound=0;
287 SoundPriority=0;
288 _SDL_turnOffPCSpeaker();
289 }
290 }
291 }
292
293 ///////////////////////////////////////////////////////////////////////////
294 //
295 // SDL_PCPlaySound() - Plays the specified sound on the PC speaker
296 //
297 ///////////////////////////////////////////////////////////////////////////
298 static void
_SDL_PCPlaySound(PCSound * sound)299 _SDL_PCPlaySound(PCSound *sound)
300 {
301 // We must stop the digitized sound because we're just faking the digitized PC Speaker playback
302 if(DigiMode == sds_PC)
303 SD_StopDigitized();
304
305 SDL_LockMutex(audioMutex);
306
307 pcPhaseTick = 0;
308 pcLastSample = 0; // Must be a value that cannot be played, so the PC Speaker is forced to reset (-1 wraps to 255 so it cannot be used here)
309 pcLengthLeft = LittleLong(sound->common.length);
310 pcSound = sound->data;
311
312 SDL_UnlockMutex(audioMutex);
313 }
314
315 ///////////////////////////////////////////////////////////////////////////
316 //
317 // SDL_PCStopSound() - Stops the current sound playing on the PC Speaker
318 //
319 ///////////////////////////////////////////////////////////////////////////
320 static void
_SDL_PCStopSound(void)321 _SDL_PCStopSound(void)
322 {
323 SDL_LockMutex(audioMutex);
324
325 pcSound = 0;
326 _SDL_turnOffPCSpeaker();
327
328 SDL_UnlockMutex(audioMutex);
329 }
330
331 ///////////////////////////////////////////////////////////////////////////
332 //
333 // SDL_ShutPC() - Turns off the pc speaker
334 //
335 ///////////////////////////////////////////////////////////////////////////
336 static void
_SDL_ShutPC(void)337 _SDL_ShutPC(void)
338 {
339 _SDL_PCStopSound();
340 }
341
342
343 ///////////////////////////////////////////////////////////////////////////
344 //
345 // SDL_EmulateAndMixPC() - Emulates the pc speaker
346 // and mixes the output into buffer
347 //
348 ///////////////////////////////////////////////////////////////////////////
_SDL_EmulateAndMixPC(Sint16 * buffer,int length)349 void _SDL_EmulateAndMixPC(Sint16 *buffer, int length)
350 {
351 // This should be called in SDL_IMFMusicPlayer() directly after calling
352 // YM3812UpdateOne() so that the PC sounds can be mixed into the output
353 // of the OPL emulator. This way, all sound hardware is emulated in the
354 // same routine. The audio gets mixed into the Music channel, so we do
355 // not need an additional channel for the PC Speaker sounds.
356
357 // Note: This code assumes that 'buffer' is a signed 16-bit stereo sound!
358
359 Sint32 mix; // Needs more bits than the buffer
360
361 if(!pcActive) return; // PC Speaker is turned off
362
363 SDL_LockMutex(audioMutex);
364
365 while(length--)
366 {
367 mix = *buffer;
368
369 // Mix it by simply adding the volume:
370 mix += pcVolume;
371 if (mix < -32768) mix = -32768;
372 else if (mix > 32767) mix = 32767;
373 // This generates pretty much the same output as SDL_MixAudio(), but
374 // it does not require a second buffer for the PC Speaker sample data.
375 //
376 // Note: If you use another mixing method, you cannot simply return
377 // from this function if pcActive is false. You will have to mix a
378 // PC Volume of 0 into the entire buffer!
379
380 // We assume that the left and right channel in the buffer contain
381 // the same value, so we only need to calculate the mix once.
382 *buffer++ = mix; //left channel
383 *buffer++ = mix; //right channel
384
385 // Update the PC speaker state:
386 if(pcPhaseTick++ >= pcPhaseLength)
387 {
388 pcVolume = -pcVolume;
389 pcPhaseTick = 0;
390 }
391 }
392
393 SDL_UnlockMutex(audioMutex);
394 }
395
396 ///////////////////////////////////////////////////////////////////////////
397 //
398 // SDL_PCSpeakerEmulator() - Emulates the pc speaker
399 // (replaces SDL_IMFMusicPlayer if no AdLib emulator is present)
400 //
401 ///////////////////////////////////////////////////////////////////////////
_SDL_PCSpeakerEmulator(void * udata,Uint8 * stream,int len)402 void _SDL_PCSpeakerEmulator(void *udata, Uint8 *stream, int len)
403 {
404 int stereolen = len>>1;
405 int sampleslen = stereolen>>1;
406 Sint16 *stream16 = (Sint16 *) (void *) stream; // expect correct alignment
407
408 SDL_LockMutex(audioMutex);
409
410 while(1)
411 {
412 if(pcNumReadySamples)
413 {
414 if(pcActive)
415 while(pcNumReadySamples && sampleslen)
416 {
417 pcNumReadySamples--;
418 sampleslen--;
419
420 *stream16++ = pcVolume;
421 *stream16++ = pcVolume;
422
423 if(pcPhaseTick++ >= pcPhaseLength)
424 {
425 pcVolume = -pcVolume;
426 pcPhaseTick = 0;
427 }
428 }
429 else
430 while(pcNumReadySamples && sampleslen)
431 {
432 pcNumReadySamples--;
433 sampleslen--;
434
435 stream16 += 2; // No need to set it to 0. SDL should have done that already.
436 }
437
438 if(!sampleslen)
439 break; // We need to unlock the mutex, so we cannot just return!
440 }
441
442 _SDL_PCService();
443
444 pcNumReadySamples = pcSamplesPerTick;
445
446 }
447
448 SDL_UnlockMutex(audioMutex);
449 }
450 /*
451 =============================================================================
452 ======================== End of PC Speaker emulator ========================
453 =============================================================================
454 */
455 #define SDL_PCPlaySound(pcsound) _SDL_PCPlaySound(pcsound)
456 #define SDL_PCStopSound() _SDL_PCStopSound()
457 #define SDL_ShutPC() _SDL_ShutPC()
458 #define SDL_PCService() _SDL_PCService()
459 #define SDL_PCEmulateAndMix(buffer, length) if(SoundMode == sdm_PC) _SDL_EmulateAndMixPC(buffer, length)
460
SD_StopDigitized(void)461 void SD_StopDigitized(void)
462 {
463 DigiPlaying = false;
464 DigiPriority = 0;
465 SoundPositioned = false;
466 if ((DigiMode == sds_PC) && (SoundMode == sdm_PC))
467 SDL_SoundFinished();
468
469 Mix_HaltChannel(-1);
470 }
471
SD_SetPosition(int channel,int leftpos,int rightpos)472 void SD_SetPosition(int channel, int leftpos, int rightpos)
473 {
474 if((leftpos < 0) || (leftpos > 15) || (rightpos < 0) || (rightpos > 15)
475 || ((leftpos == 15) && (rightpos == 15)))
476 Quit("SD_SetPosition: Illegal position");
477
478 switch (DigiMode)
479 {
480 default:
481 break;
482 case sds_SoundBlaster:
483 // SDL_PositionSBP(leftpos,rightpos);
484 Mix_SetPanning(channel, TO_SDL_POSITION(leftpos), TO_SDL_POSITION(rightpos));
485 break;
486 }
487 }
488
SD_PrepareSound(int which)489 byte* SD_PrepareSound(int which)
490 {
491 int size = Wads.LumpLength(which);
492 if(size == 0)
493 return NULL;
494
495 FMemLump soundLump = Wads.ReadLump(which);
496
497 byte* out = reinterpret_cast<byte*> (Mix_LoadWAV_RW(SDL_RWFromMem(soundLump.GetMem(), size), 1));
498 if(!out)
499 return NULL;
500
501 // TEMPORARY WORK AROUND FOR MEMORY ERROR
502 byte* nout = new byte[sizeof(Mix_Chunk)];
503 memcpy(nout, out, sizeof(Mix_Chunk));
504 return nout;
505 }
506
SD_PlayDigitized(const SoundData & which,int leftpos,int rightpos,SoundChannel chan)507 int SD_PlayDigitized(const SoundData &which,int leftpos,int rightpos,SoundChannel chan)
508 {
509 if (!DigiMode)
510 return 0;
511
512 // If this sound has been played too recently, don't play it again.
513 // (Fix for extremely loud sounds when one plays over itself too much.)
514 uint32_t currentTick = SDL_GetTicks();
515 if (currentTick - SoundInfo.GetLastPlayTick(which) < MIN_TICKS_BETWEEN_DIGI_REPEATS)
516 return 0;
517
518 SoundInfo.SetLastPlayTick(which, currentTick);
519
520 int channel = chan;
521 if(chan == SD_GENERIC)
522 {
523 channel = Mix_GroupAvailable(1);
524 if(channel == -1) channel = Mix_GroupOldest(1);
525 if(channel == -1) // All sounds stopped in the meantime?
526 channel = Mix_GroupAvailable(1);
527 }
528 SD_SetPosition(channel, leftpos,rightpos);
529
530 DigiPlaying = true;
531
532 Mix_Chunk *sample = reinterpret_cast<Mix_Chunk*> (which.GetData(SoundData::DIGITAL));
533 if(sample == NULL)
534 return 0;
535
536 Mix_Volume(channel, static_cast<int> (ceil(128.0*MULTIPLY_VOLUME(SoundVolume))));
537 if(Mix_PlayChannel(channel, sample, 0) == -1)
538 {
539 printf("Unable to play sound: %s\n", Mix_GetError());
540 return 0;
541 }
542
543 // Return channel + 1 because zero is a valid channel.
544 return channel + 1;
545 }
546
SD_ChannelFinished(int channel)547 void SD_ChannelFinished(int channel)
548 {
549 SoundPlaying = FString();
550 channelSoundPos[channel].valid = 0;
551 }
552
553 void
SD_SetDigiDevice(SDSMode mode)554 SD_SetDigiDevice(SDSMode mode)
555 {
556 bool devicenotpresent;
557
558 if (mode == DigiMode)
559 return;
560
561 SD_StopDigitized();
562
563 devicenotpresent = false;
564 switch (mode)
565 {
566 default:
567 break;
568 case sds_SoundBlaster:
569 if (!SoundBlasterPresent)
570 devicenotpresent = true;
571 break;
572 }
573
574 if (!devicenotpresent)
575 {
576 DigiMode = mode;
577
578 #ifdef NOTYET
579 SDL_SetTimerSpeed();
580 #endif
581 }
582 }
583
584 // AdLib Code
585
586 ///////////////////////////////////////////////////////////////////////////
587 //
588 // SDL_ALStopSound() - Turns off any sound effects playing through the
589 // AdLib card
590 //
591 ///////////////////////////////////////////////////////////////////////////
592 static void
SDL_ALStopSound(void)593 SDL_ALStopSound(void)
594 {
595 SDL_LockMutex(audioMutex);
596
597 alSound = 0;
598 alOut(alFreqH + 0, 0);
599
600 SDL_UnlockMutex(audioMutex);
601 }
602
SDL_AlSetChanInst(const Instrument * inst,unsigned int chan)603 static void SDL_AlSetChanInst(const Instrument *inst, unsigned int chan)
604 {
605 static const byte chanOps[OPL_CHANNELS] = {
606 0, 1, 2, 8, 9, 0xA, 0x10, 0x11, 0x12
607 };
608 byte c,m;
609
610 m = chanOps[chan]; // modulator cell for channel
611 c = m + 3; // carrier cell for channel
612 alOut(m + alChar,inst->mChar);
613 alOut(m + alScale,inst->mScale);
614 alOut(m + alAttack,inst->mAttack);
615 alOut(m + alSus,inst->mSus);
616 alOut(m + alWave,inst->mWave);
617 alOut(c + alChar,inst->cChar);
618 alOut(c + alScale,inst->cScale);
619 alOut(c + alAttack,inst->cAttack);
620 alOut(c + alSus,inst->cSus);
621 alOut(c + alWave,inst->cWave);
622
623 // Note: Switch commenting on these lines for old MUSE compatibility
624 // alOutInIRQ(alFeedCon,inst->nConn);
625
626 alOut(chan + alFreqL,0);
627 alOut(chan + alFreqH,0);
628 alOut(chan + alFeedCon,0);
629 }
SDL_AlSetFXInst(const Instrument * inst)630 static void SDL_AlSetFXInst(const Instrument *inst)
631 {
632 SDL_AlSetChanInst(inst, 0);
633 }
634
635 ///////////////////////////////////////////////////////////////////////////
636 //
637 // SDL_ALPlaySound() - Plays the specified sound on the AdLib card
638 //
639 ///////////////////////////////////////////////////////////////////////////
640 static void
SDL_ALPlaySound(AdLibSound * sound)641 SDL_ALPlaySound(AdLibSound *sound)
642 {
643 Instrument *inst;
644 byte *data;
645
646 SDL_ALStopSound();
647
648 SDL_LockMutex(audioMutex);
649
650 alLengthLeft = LittleLong(sound->common.length);
651 data = sound->data;
652 alBlock = ((sound->block & 7) << 2) | 0x20;
653 inst = &sound->inst;
654
655 if (!(inst->mSus | inst->cSus))
656 {
657 Quit("SDL_ALPlaySound() - Bad instrument");
658 }
659
660 SDL_AlSetFXInst(inst);
661 alSound = (byte *)data;
662
663 SDL_UnlockMutex(audioMutex);
664 }
665
666 ///////////////////////////////////////////////////////////////////////////
667 //
668 // SDL_ShutAL() - Shuts down the AdLib card for sound effects
669 //
670 ///////////////////////////////////////////////////////////////////////////
671 static void
SDL_ShutAL(void)672 SDL_ShutAL(void)
673 {
674 SDL_LockMutex(audioMutex);
675
676 alSound = 0;
677 //alOut(alEffects,0); // Sound effects should not mess with the music's rhythm settings!
678 alOut(alFreqH + 0,0);
679 SDL_AlSetFXInst(&alZeroInst);
680
681 SDL_UnlockMutex(audioMutex);
682 }
683
684 ///////////////////////////////////////////////////////////////////////////
685 //
686 // SDL_StartAL() - Starts up the AdLib card for sound effects
687 //
688 ///////////////////////////////////////////////////////////////////////////
689 static void
SDL_StartAL(void)690 SDL_StartAL(void)
691 {
692 //alOut(alEffects, 0); // Sound effects should not mess with the music's rhythm settings!
693 SDL_AlSetFXInst(&alZeroInst);
694 }
695
696 ////////////////////////////////////////////////////////////////////////////
697 //
698 // SDL_ShutDevice() - turns off whatever device was being used for sound fx
699 //
700 ////////////////////////////////////////////////////////////////////////////
701 static void
SDL_ShutDevice(void)702 SDL_ShutDevice(void)
703 {
704 switch (SoundMode)
705 {
706 default:
707 break;
708 case sdm_PC:
709 SDL_ShutPC();
710 break;
711 case sdm_AdLib:
712 SDL_ShutAL();
713 break;
714 }
715 SoundMode = sdm_Off;
716 }
717
718 ///////////////////////////////////////////////////////////////////////////
719 //
720 // SDL_StartDevice() - turns on whatever device is to be used for sound fx
721 //
722 ///////////////////////////////////////////////////////////////////////////
723 static void
SDL_StartDevice(void)724 SDL_StartDevice(void)
725 {
726 switch (SoundMode)
727 {
728 default:
729 break;
730 case sdm_AdLib:
731 SDL_StartAL();
732 break;
733 }
734 SoundPlaying = FString();
735 SoundPriority = 0;
736 }
737
738 // Public routines
739
740 ///////////////////////////////////////////////////////////////////////////
741 //
742 // SD_SetSoundMode() - Sets which sound hardware to use for sound effects
743 //
744 ///////////////////////////////////////////////////////////////////////////
SD_SetSoundMode(SDMode mode)745 bool SD_SetSoundMode(SDMode mode)
746 {
747 bool result = false;
748
749 SD_StopSound();
750
751 if ((mode == sdm_AdLib) && !AdLibPresent)
752 mode = sdm_PC;
753
754 switch (mode)
755 {
756 case sdm_Off:
757 case sdm_PC:
758 result = true;
759 break;
760 case sdm_AdLib:
761 if (AdLibPresent)
762 result = true;
763 break;
764 default:
765 Quit("SD_SetSoundMode: Invalid sound mode %i", mode);
766 return false;
767 }
768
769 if (result && (mode != SoundMode))
770 {
771 SDL_ShutDevice();
772 SoundMode = mode;
773 SDL_StartDevice();
774 }
775
776 return(result);
777 }
778
779 ///////////////////////////////////////////////////////////////////////////
780 //
781 // SD_SetMusicMode() - sets the device to use for background music
782 //
783 ///////////////////////////////////////////////////////////////////////////
SD_SetMusicMode(SMMode mode)784 bool SD_SetMusicMode(SMMode mode)
785 {
786 bool result = false;
787
788 SD_FadeOutMusic();
789 while (SD_MusicPlaying())
790 SDL_Delay(5);
791
792 switch (mode)
793 {
794 case smm_Off:
795 result = true;
796 break;
797 case smm_AdLib:
798 if (AdLibPresent)
799 result = true;
800 break;
801 }
802
803 if (result)
804 MusicMode = mode;
805
806 // SDL_SetTimerSpeed();
807
808 return(result);
809 }
810
811 int numreadysamples = 0;
812 int soundTimeCounter = SOUND_TICKS;
813 int samplesPerMusicTick;
814 /*-----------------------------------------------------------------------------
815 The variables below are not required unless you WANT to change the behavior of
816 AdLib sound effects compared to the original Wolfenstein 3-D code.
817
818 What would happen is this: SDL_AlPlaySound() resets the AdLib instrument and
819 the alSound pointer to play the given AdLib sound from the beginning. The check
820 that was implemented in SDL_IMFMusicPlayer() would not reset the curAlSoundPtr
821 if the alSound pointer pointed to the data that was already being played. This
822 resulted in situations where the instrument data was reset, but not the data
823 pointer. This caused some sounds to be played at the wrong pitch.
824
825 If you really WANT to set the behavior so that an AdLib sound will not be re-
826 started when it is interrupted by itself, you should change the code in the
827 SDL_AlPlaySound() function instead, making sure not to reset the instrument.
828
829 Any thread-safety issues should be solved now. The mutex 'audioMutex' protects
830 gloabal variables that need to be accessed in SDL_IMFMusicPlayer().
831
832 -- K1n9_Duk3
833 -----------------------------------------------------------------------------*/
834 //byte *curAlSound = 0;
835 //byte *curAlSoundPtr = 0;
836 //longword curAlLengthLeft = 0;
837
SDL_IMFMusicPlayer(void * udata,Uint8 * stream,int len)838 void SDL_IMFMusicPlayer(void *udata, Uint8 *stream, int len)
839 {
840 int stereolen = len>>1;
841 int sampleslen = stereolen>>1;
842 Sint16 *stream16 = (Sint16 *) (void *) stream; // expect correct alignment
843
844 while(1)
845 {
846 if(numreadysamples)
847 {
848 if(numreadysamples<sampleslen)
849 {
850 if(MusicMode == smm_AdLib || SoundMode == sdm_AdLib)
851 YM3812UpdateOne(oplChip, stream16, numreadysamples);
852
853 // Mix the emulated PC sounds into the AdLib buffer:
854 SDL_PCEmulateAndMix(stream16, numreadysamples);
855
856 stream16 += numreadysamples*2;
857 sampleslen -= numreadysamples;
858 }
859 else
860 {
861 if(MusicMode == smm_AdLib || SoundMode == sdm_AdLib)
862 YM3812UpdateOne(oplChip, stream16, sampleslen);
863
864 // Mix the emulated PC sounds into the AdLib buffer:
865 SDL_PCEmulateAndMix(stream16, sampleslen);
866
867 numreadysamples -= sampleslen;
868 return;
869 }
870 }
871
872 SDL_LockMutex(audioMutex);
873
874 soundTimeCounter--;
875 if(!soundTimeCounter)
876 {
877 // Sound effects are played at 140 Hz (every 5 cycles of the 700 Hz music service)
878 soundTimeCounter = SOUND_TICKS;
879
880 SDL_PCService();
881
882 // THIS is the way the original Wolfenstein 3-D code handled it!
883 if(alSound)
884 {
885 if(*alSound)
886 {
887 alOut(alFreqL, *alSound);
888 alOut(alFreqH, alBlock);
889 } else alOut(alFreqH, 0);
890 alSound++;
891 if (!(--alLengthLeft))
892 {
893 alSound = 0;
894 SoundPriority=0;
895 alOut(alFreqH, 0);
896 }
897 }
898 }
899 if(sqActive)
900 {
901 do
902 {
903 if(sqHackTime > alTimeCount) break;
904 sqHackTime = alTimeCount + LittleShort(*(sqHackPtr+1));
905 alOutMusic(*(byte *) sqHackPtr, *(((byte *) sqHackPtr)+1));
906 sqHackPtr += 2;
907 sqHackLen -= 4;
908 }
909 while(sqHackLen>0);
910 alTimeCount++;
911 if(!sqHackLen)
912 {
913 sqHackPtr = sqHack;
914 sqHackLen = sqHackSeqLen;
915 sqHackTime = 0;
916 alTimeCount = 0;
917 }
918 }
919 numreadysamples = samplesPerMusicTick;
920
921 SDL_UnlockMutex(audioMutex);
922 }
923 }
924
925 ///////////////////////////////////////////////////////////////////////////
926 //
927 // SD_Startup() - starts up the Sound Mgr
928 // Detects all additional sound hardware and installs my ISR
929 //
930 ///////////////////////////////////////////////////////////////////////////
931 void
SD_Startup(void)932 SD_Startup(void)
933 {
934 int i;
935
936 if (SD_Started)
937 return;
938
939 if(SDL_InitSubSystem(SDL_INIT_AUDIO) != 0)
940 {
941 Printf("Unable to initialize audio.\n");
942 return;
943 }
944
945 if((audioMutex = SDL_CreateMutex()) == NULL)
946 {
947 printf("Unable to create audio mutex\n");
948 return;
949 }
950
951 #if defined(__ANDROID__)
952 // Working directory will be in the form: Beloko/Wolf3d/FULL
953 Mix_SetSoundFonts("../../FluidR3_GM.sf2");
954 #elif defined(__unix__)
955 Mix_SetSoundFonts("/usr/share/sounds/sf2/FluidR3_GM.sf2");
956 #endif
957
958 if(Mix_OpenAudio(param_samplerate, AUDIO_S16, 2, param_audiobuffer))
959 {
960 printf("Unable to open audio: %s\n", Mix_GetError());
961 return;
962 }
963
964 Mix_ReserveChannels(2); // reserve player and boss weapon channels
965 Mix_GroupChannels(2, MIX_CHANNELS-1, 1); // group remaining channels
966
967 // Init music
968 if(YM3812Init(1,3579545,param_samplerate))
969 {
970 printf("Unable to create virtual OPL!!\n");
971 }
972
973 for(i=1;i<0xf6;i++)
974 YM3812Write(oplChip,i,0,MAX_VOLUME);
975
976 YM3812Write(oplChip,1,0x20,MAX_VOLUME); // Set WSE=1
977 // YM3812Write(0,8,0); // Set CSM=0 & SEL=0 // already set in for statement
978
979 samplesPerMusicTick = param_samplerate / MUSIC_RATE; // SDL_t0FastAsmService played at 700Hz
980 Mix_HookMusic(SDL_IMFMusicPlayer, 0);
981 Mix_ChannelFinished(SD_ChannelFinished);
982
983 Mix_VolumeMusic(static_cast<int> (ceil(128.0*MULTIPLY_VOLUME(MusicVolume))));
984
985 // Make sure that the musicFinished() function is called when the music stops playing
986 Mix_HookMusicFinished(musicFinished);
987
988 AdLibPresent = true;
989 SoundBlasterPresent = true;
990
991 alTimeCount = 0;
992
993 SD_SetSoundMode(sdm_Off);
994 SD_SetMusicMode(smm_Off);
995
996 SD_Started = true;
997
998 SoundInfo.Init();
999 SoundSeq.Init();
1000 }
1001
1002 ///////////////////////////////////////////////////////////////////////////
1003 //
1004 // SD_Shutdown() - shuts down the Sound Mgr
1005 // Removes sound ISR and turns off whatever sound hardware was active
1006 //
1007 ///////////////////////////////////////////////////////////////////////////
1008 void
SD_Shutdown(void)1009 SD_Shutdown(void)
1010 {
1011 if (!SD_Started)
1012 return;
1013
1014 SD_MusicOff();
1015 SD_StopSound();
1016
1017 if(audioMutex != NULL)
1018 {
1019 SDL_DestroyMutex(audioMutex);
1020 audioMutex = NULL;
1021 }
1022
1023 SDL_QuitSubSystem(SDL_INIT_AUDIO);
1024
1025 SD_Started = false;
1026 }
1027
1028 ///////////////////////////////////////////////////////////////////////////
1029 //
1030 // SD_PositionSound() - Sets up a stereo imaging location for the next
1031 // sound to be played. Each channel ranges from 0 to 15.
1032 //
1033 ///////////////////////////////////////////////////////////////////////////
1034 void
SD_PositionSound(int leftvol,int rightvol)1035 SD_PositionSound(int leftvol,int rightvol)
1036 {
1037 LeftPosition = leftvol;
1038 RightPosition = rightvol;
1039 nextsoundpos = true;
1040 }
1041
1042 ///////////////////////////////////////////////////////////////////////////
1043 //
1044 // SD_PlaySound() - plays the specified sound on the appropriate hardware
1045 // Returns the channel of the sound if it played, else 0.
1046 //
1047 ///////////////////////////////////////////////////////////////////////////
SD_PlaySound(const char * sound,SoundChannel chan)1048 int SD_PlaySound(const char* sound, SoundChannel chan)
1049 {
1050 bool ispos;
1051 int lp,rp;
1052
1053 lp = LeftPosition;
1054 rp = RightPosition;
1055 LeftPosition = 0;
1056 RightPosition = 0;
1057
1058 ispos = nextsoundpos;
1059 nextsoundpos = false;
1060
1061 const SoundData &sindex = SoundInfo[sound];
1062
1063 if ((SoundMode != sdm_Off) && sindex.IsNull())
1064 return 0;
1065
1066 if ((DigiMode != sds_Off) && sindex.HasType(SoundData::DIGITAL))
1067 {
1068 if ((DigiMode == sds_PC) && (SoundMode == sdm_PC))
1069 {
1070 #ifdef NOTYET
1071 if (s->priority < SoundPriority)
1072 return 0;
1073
1074 SDL_PCStopSound();
1075
1076 SD_PlayDigitized(sindex,lp,rp);
1077 SoundPositioned = ispos;
1078 SoundPriority = s->priority;
1079 #else
1080 return 0;
1081 #endif
1082 }
1083 else
1084 {
1085 #ifdef NOTYET
1086 if (s->priority < DigiPriority)
1087 return(false);
1088 #endif
1089
1090 int channel = SD_PlayDigitized(sindex, lp, rp, chan);
1091 SoundPositioned = ispos;
1092 DigiPriority = sindex.GetPriority();
1093 SoundPlaying = sound;
1094 return channel;
1095 }
1096
1097 return(true);
1098 }
1099
1100 if (SoundMode == sdm_Off)
1101 return 0;
1102
1103 // if (!s->length)
1104 // Quit("SD_PlaySound() - Zero length sound");
1105 if (sindex.GetPriority() < SoundPriority)
1106 return 0;
1107
1108 #ifndef ECWOLF_MIXER
1109 // With stock SDL_mixer we can't play music and emulated sounds.
1110 if (music != NULL)
1111 return 0;
1112 #endif
1113
1114 bool didPlaySound = false;
1115
1116 switch (SoundMode)
1117 {
1118 default:
1119 didPlaySound = true;
1120 break;
1121 case sdm_PC:
1122 if(sindex.HasType(SoundData::PCSPEAKER))
1123 {
1124 SDL_PCPlaySound((PCSound *)sindex.GetData(SoundData::PCSPEAKER));
1125 didPlaySound = true;
1126 }
1127 break;
1128 case sdm_AdLib:
1129 if(sindex.HasType(SoundData::ADLIB))
1130 {
1131 SDL_ALPlaySound((AdLibSound *)sindex.GetData(SoundData::ADLIB));
1132 didPlaySound = true;
1133 }
1134 break;
1135 }
1136
1137 if (didPlaySound)
1138 {
1139 SoundPriority = sindex.GetPriority();
1140 SoundPlaying = sound;
1141 }
1142
1143 return 0;
1144 }
1145
1146 ///////////////////////////////////////////////////////////////////////////
1147 //
1148 // SD_SoundPlaying() - returns the sound number that's playing, or 0 if
1149 // no sound is playing
1150 //
1151 ///////////////////////////////////////////////////////////////////////////
SD_SoundPlaying(void)1152 bool SD_SoundPlaying(void)
1153 {
1154 bool result = false;
1155
1156 switch (SoundMode)
1157 {
1158 default:
1159 break;
1160 case sdm_PC:
1161 result = pcSound? true : false; // not really thread-safe, but a mutex would be overkill
1162 break;
1163 case sdm_AdLib:
1164 result = alSound? true : false; // not really thread-safe, but a mutex would be overkill
1165 break;
1166 }
1167
1168 if (result)
1169 return SoundPlaying.IsNotEmpty();
1170 else
1171 return false;
1172 }
1173
1174 ///////////////////////////////////////////////////////////////////////////
1175 //
1176 // SD_StopSound() - if a sound is playing, stops it
1177 //
1178 ///////////////////////////////////////////////////////////////////////////
1179 void
SD_StopSound(void)1180 SD_StopSound(void)
1181 {
1182 if (DigiPlaying)
1183 SD_StopDigitized();
1184
1185 switch (SoundMode)
1186 {
1187 default:
1188 break;
1189 case sdm_PC:
1190 SDL_PCStopSound();
1191 break;
1192 case sdm_AdLib:
1193 SDL_ALStopSound();
1194 break;
1195 }
1196
1197 SoundPositioned = false;
1198
1199 SDL_SoundFinished();
1200 }
1201
1202 ///////////////////////////////////////////////////////////////////////////
1203 //
1204 // SD_WaitSoundDone() - waits until the current sound is done playing
1205 //
1206 ///////////////////////////////////////////////////////////////////////////
1207 void
SD_WaitSoundDone(void)1208 SD_WaitSoundDone(void)
1209 {
1210 while (SD_SoundPlaying())
1211 SDL_Delay(5);
1212 }
1213
1214 ///////////////////////////////////////////////////////////////////////////
1215 //
1216 // SD_MusicOn() - turns on the sequencer
1217 //
1218 ///////////////////////////////////////////////////////////////////////////
1219 void
SD_MusicOn(void)1220 SD_MusicOn(void)
1221 {
1222 sqActive = true; // not really thread-safe, but a mutex would be overkill
1223 }
1224
1225 ///////////////////////////////////////////////////////////////////////////
1226 //
1227 // SD_MusicOff() - turns off the sequencer and any playing notes
1228 // returns the last music offset for music continue
1229 //
1230 ///////////////////////////////////////////////////////////////////////////
1231 int
SD_MusicOff(void)1232 SD_MusicOff(void)
1233 {
1234 word i;
1235 int musoffs;
1236
1237 SDL_LockMutex(audioMutex);
1238
1239 sqActive = false;
1240 musoffs = (int) (sqHackPtr-sqHack);
1241
1242 SDL_UnlockMutex(audioMutex);
1243
1244 switch (MusicMode)
1245 {
1246 default:
1247 break;
1248 case smm_AdLib:
1249 if (music == NULL)
1250 {
1251 alOut(alEffects, 0);
1252 for (i = 0;i < sqMaxTracks;i++)
1253 alOut(alFreqH + i + 1, 0);
1254 }
1255 else
1256 {
1257 Mix_PauseMusic();
1258 return 0;
1259 }
1260 break;
1261 }
1262
1263 return musoffs;
1264 }
1265
1266 ///////////////////////////////////////////////////////////////////////////
1267 //
1268 // SD_StartMusic() - starts playing the music pointed to
1269 //
1270 ///////////////////////////////////////////////////////////////////////////
1271 void
SD_StartMusic(const char * chunk)1272 SD_StartMusic(const char* chunk)
1273 {
1274 static const Instrument ChannelRelease = {
1275 0, 0,
1276 0x3F, 0x3F,
1277 0xFF, 0xFF,
1278 0xF, 0xF,
1279 0, 0,
1280 0,
1281
1282 0, 0, {0, 0, 0}
1283 };
1284
1285 SD_MusicOff();
1286
1287 if (MusicMode == smm_AdLib)
1288 {
1289 int lumpNum = Wads.CheckNumForName(chunk, ns_music);
1290 if(lumpNum == -1)
1291 return;
1292
1293 // Load our music file from chunk
1294 chunkmem = new byte[Wads.LumpLength(lumpNum)];
1295 FWadLump lump = Wads.OpenLumpNum(lumpNum);
1296 lump.Read(chunkmem, Wads.LumpLength(lumpNum));
1297 SDL_RWops *mus_cunk = SDL_RWFromMem(chunkmem, Wads.LumpLength(lumpNum));
1298 music = Mix_LoadMUS_RW(mus_cunk);
1299
1300 // We assume that when music equals to NULL, we've an IMF file to play
1301 if (music == NULL)
1302 {
1303 Mix_HookMusic(SDL_IMFMusicPlayer, 0);
1304
1305 SDL_LockMutex(audioMutex);
1306
1307 for (int i = 0;i < OPL_CHANNELS;++i)
1308 SDL_AlSetChanInst(&ChannelRelease, i);
1309
1310 delete[] sqHackFreeable;
1311 sqHack = reinterpret_cast<word*>(chunkmem);
1312 sqHackFreeable = sqHack;
1313 chunkmem = NULL;
1314 if(*sqHack == 0) sqHackLen = sqHackSeqLen = Wads.LumpLength(lumpNum);
1315 else sqHackLen = sqHackSeqLen = LittleShort(*sqHack++);
1316 sqHackPtr = sqHack;
1317 sqHackTime = 0;
1318 alTimeCount = 0;
1319
1320 SDL_UnlockMutex(audioMutex);
1321
1322 SD_MusicOn();
1323 }
1324 else
1325 {
1326 Mix_HookMusic(0, 0);
1327
1328 SDL_LockMutex(audioMutex);
1329
1330 // Play the music
1331 musicchunk = lumpNum;
1332 if (Mix_PlayMusic(music, -1) == -1)
1333 {
1334 printf("Unable to play music file: %s\n", Mix_GetError());
1335 }
1336
1337 SDL_UnlockMutex(audioMutex);
1338 }
1339 }
1340 }
1341
1342 int
SD_PauseMusic(void)1343 SD_PauseMusic(void)
1344 {
1345 if (music != NULL && Mix_PlayingMusic() == 1)
1346 {
1347 Mix_PauseMusic();
1348 return (int)Mix_GetMusicPCMPosition();
1349 }
1350 return 0;
1351 }
1352
1353 void
SD_ContinueMusic(const char * chunk,int startoffs)1354 SD_ContinueMusic(const char* chunk, int startoffs)
1355 {
1356 SD_MusicOff();
1357
1358 if (MusicMode == smm_AdLib)
1359 {
1360 int lumpNum = Wads.CheckNumForName(chunk, ns_music);
1361 if(lumpNum == -1)
1362 return;
1363
1364 if (music == NULL || musicchunk != lumpNum)
1365 { // We need this scope to "delete" the lump before modifying the sqHack pointers.
1366 SDL_LockMutex(audioMutex);
1367 FWadLump lump = Wads.OpenLumpNum(lumpNum);
1368 delete[] sqHackFreeable;
1369 sqHackFreeable = NULL;
1370
1371 // Load our music file from chunk
1372 chunkmem = new byte[Wads.LumpLength(lumpNum)];
1373 lump.Read(chunkmem, Wads.LumpLength(lumpNum));
1374 SDL_RWops *mus_cunk = SDL_RWFromMem(chunkmem, Wads.LumpLength(lumpNum));
1375 music = Mix_LoadMUS_RW(mus_cunk);
1376
1377 if (music == NULL)
1378 {
1379 sqHack = reinterpret_cast<word*>(chunkmem);
1380 sqHackFreeable = sqHack;
1381 chunkmem = NULL;
1382 if(*sqHack == 0) sqHackLen = sqHackSeqLen = Wads.LumpLength(lumpNum);
1383 else sqHackLen = sqHackSeqLen = LittleShort(*sqHack++);
1384 sqHackPtr = sqHack;
1385 }
1386 }
1387
1388 if (music == NULL)
1389 {
1390 if(startoffs >= sqHackLen)
1391 {
1392 SDL_UnlockMutex(audioMutex);
1393 Quit("SD_StartMusic: Illegal startoffs provided!");
1394 }
1395
1396 // fast forward to correct position
1397 // (needed to reconstruct the instruments)
1398
1399 for(int i = 0; i < startoffs; i += 2)
1400 {
1401 byte reg = *(byte *)sqHackPtr;
1402 byte val = *(((byte *)sqHackPtr) + 1);
1403 if(reg >= 0xb1 && reg <= 0xb8) val &= 0xdf; // disable play note flag
1404 else if(reg == 0xbd) val &= 0xe0; // disable drum flags
1405
1406 alOut(reg,val);
1407 sqHackPtr += 2;
1408 sqHackLen -= 4;
1409 }
1410 sqHackTime = 0;
1411 alTimeCount = 0;
1412
1413 SDL_UnlockMutex(audioMutex);
1414
1415 Mix_HookMusic(SDL_IMFMusicPlayer, 0);
1416
1417 SD_MusicOn();
1418 }
1419 else
1420 {
1421 SDL_UnlockMutex(audioMutex);
1422
1423 Mix_HookMusic(0, 0);
1424
1425 if (Mix_PausedMusic() == 1 && musicchunk == lumpNum)
1426 {
1427 Mix_ResumeMusic();
1428 return;
1429 }
1430
1431 // Play the music
1432 musicchunk = lumpNum;
1433 if (Mix_PlayMusic(music, -1) == -1)
1434 {
1435 printf("Unable to play music file: %s\n", Mix_GetError());
1436 }
1437
1438 Mix_SetMusicPCMPosition(startoffs);
1439 }
1440 }
1441 }
1442
1443 ///////////////////////////////////////////////////////////////////////////
1444 //
1445 // SD_FadeOutMusic() - starts fading out the music. Call SD_MusicPlaying()
1446 // to see if the fadeout is complete
1447 //
1448 ///////////////////////////////////////////////////////////////////////////
1449 void
SD_FadeOutMusic(void)1450 SD_FadeOutMusic(void)
1451 {
1452 switch (MusicMode)
1453 {
1454 default:
1455 break;
1456 case smm_AdLib:
1457 // DEBUG - quick hack to turn the music off
1458 SD_MusicOff();
1459 break;
1460 }
1461 }
1462
1463 ///////////////////////////////////////////////////////////////////////////
1464 //
1465 // SD_MusicPlaying() - returns true if music is currently playing, false if
1466 // not
1467 //
1468 ///////////////////////////////////////////////////////////////////////////
SD_MusicPlaying(void)1469 bool SD_MusicPlaying(void)
1470 {
1471 bool result;
1472
1473 switch (MusicMode)
1474 {
1475 case smm_AdLib:
1476 if (music == NULL)
1477 result = sqActive; // not really thread-safe, but a mutex would be overkill
1478 else
1479 result = Mix_PlayingMusic() && !Mix_PausedMusic();
1480 break;
1481 default:
1482 result = false;
1483 break;
1484 }
1485
1486 return(result);
1487 }
1488