1 /* Project 16 Source Code~
\r
2 * Copyright (C) 2012-2017 sparky4 & pngwen & andrius4669 & joncampbell123 & yakui-lover
\r
4 * This file is part of Project 16.
\r
6 * Project 16 is free software; you can redistribute it and/or modify
\r
7 * it under the terms of the GNU General Public License as published by
\r
8 * the Free Software Foundation; either version 3 of the License, or
\r
9 * (at your option) any later version.
\r
11 * Project 16 is distributed in the hope that it will be useful,
\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 * GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License
\r
17 * along with this program. If not, see <http://www.gnu.org/licenses/>, or
\r
18 * write to the Free Software Foundation, Inc., 51 Franklin Street,
\r
19 * Fifth Floor, Boston, MA 02110-1301 USA.
\r
25 // ID_SD.c - Sound Manager for Wolfenstein 3D
\r
27 // By Jason Blochowiak
\r
28 // Open Watcom port by sparky4
\r
32 // This module handles dealing with generating sound on the appropriate
\r
35 // Depends on: User Mgr (for parm checking)
\r
39 // SoundSourcePresent - Sound Source thingie present?
\r
40 // SoundBlasterPresent - SoundBlaster card present?
\r
41 // AdLibPresent - AdLib card present?
\r
42 // SoundMode - What device is used for sound effects
\r
43 // (Use SM_SetSoundMode() to set)
\r
44 // MusicMode - What device is used for music
\r
45 // (Use SM_SetMusicMode() to set)
\r
46 // DigiMode - What device is used for digitized sound effects
\r
47 // (Use SM_SetDigiDevice() to set)
\r
50 // NeedsDigitized - load digitized sounds?
\r
51 // NeedsMusic - load music?
\r
54 #pragma hdrstop // Wierdo thing with MUSE
\r
58 #ifndef _MUSE_ // Will be defined in ID_Types.h
\r
59 #include "src/lib/id_sd.h"
\r
61 #include "src/lib/16_head.h"
\r
66 #define SDL_SoundFinished() {SoundNumber = SoundPriority = 0;}
\r
68 // Macros for AdLib stuff
\r
69 #define selreg(n) outportb(alFMAddr,n)
\r
70 #define writereg(n) outportb(alFMData,n)
\r
71 #define readstat() inportb(alFMStatus)
\r
73 #define SD_USECATA3DSETTIMERSPEED
\r
75 // Imports from ID_SD_A.ASM
\r
77 /*extern*/ void SDL_SetDS(void);
\r
78 /*extern*/ void interrupt SDL_t0ExtremeAsmService(void),
\r
79 SDL_t0FastAsmService(void),
\r
80 SDL_t0SlowAsmService(void);
\r
82 void SDL_IndicatePC(boolean on);
\r
85 boolean SoundSourcePresent,
\r
87 SoundBlasterPresent,SBProPresent,
\r
88 NeedsDigitized,NeedsMusic,
\r
95 word *SoundTable; // Really * _seg *SoundTable, but that don't work
\r
98 int DigiMap[LASTSOUND];
\r
100 // Internal variables
\r
101 static boolean SD_Started;
\r
102 boolean nextsoundpos;
\r
103 word/*boolean_+*/ TimerDone;
\r
104 word TimerVal,TimerDelay10,TimerDelay25,TimerDelay100;
\r
105 dword TimerDivisor,TimerCount;
\r
106 static char *ParmStrings[] =
\r
118 static void (*SoundUserHook)(void);
\r
119 soundnames SoundNumber,DigiNumber;
\r
120 word SoundPriority,DigiPriority;
\r
121 int LeftPosition,RightPosition;
\r
122 void interrupt (*t0OldService)(void);
\r
126 word NumDigi,DigiLeft,DigiPage;
\r
127 word _seg *DigiList;
\r
128 word DigiLastStart,DigiLastEnd;
\r
129 boolean DigiPlaying;
\r
130 static boolean DigiMissed,DigiLastSegment;
\r
131 static memptr DigiNextAddr;
\r
132 static word DigiNextLen;
\r
133 boolean pcindicate;
\r
136 // SoundBlaster variables
\r
137 static boolean sbNoCheck,sbNoProCheck;
\r
138 static volatile boolean sbSamplePlaying;
\r
139 static byte sbOldIntMask = -1;
\r
140 static volatile byte huge *sbNextSegPtr;
\r
141 static byte sbDMA = 1,
\r
142 sbDMAa1 = 0x83,sbDMAa2 = 2,sbDMAa3 = 3,
\r
143 sba1Vals[] = {0x87,0x83,0,0x82},
\r
144 sba2Vals[] = {0,2,0,6},
\r
145 sba3Vals[] = {1,3,0,7};
\r
146 static int sbLocation = -1,sbInterrupt = 7,sbIntVec = 0xf,
\r
147 sbIntVectors[] = {-1,-1,0xa,0xb,-1,0xd,-1,0xf,-1,-1,-1};
\r
148 static volatile dword sbNextSegLen;
\r
149 static volatile SampledSound huge *sbSamples;
\r
150 static void interrupt (*sbOldIntHand)(void);
\r
151 static byte sbpOldFMMix,sbpOldVOCMix;
\r
153 // SoundSource variables
\r
156 word ssControl,ssStatus,ssData;
\r
158 volatile byte far *ssSample;
\r
159 volatile dword ssLengthLeft;
\r
162 // PC Sound variables
\r
163 volatile byte pcLastSample,far *pcSound;
\r
164 dword pcLengthLeft;
\r
165 word pcSoundLookup[255];
\r
171 dword alLengthLeft;
\r
173 Instrument alZeroInst;
\r
175 // This table maps channel numbers to carrier and modulator op cells
\r
176 static byte carriers[9] = { 3, 4, 5,11,12,13,19,20,21},
\r
177 modifiers[9] = { 0, 1, 2, 8, 9,10,16,17,18},
\r
178 // This table maps percussive voice numbers to op cells
\r
179 pcarriers[5] = {19,0xff,0xff,0xff,0xff},
\r
180 pmodifiers[5] = {16,17,18,20,21};
\r
182 // Sequencer variables
\r
184 static word alFXReg;
\r
185 static ActiveTrack *tracks[sqMaxTracks];//,
\r
186 //-- mytracks[sqMaxTracks];
\r
187 //--static word sqMode,sqFadeStep;
\r
188 word far *sqHack,far *sqHackPtr,sqHackLen,sqHackSeqLen;
\r
191 // Internal routines
\r
192 void SDL_DigitizedDone(void);
\r
194 ///////////////////////////////////////////////////////////////////////////
\r
196 // SDL_SetTimer0() - Sets system timer 0 to the specified speed
\r
198 ///////////////////////////////////////////////////////////////////////////
\r
201 SDL_SetTimer0(word speed)
\r
203 #ifndef TPROF // If using Borland's profiling, don't screw with the timer
\r
207 outportb(0x43,0x36); // Change timer 0
\r
208 outportb(0x40,speed);
\r
209 outportb(0x40,speed >> 8);
\r
210 // Kludge to handle special case for digitized PC sounds
\r
211 if (TimerDivisor == (1192030 / (TickBase * 100)))
\r
212 TimerDivisor = (1192030 / (TickBase * 10));
\r
214 TimerDivisor = speed;
\r
218 TimerDivisor = 0x10000;
\r
222 ///////////////////////////////////////////////////////////////////////////
\r
224 // SDL_SetIntsPerSec() - Uses SDL_SetTimer0() to set the number of
\r
225 // interrupts generated by system timer 0 per second
\r
227 ///////////////////////////////////////////////////////////////////////////
\r
229 SDL_SetIntsPerSec(word ints)
\r
232 SDL_SetTimer0(1192030 / ints);
\r
235 #ifndef SD_USECATA3DSETTIMERSPEED
\r
237 SDL_SetTimerSpeed(void)
\r
240 void interrupt (*isr)(void);
\r
242 if ((DigiMode == sds_PC) && DigiPlaying)
\r
244 rate = TickBase * 100;
\r
245 isr = SDL_t0ExtremeAsmService;
\r
249 (MusicMode == smm_AdLib)
\r
250 || ((DigiMode == sds_SoundSource) && DigiPlaying)
\r
253 rate = TickBase * 10;
\r
254 isr = SDL_t0FastAsmService;
\r
258 rate = TickBase * 2;
\r
259 isr = SDL_t0SlowAsmService;
\r
262 if (rate != TimerRate)
\r
265 SDL_SetIntsPerSec(rate);
\r
271 SDL_SetTimerSpeed(void)
\r
275 if (MusicMode == smm_AdLib)
\r
276 rate = TickBase * 8;
\r
278 rate = TickBase * 2;
\r
279 SDL_SetIntsPerSec(rate);
\r
283 ///////////////////////////////////////////////////////////////////////////
\r
285 // SDL_TimingService() - Used by SDL_InitDelay() to determine a timing
\r
286 // value for the current system that we're running on
\r
288 ///////////////////////////////////////////////////////////////////////////
\r
289 //static void interrupt
\r
291 SDL_TimingService(void)
\r
299 outportb(0x20,0x20); // Ack interrupt
\r
302 ///////////////////////////////////////////////////////////////////////////
\r
304 // SDL_InitDelay() - Sets up TimerDelay's for SDL_Delay()
\r
306 ///////////////////////////////////////////////////////////////////////////
\r
308 SDL_InitDelay(void)
\r
313 setvect(8,SDL_TimingService); // Set to my timer 0 ISR
\r
315 SDL_SetIntsPerSec(1000); // Time 1ms
\r
317 for (i = 0,timer = 0;i < 10;i++) // Do timing test 10 times
\r
320 xor dx,dx // Zero DX
\r
321 mov cx,0xffff // Put starting value in CX
\r
322 mov [TimerDone],cx // TimerDone = false - 1
\r
323 #ifdef __BORLANDC__
\r
327 #ifdef __BORLANDC__
\r
331 jnz startloop // Make sure we're at the start
\r
332 #ifdef __BORLANDC__
\r
336 #ifdef __BORLANDC__
\r
339 test [TimerDone],1 // See if TimerDone flag got hit
\r
340 jnz done // Yep - drop out of the loop
\r
342 #ifdef __BORLANDC__
\r
350 if (0xffff - TimerVal > timer)
\r
351 timer = 0xffff - TimerVal;
\r
353 timer += timer / 2; // Use some slop
\r
354 TimerDelay10 = timer / (1000 / 10);
\r
355 TimerDelay25 = timer / (1000 / 25);
\r
356 TimerDelay100 = timer / (1000 / 100);
\r
358 SDL_SetTimer0(0); // Reset timer 0
\r
360 setvect(8,t0OldService); // Set back to old ISR
\r
363 ///////////////////////////////////////////////////////////////////////////
\r
365 // SDL_Delay() - Delays the specified amount of time
\r
367 ///////////////////////////////////////////////////////////////////////////
\r
369 SDL_Delay(word delay)
\r
376 #ifdef __BORLANDC__
\r
380 #ifdef __BORLANDC__
\r
383 test [TimerDone],0 // Useless code - just for timing equivilency
\r
386 #ifdef __BORLANDC__
\r
399 ///////////////////////////////////////////////////////////////////////////
\r
401 // SDL_PCPlaySample() - Plays the specified sample on the PC speaker
\r
403 ///////////////////////////////////////////////////////////////////////////
\r
409 SDL_PCPlaySample(byte huge *data,dword len)
\r
414 SDL_IndicatePC(true);
\r
416 pcLengthLeft = len;
\r
417 pcSound = (volatile byte far *)data;
\r
422 ///////////////////////////////////////////////////////////////////////////
\r
424 // SDL_PCStopSample() - Stops a sample playing on the PC speaker
\r
426 ///////////////////////////////////////////////////////////////////////////
\r
432 SDL_PCStopSample(void)
\r
437 /*(long)*/pcSound = 0;
\r
439 SDL_IndicatePC(false);
\r
441 asm in al,0x61 // Turn the speaker off
\r
442 asm and al,0xfd // ~2
\r
448 ///////////////////////////////////////////////////////////////////////////
\r
450 // SDL_PCPlaySound() - Plays the specified sound on the PC speaker
\r
452 ///////////////////////////////////////////////////////////////////////////
\r
458 SDL_PCPlaySound(PCSound far *sound)
\r
464 pcLengthLeft = sound->common.length;
\r
465 pcSound = sound->data;
\r
470 ///////////////////////////////////////////////////////////////////////////
\r
472 // SDL_PCStopSound() - Stops the current sound playing on the PC Speaker
\r
474 ///////////////////////////////////////////////////////////////////////////
\r
480 SDL_PCStopSound(void)
\r
485 /*(long)*/pcSound = 0;
\r
487 asm in al,0x61 // Turn the speaker off
\r
488 asm and al,0xfd // ~2
\r
494 ///////////////////////////////////////////////////////////////////////////
\r
496 // SDL_PCService() - Handles playing the next sample in a PC sound
\r
498 ///////////////////////////////////////////////////////////////////////////
\r
500 SDL_PCService(void)
\r
508 if (s != pcLastSample)
\r
514 if (s) // We have a frequency!
\r
516 t = pcSoundLookup[s];
\r
519 asm mov al,0xb6 // Write to channel 2 (speaker) timer
\r
522 asm out 42h,al // Low byte
\r
524 asm out 42h,al // High byte
\r
526 asm in al,0x61 // Turn the speaker & gate on
\r
530 else // Time for some silence
\r
532 asm in al,0x61 // Turn the speaker & gate off
\r
533 asm and al,0xfc // ~3
\r
540 if (!(--pcLengthLeft))
\r
543 SDL_SoundFinished();
\r
548 ///////////////////////////////////////////////////////////////////////////
\r
550 // SDL_ShutPC() - Turns off the pc speaker
\r
552 ///////////////////////////////////////////////////////////////////////////
\r
561 asm in al,0x61 // Turn the speaker & gate off
\r
562 asm and al,0xfc // ~3
\r
569 // Stuff for digitized sounds
\r
572 SDL_LoadDigiSegment(word page, global_game_variables_t *gvar)
\r
576 #if 0 // for debugging
\r
577 asm mov dx,STATUS_REGISTER_1
\r
579 asm mov dx,ATR_INDEX
\r
580 asm mov al,ATR_OVERSCAN
\r
582 asm mov al,10 // bright green
\r
586 addr = PM_GetSoundPage(page);
\r
587 PM_SetPageLock(gvar->pm.fi.PMSoundStart + page,pml_Locked, gvar);
\r
589 #if 0 // for debugging
\r
590 asm mov dx,STATUS_REGISTER_1
\r
592 asm mov dx,ATR_INDEX
\r
593 asm mov al,ATR_OVERSCAN
\r
595 asm mov al,3 // blue
\r
597 asm mov al,0x20 // normal
\r
605 SDL_PlayDigiSegment(memptr addr,word len)
\r
610 SDL_PCPlaySample(addr,len);
\r
612 //SS case sds_SoundSource:
\r
613 //SS SDL_SSPlaySample(addr,len);
\r
615 //SB case sds_SoundBlaster:
\r
616 //SB SDL_SBPlaySample(addr,len);
\r
622 SD_StopDigitized(global_game_variables_t *gvar)
\r
630 DigiNextAddr = nil;
\r
632 DigiMissed = false;
\r
633 DigiPlaying = false;
\r
634 DigiNumber = DigiPriority = 0;
\r
635 SoundPositioned = false;
\r
636 if ((DigiMode == sds_PC) && (SoundMode == sdm_PC))
\r
637 SDL_SoundFinished();
\r
642 SDL_PCStopSample();
\r
644 //SS case sds_SoundSource:
\r
645 //SS SDL_SSStopSample();
\r
647 //SB case sds_SoundBlaster:
\r
648 //SB SDL_SBStopSample();
\r
654 for (i = DigiLastStart;i < DigiLastEnd;i++)
\r
655 PM_SetPageLock(i + gvar->pm.fi.PMSoundStart,pml_Unlocked, gvar);
\r
661 SD_Poll(global_game_variables_t *gvar)
\r
663 if (DigiLeft && !DigiNextAddr)
\r
665 DigiNextLen = (DigiLeft >= PMPageSize)? PMPageSize : (DigiLeft % PMPageSize);
\r
666 DigiLeft -= DigiNextLen;
\r
668 DigiLastSegment = true;
\r
669 DigiNextAddr = SDL_LoadDigiSegment(DigiPage++, gvar);
\r
671 if (DigiMissed && DigiNextAddr)
\r
673 SDL_PlayDigiSegment(DigiNextAddr,DigiNextLen);
\r
674 DigiNextAddr = nil;
\r
675 DigiMissed = false;
\r
676 if (DigiLastSegment)
\r
678 DigiPlaying = false;
\r
679 DigiLastSegment = false;
\r
682 SDL_SetTimerSpeed();
\r
686 SD_SetPosition(int leftpos,int rightpos, global_game_variables_t *gvar)
\r
694 || ((leftpos == 15) && (rightpos == 15))
\r
696 Quit(gvar, "SD_SetPosition: Illegal position");
\r
700 //SB case sds_SoundBlaster:
\r
701 //SB SDL_PositionSBP(leftpos,rightpos);
\r
707 SD_PlayDigitized(word which,int leftpos,int rightpos, global_game_variables_t *gvar)
\r
715 SD_StopDigitized(gvar);
\r
716 if (which >= NumDigi)
\r
717 Quit(gvar, "SD_PlayDigitized: bad sound number");
\r
719 SD_SetPosition(leftpos,rightpos, gvar);
\r
721 DigiPage = DigiList[(which * 2) + 0];
\r
722 DigiLeft = DigiList[(which * 2) + 1];
\r
724 DigiLastStart = DigiPage;
\r
725 DigiLastEnd = DigiPage + ((DigiLeft + (PMPageSize - 1)) / PMPageSize);
\r
727 len = (DigiLeft >= PMPageSize)? PMPageSize : (DigiLeft % PMPageSize);
\r
728 addr = SDL_LoadDigiSegment(DigiPage++, gvar);
\r
730 DigiPlaying = true;
\r
731 DigiLastSegment = false;
\r
733 SDL_PlayDigiSegment(addr,len);
\r
736 DigiLastSegment = true;
\r
742 SDL_DigitizedDone(void)
\r
746 SDL_PlayDigiSegment(DigiNextAddr,DigiNextLen);
\r
747 DigiNextAddr = nil;
\r
748 DigiMissed = false;
\r
752 if (DigiLastSegment)
\r
754 DigiPlaying = false;
\r
755 DigiLastSegment = false;
\r
756 if ((DigiMode == sds_PC) && (SoundMode == sdm_PC))
\r
758 SDL_SoundFinished();
\r
761 DigiNumber = DigiPriority = 0;
\r
762 SoundPositioned = false;
\r
770 SD_SetDigiDevice(SDSMode mode, global_game_variables_t *gvar)
\r
772 boolean devicenotpresent;
\r
774 if (mode == DigiMode)
\r
777 SD_StopDigitized(gvar);
\r
779 devicenotpresent = false;
\r
782 case sds_SoundBlaster:
\r
783 if (!SoundBlasterPresent)
\r
785 if (SoundSourcePresent)
\r
786 mode = sds_SoundSource;
\r
788 devicenotpresent = true;
\r
791 case sds_SoundSource:
\r
792 if (!SoundSourcePresent)
\r
793 devicenotpresent = true;
\r
797 if (!devicenotpresent)
\r
799 //SS if (DigiMode == sds_SoundSource)
\r
804 //SS if (mode == sds_SoundSource)
\r
805 //SS SDL_StartSS();
\r
807 SDL_SetTimerSpeed();
\r
812 SDL_SetupDigi(global_game_variables_t *gvar)
\r
819 PM_UnlockMainMem(gvar);
\r
820 MM_GetPtr(&list,PMPageSize, gvar);
\r
821 PM_CheckMainMem(gvar);
\r
822 p = (word far *)MK_FP(PM_GetPage(gvar->pm.fi.ChunksInFile - 1, gvar),0);
\r
823 _fmemcpy((void far *)list,(void far *)p,PMPageSize);
\r
824 pg = gvar->pm.fi.PMSoundStart;
\r
825 for (i = 0;i < PMPageSize / (sizeof(word) * 2);i++,p += 2)
\r
827 if (pg >= gvar->pm.fi.ChunksInFile - 1)
\r
829 pg += (p[1] + (PMPageSize - 1)) / PMPageSize;
\r
831 PM_UnlockMainMem(gvar);
\r
832 MM_GetPtr(MEMPTRCONV DigiList,i * sizeof(word) * 2, gvar);
\r
833 _fmemcpy((void far *)DigiList,(void far *)list,i * sizeof(word) * 2);
\r
834 MM_FreePtr(&list, gvar);
\r
837 for (i = 0;i < LASTSOUND;i++)
\r
843 ///////////////////////////////////////////////////////////////////////////
\r
845 // alOut(n,b) - Puts b in AdLib card register n
\r
847 ///////////////////////////////////////////////////////////////////////////
\r
849 alOut(byte n,byte b)
\r
858 SDL_Delay(TimerDelay10);
\r
875 SDL_Delay(TimerDelay25);
\r
920 ///////////////////////////////////////////////////////////////////////////
\r
922 // SDL_SetInstrument() - Puts an instrument into a generator
\r
924 ///////////////////////////////////////////////////////////////////////////
\r
927 SDL_SetInstrument(int track,int which,Instrument far *inst,boolean percussive)
\r
933 c = pcarriers[which];
\r
934 m = pmodifiers[which];
\r
938 c = carriers[which];
\r
939 m = modifiers[which];
\r
942 tracks[track - 1]->inst = *inst;
\r
943 tracks[track - 1]->percussive = percussive;
\r
945 alOut(m + alChar,inst->mChar);
\r
946 alOut(m + alScale,inst->mScale);
\r
947 alOut(m + alAttack,inst->mAttack);
\r
948 alOut(m + alSus,inst->mSus);
\r
949 alOut(m + alWave,inst->mWave);
\r
951 // Most percussive instruments only use one cell
\r
954 alOut(c + alChar,inst->cChar);
\r
955 alOut(c + alScale,inst->cScale);
\r
956 alOut(c + alAttack,inst->cAttack);
\r
957 alOut(c + alSus,inst->cSus);
\r
958 alOut(c + alWave,inst->cWave);
\r
961 alOut(which + alFeedCon,inst->nConn); // DEBUG - I think this is right
\r
965 ///////////////////////////////////////////////////////////////////////////
\r
967 // SDL_ALStopSound() - Turns off any sound effects playing through the
\r
970 ///////////////////////////////////////////////////////////////////////////
\r
976 SDL_ALStopSound(void)
\r
981 /*(long)*/alSound = 0;
\r
982 alOut(alFreqH + 0,0);
\r
988 SDL_AlSetFXInst(Instrument far *inst)
\r
994 alOut(m + alChar,inst->mChar);
\r
995 alOut(m + alScale,inst->mScale);
\r
996 alOut(m + alAttack,inst->mAttack);
\r
997 alOut(m + alSus,inst->mSus);
\r
998 alOut(m + alWave,inst->mWave);
\r
999 alOut(c + alChar,inst->cChar);
\r
1000 alOut(c + alScale,inst->cScale);
\r
1001 alOut(c + alAttack,inst->cAttack);
\r
1002 alOut(c + alSus,inst->cSus);
\r
1003 alOut(c + alWave,inst->cWave);
\r
1005 // Note: Switch commenting on these lines for old MUSE compatibility
\r
1006 // alOut(alFeedCon,inst->nConn);
\r
1007 alOut(alFeedCon,0);
\r
1010 ///////////////////////////////////////////////////////////////////////////
\r
1012 // SDL_ALPlaySound() - Plays the specified sound on the AdLib card
\r
1014 ///////////////////////////////////////////////////////////////////////////
\r
1020 SDL_ALPlaySound(AdLibSound far *sound, global_game_variables_t *gvar)
\r
1022 Instrument __far *inst;
\r
1025 SDL_ALStopSound();
\r
1030 alLengthLeft = sound->common.length;
\r
1031 data = sound->data;
\r
1034 alSound = (byte far *)data;
\r
1035 alBlock = ((sound->block & 7) << 2) | 0x20;
\r
1036 inst = &sound->inst;
\r
1038 if (!(inst->mSus | inst->cSus))
\r
1041 Quit(gvar, "SDL_ALPlaySound() - Bad instrument");
\r
1044 SDL_AlSetFXInst(&alZeroInst); // DEBUG
\r
1045 SDL_AlSetFXInst(inst);
\r
1051 ///////////////////////////////////////////////////////////////////////////
\r
1053 // SDL_ALSoundService() - Plays the next sample out through the AdLib card
\r
1055 ///////////////////////////////////////////////////////////////////////////
\r
1058 SDL_ALSoundService(void)
\r
1066 alOut(alFreqH + 0,0);
\r
1069 alOut(alFreqL + 0,s);
\r
1070 alOut(alFreqH + 0,alBlock);
\r
1073 if (!(--alLengthLeft))
\r
1075 /*(long)*/alSound = 0;
\r
1076 alOut(alFreqH + 0,0);
\r
1077 SDL_SoundFinished();
\r
1084 ///////////////////////////////////////////////////////////////////////////
\r
1086 // SDL_SelectMeasure() - sets up sequencing variables for a given track
\r
1088 ///////////////////////////////////////////////////////////////////////////
\r
1090 SDL_SelectMeasure(ActiveTrack *track)
\r
1092 track->seq = track->moods[track->mood];
\r
1093 track->nextevent = 0;
\r
1099 SDL_ALService(void)
\r
1109 while (sqHackLen && (sqHackTime <= alTimeCount))
\r
1112 sqHackTime = alTimeCount + *sqHackPtr++;
\r
1122 sqHackPtr = (word far *)sqHack;
\r
1123 sqHackLen = sqHackSeqLen;
\r
1124 alTimeCount = sqHackTime = 0;
\r
1129 ///////////////////////////////////////////////////////////////////////////
\r
1131 // SDL_ShutAL() - Shuts down the AdLib card for sound effects
\r
1133 ///////////////////////////////////////////////////////////////////////////
\r
1140 alOut(alEffects,0);
\r
1141 alOut(alFreqH + 0,0);
\r
1142 SDL_AlSetFXInst(&alZeroInst);
\r
1148 ///////////////////////////////////////////////////////////////////////////
\r
1150 // SDL_CleanAL() - Totally shuts down the AdLib card
\r
1152 ///////////////////////////////////////////////////////////////////////////
\r
1161 alOut(alEffects,0);
\r
1162 for (i = 1;i < 0xf5;i++)
\r
1168 ///////////////////////////////////////////////////////////////////////////
\r
1170 // SDL_StartAL() - Starts up the AdLib card for sound effects
\r
1172 ///////////////////////////////////////////////////////////////////////////
\r
1177 alOut(alEffects,alFXReg);
\r
1178 SDL_AlSetFXInst(&alZeroInst);
\r
1181 ///////////////////////////////////////////////////////////////////////////
\r
1183 // SDL_DetectAdLib() - Determines if there's an AdLib (or SoundBlaster
\r
1184 // emulating an AdLib) present
\r
1186 ///////////////////////////////////////////////////////////////////////////
\r
1188 SDL_DetectAdLib(void)
\r
1190 byte status1,status2;
\r
1193 alOut(4,0x60); // Reset T1 & T2
\r
1194 alOut(4,0x80); // Reset IRQ
\r
1195 status1 = readstat();
\r
1196 alOut(2,0xff); // Set timer 1
\r
1197 alOut(4,0x21); // Start timer 1
\r
1198 SDL_Delay(TimerDelay100);
\r
1204 #ifdef __BORLANDC__
\r
1208 #ifdef __BORLANDC__
\r
1216 status2 = readstat();
\r
1220 if (((status1 & 0xe0) == 0x00) && ((status2 & 0xe0) == 0xc0))
\r
1222 for (i = 1;i <= 0xf5;i++) // Zero all the registers
\r
1225 alOut(1,0x20); // Set WSE=1
\r
1226 alOut(8,0); // Set CSM=0 & SEL=0
\r
1234 ///////////////////////////////////////////////////////////////////////////
\r
1236 // SDL_t0Service() - My timer 0 ISR which handles the different timings and
\r
1237 // dispatches to whatever other routines are appropriate
\r
1239 ///////////////////////////////////////////////////////////////////////////
\r
1240 //static void interrupt
\r
1242 SDL_t0Service(void)
\r
1244 static word count = 1;
\r
1245 boolean myackflag = 0;
\r
1247 //00#if 0 // for debugging
\r
1248 asm mov dx,STATUS_REGISTER_1
\r
1250 asm mov dx,ATR_INDEX
\r
1251 asm mov al,ATR_OVERSCAN
\r
1253 asm mov al,4 // red
\r
1259 if ((MusicMode == smm_AdLib) || (DigiMode == sds_SoundSource))
\r
1262 //SS SDL_SSService();
\r
1263 // if (!(++count & 7))
\r
1264 if (!(++count % 10))
\r
1268 if (SoundUserHook)
\r
1271 // if (!(count & 3))
\r
1274 switch (SoundMode)
\r
1280 SDL_ALSoundService();
\r
1287 if (!(++count & 1))
\r
1291 if (SoundUserHook)
\r
1294 switch (SoundMode)
\r
1300 SDL_ALSoundService();
\r
1306 mov ax,[WORD PTR TimerCount]
\r
1307 add ax,[WORD PTR TimerDivisor]
\r
1308 mov [WORD PTR TimerCount],ax
\r
1311 #ifdef __BORLANDC__
\r
1315 #ifdef __BORLANDC__
\r
1319 #ifdef __BORLANDC__
\r
1323 #ifdef __WATCOMC__
\r
1327 t0OldService(); // If we overflow a word, time to call old int handler
\r
1329 outportb(0x20,0x20); // Ack the interrupt
\r
1331 //00#if 0 // for debugging
\r
1332 asm mov dx,STATUS_REGISTER_1
\r
1334 asm mov dx,ATR_INDEX
\r
1335 asm mov al,ATR_OVERSCAN
\r
1337 asm mov al,3 // blue
\r
1339 asm mov al,0x20 // normal
\r
1344 ////////////////////////////////////////////////////////////////////////////
\r
1346 // SDL_ShutDevice() - turns off whatever device was being used for sound fx
\r
1348 ////////////////////////////////////////////////////////////////////////////
\r
1350 SDL_ShutDevice(void)
\r
1352 switch (SoundMode)
\r
1361 SoundMode = sdm_Off;
\r
1364 ///////////////////////////////////////////////////////////////////////////
\r
1366 // SDL_CleanDevice() - totally shuts down all sound devices
\r
1368 ///////////////////////////////////////////////////////////////////////////
\r
1370 SDL_CleanDevice(void)
\r
1372 if ((SoundMode == sdm_AdLib) || (MusicMode == smm_AdLib))
\r
1376 ///////////////////////////////////////////////////////////////////////////
\r
1378 // SDL_StartDevice() - turns on whatever device is to be used for sound fx
\r
1380 ///////////////////////////////////////////////////////////////////////////
\r
1382 SDL_StartDevice(void)
\r
1384 switch (SoundMode)
\r
1390 SoundNumber = SoundPriority = 0;
\r
1394 SDL_SetTimerSpeed(void)
\r
1398 if (MusicMode == smm_AdLib)
\r
1399 rate = TickBase * 8;
\r
1401 rate = TickBase * 2;
\r
1402 SDL_SetIntsPerSec(rate);
\r
1405 // Public routines
\r
1407 ///////////////////////////////////////////////////////////////////////////
\r
1409 // SD_SetSoundMode() - Sets which sound hardware to use for sound effects
\r
1411 ///////////////////////////////////////////////////////////////////////////
\r
1413 SD_SetSoundMode(SDMode mode, global_game_variables_t *gvar)
\r
1415 boolean result = false;
\r
1418 SD_StopSound(gvar);
\r
1421 if ((mode == sdm_AdLib) && !AdLibPresent)
\r
1427 NeedsDigitized = false;
\r
1431 tableoffset = STARTPCSOUNDS;
\r
1432 NeedsDigitized = false;
\r
1438 tableoffset = STARTADLIBSOUNDS;
\r
1439 NeedsDigitized = false;
\r
1448 if (result && (mode != SoundMode))
\r
1453 SoundTable = (word *)(&gvar->ca.audiosegs[tableoffset]);
\r
1455 SDL_StartDevice();
\r
1458 SDL_SetTimerSpeed();
\r
1463 ///////////////////////////////////////////////////////////////////////////
\r
1465 // SD_SetMusicMode() - sets the device to use for background music
\r
1467 ///////////////////////////////////////////////////////////////////////////
\r
1469 SD_SetMusicMode(SMMode mode)
\r
1471 boolean result = false;
\r
1473 SD_FadeOutMusic();
\r
1474 while (SD_MusicPlaying())
\r
1480 NeedsMusic = false;
\r
1486 NeedsMusic = true;
\r
1495 SDL_SetTimerSpeed();
\r
1500 ///////////////////////////////////////////////////////////////////////////
\r
1502 // SD_Startup() - starts up the Sound Mgr
\r
1503 // Detects all additional sound hardware and installs my ISR
\r
1505 ///////////////////////////////////////////////////////////////////////////
\r
1507 SD_Startup(global_game_variables_t *gvar)
\r
1516 ssIsTandy = false;
\r
1517 //SS ssNoCheck = false;
\r
1518 alNoCheck = false;
\r
1519 //SB sbNoCheck = false;
\r
1520 //SB sbNoProCheck = false;
\r
1522 for (i = 1;i < _argc;i++)
\r
1524 switch (US_CheckParm(_argv[i],ParmStrings))
\r
1526 case 0: // No AdLib detection
\r
1529 //SB case 1: // No SoundBlaster detection
\r
1530 //SB sbNoCheck = true;
\r
1532 //SB case 2: // No SoundBlaster Pro detection
\r
1533 //SB sbNoProCheck = true;
\r
1536 //SS ssNoCheck = true; // No Sound Source detection
\r
1538 case 4: // Tandy Sound Source handling
\r
1541 //SS case 5: // Sound Source present at LPT1
\r
1543 //SS ssNoCheck = SoundSourcePresent = true;
\r
1545 //SS case 6: // Sound Source present at LPT2
\r
1547 //SS ssNoCheck = SoundSourcePresent = true;
\r
1549 //SS case 7: // Sound Source present at LPT3
\r
1551 //SS ssNoCheck = SoundSourcePresent = true;
\r
1557 SoundUserHook = 0;
\r
1559 t0OldService = getvect(8); // Get old timer 0 ISR
\r
1561 SDL_InitDelay(); // SDL_InitDelay() uses t0OldService
\r
1563 setvect(8,SDL_t0Service); // Set to my timer 0 ISR
\r
1564 LocalTime = TimeCount = alTimeCount = 0;
\r
1566 SD_SetSoundMode(sdm_Off, gvar);
\r
1567 SD_SetMusicMode(smm_Off);
\r
1569 //SS if (!ssNoCheck)
\r
1570 //SS SoundSourcePresent = SDL_DetectSoundSource();
\r
1574 AdLibPresent = SDL_DetectAdLib();
\r
1575 //SB if (AdLibPresent) && !sbNoCheck)
\r
1577 //SB int port = -1;
\r
1578 //SB char *env = getenv("BLASTER");
\r
1584 //SB while (isspace(*env))
\r
1587 //SB switch (toupper(*env))
\r
1590 //SB temp = strtol(env + 1,&env,16);
\r
1593 //SB (temp >= 0x210)
\r
1594 //SB && (temp <= 0x260)
\r
1595 //SB && (!(temp & 0x00f))
\r
1597 //SB port = (temp - 0x200) >> 4;
\r
1599 //SB Quit(gvar, "SD_Startup: Unsupported address value in BLASTER");
\r
1602 //SB temp = strtol(env + 1,&env,10);
\r
1606 //SB && (temp <= 10)
\r
1607 //SB && (sbIntVectors[temp] != -1)
\r
1610 //SB sbInterrupt = temp;
\r
1611 //SB sbIntVec = sbIntVectors[sbInterrupt];
\r
1614 //SB Quit(gvar, "SD_Startup: Unsupported interrupt value in BLASTER");
\r
1617 //SB temp = strtol(env + 1,&env,10);
\r
1618 //SB if ((temp == 0) || (temp == 1) || (temp == 3))
\r
1619 //SB SDL_SBSetDMA(temp);
\r
1621 //SB Quit(gvar, "SD_Startup: Unsupported DMA value in BLASTER");
\r
1624 //SB while (isspace(*env))
\r
1626 //SB while (*env && !isspace(*env))
\r
1632 //SB SoundBlasterPresent = SDL_DetectSoundBlaster(port);
\r
1636 for (i = 0;i < 255;i++)
\r
1637 pcSoundLookup[i] = i * 60;
\r
1639 //SB if (SoundBlasterPresent)
\r
1640 //SB SDL_StartSB();
\r
1642 SDL_SetupDigi(gvar);
\r
1644 SD_Started = true;
\r
1647 ///////////////////////////////////////////////////////////////////////////
\r
1649 // SD_Default() - Sets up the default behaviour for the Sound Mgr whether
\r
1650 // the config file was present or not.
\r
1652 ///////////////////////////////////////////////////////////////////////////
\r
1654 SD_Default(boolean gotit,SDMode sd,SMMode sm, global_game_variables_t *gvar)
\r
1656 boolean gotsd,gotsm;
\r
1658 gotsd = gotsm = gotit;
\r
1660 if (gotsd) // Make sure requested sound hardware is available
\r
1665 gotsd = AdLibPresent;
\r
1676 if (sd != SoundMode)
\r
1677 SD_SetSoundMode(sd, gvar);
\r
1680 if (gotsm) // Make sure requested music hardware is available
\r
1685 gotsm = AdLibPresent;
\r
1694 if (sm != MusicMode)
\r
1695 SD_SetMusicMode(sm);
\r
1698 ///////////////////////////////////////////////////////////////////////////
\r
1700 // SD_Shutdown() - shuts down the Sound Mgr
\r
1701 // Removes sound ISR and turns off whatever sound hardware was active
\r
1703 ///////////////////////////////////////////////////////////////////////////
\r
1705 SD_Shutdown(global_game_variables_t *gvar)
\r
1711 SD_StopSound(gvar);
\r
1713 SDL_CleanDevice();
\r
1715 //SB if (SoundBlasterPresent)
\r
1716 //SB SDL_ShutSB();
\r
1718 //SS if (SoundSourcePresent)
\r
1719 //SS SDL_ShutSS();
\r
1726 setvect(8,t0OldService);
\r
1730 SD_Started = false;
\r
1733 ///////////////////////////////////////////////////////////////////////////
\r
1735 // SD_SetUserHook() - sets the routine that the Sound Mgr calls every 1/70th
\r
1736 // of a second from its timer 0 ISR
\r
1738 ///////////////////////////////////////////////////////////////////////////
\r
1740 SD_SetUserHook(void (* hook)(void))
\r
1742 SoundUserHook = hook;
\r
1745 ///////////////////////////////////////////////////////////////////////////
\r
1747 // SD_PositionSound() - Sets up a stereo imaging location for the next
\r
1748 // sound to be played. Each channel ranges from 0 to 15.
\r
1750 ///////////////////////////////////////////////////////////////////////////
\r
1752 SD_PositionSound(int leftvol,int rightvol)
\r
1754 LeftPosition = leftvol;
\r
1755 RightPosition = rightvol;
\r
1756 nextsoundpos = true;
\r
1759 ///////////////////////////////////////////////////////////////////////////
\r
1761 // SD_PlaySound() - plays the specified sound on the appropriate hardware
\r
1763 ///////////////////////////////////////////////////////////////////////////
\r
1765 SD_PlaySound(soundnames sound, global_game_variables_t *gvar)
\r
1768 SoundCommon far *s;
\r
1771 lp = LeftPosition;
\r
1772 rp = RightPosition;
\r
1774 RightPosition = 0;
\r
1776 ispos = nextsoundpos;
\r
1777 nextsoundpos = false;
\r
1782 s = MK_FP(SoundTable[sound],0);
\r
1783 if ((SoundMode != sdm_Off) && !s)
\r
1784 Quit(gvar, "SD_PlaySound() - Uncached sound");
\r
1786 if ((DigiMode != sds_Off) && (DigiMap[sound] != -1))
\r
1788 if ((DigiMode == sds_PC) && (SoundMode == sdm_PC))
\r
1790 if (s->priority < SoundPriority)
\r
1793 SDL_PCStopSound();
\r
1795 SD_PlayDigitized(DigiMap[sound],lp,rp, gvar);
\r
1796 SoundPositioned = ispos;
\r
1797 SoundNumber = sound;
\r
1798 SoundPriority = s->priority;
\r
1804 if (DigiPriority && !DigiNumber)
\r
1807 Quit(gvar, "SD_PlaySound: Priority without a sound");
\r
1811 if (s->priority < DigiPriority)
\r
1814 SD_PlayDigitized(DigiMap[sound],lp,rp, gvar);
\r
1815 SoundPositioned = ispos;
\r
1816 DigiNumber = sound;
\r
1817 DigiPriority = s->priority;
\r
1823 if (SoundMode == sdm_Off)
\r
1826 Quit(gvar, "SD_PlaySound() - Zero length sound");
\r
1827 if (s->priority < SoundPriority)
\r
1830 switch (SoundMode)
\r
1833 SDL_PCPlaySound((void far *)s);
\r
1836 SDL_ALPlaySound((void far *)s, gvar);
\r
1840 SoundNumber = sound;
\r
1841 SoundPriority = s->priority;
\r
1846 ///////////////////////////////////////////////////////////////////////////
\r
1848 // SD_SoundPlaying() - returns the sound number that's playing, or 0 if
\r
1849 // no sound is playing
\r
1851 ///////////////////////////////////////////////////////////////////////////
\r
1853 SD_SoundPlaying(void)
\r
1855 boolean result = false;
\r
1857 switch (SoundMode)
\r
1860 result = pcSound? true : false;
\r
1863 result = alSound? true : false;
\r
1868 return(SoundNumber);
\r
1873 ///////////////////////////////////////////////////////////////////////////
\r
1875 // SD_StopSound() - if a sound is playing, stops it
\r
1877 ///////////////////////////////////////////////////////////////////////////
\r
1879 SD_StopSound(global_game_variables_t *gvar)
\r
1882 SD_StopDigitized(gvar);
\r
1884 switch (SoundMode)
\r
1887 SDL_PCStopSound();
\r
1890 SDL_ALStopSound();
\r
1894 SoundPositioned = false;
\r
1896 SDL_SoundFinished();
\r
1899 ///////////////////////////////////////////////////////////////////////////
\r
1901 // SD_WaitSoundDone() - waits until the current sound is done playing
\r
1903 ///////////////////////////////////////////////////////////////////////////
\r
1905 SD_WaitSoundDone(void)
\r
1907 while (SD_SoundPlaying())
\r
1911 ///////////////////////////////////////////////////////////////////////////
\r
1913 // SD_MusicOn() - turns on the sequencer
\r
1915 ///////////////////////////////////////////////////////////////////////////
\r
1922 ///////////////////////////////////////////////////////////////////////////
\r
1924 // SD_MusicOff() - turns off the sequencer and any playing notes
\r
1926 ///////////////////////////////////////////////////////////////////////////
\r
1933 switch (MusicMode)
\r
1937 alOut(alEffects,0);
\r
1938 for (i = 0;i < sqMaxTracks;i++)
\r
1939 alOut(alFreqH + i + 1,0);
\r
1945 ///////////////////////////////////////////////////////////////////////////
\r
1947 // SD_StartMusic() - starts playing the music pointed to
\r
1949 ///////////////////////////////////////////////////////////////////////////
\r
1951 SD_StartMusic(MusicGroup far *music)
\r
1957 if (MusicMode == smm_AdLib)
\r
1959 sqHackPtr = sqHack = music->values;
\r
1960 sqHackSeqLen = sqHackLen = music->length;
\r
1969 ///////////////////////////////////////////////////////////////////////////
\r
1971 // SD_FadeOutMusic() - starts fading out the music. Call SD_MusicPlaying()
\r
1972 // to see if the fadeout is complete
\r
1974 ///////////////////////////////////////////////////////////////////////////
\r
1976 SD_FadeOutMusic(void)
\r
1978 switch (MusicMode)
\r
1981 // DEBUG - quick hack to turn the music off
\r
1987 ///////////////////////////////////////////////////////////////////////////
\r
1989 // SD_MusicPlaying() - returns true if music is currently playing, false if
\r
1992 ///////////////////////////////////////////////////////////////////////////
\r
1994 SD_MusicPlaying(void)
\r
1998 switch (MusicMode)
\r
2002 // DEBUG - not written
\r
2022 void SDL_turnOnPCSpeaker(word timerval)
\r
2038 void SDL_turnOffPCSpeaker()
\r
2047 void SDL_setPCSpeaker(byte val)
\r
2062 if(*pcSound!=pcLastSample)
\r
2064 pcLastSample=*pcSound;
\r
2067 SDL_turnOnPCSpeaker(pcLastSample*60);
\r
2069 SDL_turnOffPCSpeaker();
\r
2076 SoundNumber=(soundnames)0;
\r
2078 SDL_turnOffPCSpeaker();
\r
2082 if(alSound && !alNoIRQ)
\r
2086 alOutInIRQ(alFreqL,*alSound);
\r
2087 alOutInIRQ(alFreqH,alBlock);
\r
2089 else alOutInIRQ(alFreqH,0);
\r
2095 SoundNumber=(soundnames)0;
\r
2097 alOutInIRQ(alFreqH,0);
\r
2120 if(sqActive && !alNoIRQ)
\r
2126 if(sqHackTime>alTimeCount) break;
\r
2127 sqHackTime=alTimeCount+*(sqHackPtr+1);
\r
2128 alOutInIRQ(*(byte *)sqHackPtr,*(((byte *)sqHackPtr)+1));
\r
2138 sqHackLen=sqHackSeqLen;
\r
2146 if(!(inp(ssStatus)&0x40))
\r
2148 outp(ssData,*ssSample++);
\r
2149 outp(ssControl,ssOff);
\r
2152 outp(ssControl,ssOn);
\r
2159 SDL_DigitizedDoneInIRQ();
\r
2164 TimerCount+=TimerDivisor;
\r
2165 if(*((word *)&TimerCount+1))
\r
2167 *((word *)&TimerCount+1)=0;
\r
2176 // Timer 0 ISR for 7000Hz interrupts
\r
2177 void interrupt SDL_t0ExtremeAsmService(void)
\r
2183 SDL_setPCSpeaker(((*pcSound++)&0x80)>>6);
\r
2188 SDL_turnOffPCSpeaker();
\r
2189 SDL_DigitizedDoneInIRQ();
\r
2203 // Timer 0 ISR for 7000Hz interrupts
\r
2204 void interrupt __SDL_t0ExtremeAsmService()
\r
2210 SDL_setPCSpeaker(((*pcSound++)&0x80)>>6);
\r
2215 SDL_turnOffPCSpeaker();
\r
2216 SDL_DigitizedDoneInIRQ();
\r
2230 // Timer 0 ISR for 700Hz interrupts
\r
2231 void interrupt SDL_t0FastAsmService(void)
\r
2236 // Timer 0 ISR for 140Hz interrupts
\r
2237 void interrupt SDL_t0SlowAsmService(void)
\r
2248 TimerCount+=TimerDivisor;
\r
2249 if(*((word *)&TimerCount+1))
\r
2251 *((word *)&TimerCount+1)=0;
\r
2258 void SDL_IndicatePC(boolean ind)
\r
2264 SDL_DigitizedDoneInIRQ(void)
\r
2268 SDL_PlayDigiSegment(DigiNextAddr,DigiNextLen/*,true*/);
\r
2269 DigiNextAddr = nil;
\r
2270 DigiMissed = false;
\r
2274 if (DigiLastSegment)
\r
2276 DigiPlaying = false;
\r
2277 DigiLastSegment = false;
\r
2278 if ((DigiMode == sds_PC) && (SoundMode == sdm_PC))
\r
2280 SDL_SoundFinished();
\r
2284 DigiNumber = (soundnames) 0;
\r
2287 SoundPositioned = false;
\r
2290 DigiMissed = true;
\r
2294 // Inside an interrupt handler interrupts should already be disabled
\r
2295 // so don't disable them again and cause V86 exceptions which cost
\r
2296 // aprox. 300 processor tics!
\r
2299 void alOutInIRQ(byte n,byte b)
\r