SHPLAY.CPP 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257
  1. /*******************************************************************************
  2. FILE: SHPLAY.CPP
  3. DESCRIPTION: Sonic Holography 3D sound player
  4. AUTHOR: Peter M. Freese
  5. CREATED: 11-09-95
  6. COPYRIGHT: Copyright (c) 1995 Q Studios Corporation
  7. *******************************************************************************/
  8. #include <conio.h>
  9. #include <dos.h>
  10. #include <stdlib.h>
  11. #include <stdio.h>
  12. #include <math.h>
  13. #include <string.h>
  14. #include <io.h>
  15. #include <fcntl.h>
  16. #include "debug4g.h"
  17. #include "error.h"
  18. #include "fx_man.h"
  19. #include "getopt.h"
  20. #include "inifile.h"
  21. #include "key.h"
  22. #include "misc.h"
  23. #include "music.h"
  24. #include "resource.h"
  25. #include "task_man.h"
  26. #include "textio.h"
  27. #include "typedefs.h"
  28. #include "usrhooks.h"
  29. #include "helix.h"
  30. #include "gfx.h"
  31. #include "pcx.h"
  32. #define kAspectAdjustX 1.2
  33. #define kPi 3.141592654
  34. #define kPi2 (kPi * 2)
  35. #define kAngle360 kPi2
  36. #define kAngle180 (kAngle360 / 2)
  37. #define kAngle90 (kAngle360 / 4)
  38. #define kAngle60 (kAngle360 / 6)
  39. #define kAngle45 (kAngle360 / 8)
  40. #define kAngle30 (kAngle360 / 12)
  41. #define kAngle20 (kAngle360 / 18)
  42. #define kAngle15 (kAngle360 / 24)
  43. #define kAngle10 (kAngle360 / 36)
  44. #define kAngle5 (kAngle360 / 72)
  45. #define kTimerRate 30
  46. #define kSampleRate 11110
  47. #define kIntraAuralTime 0.0006
  48. #define kVolScale 10
  49. #define kMaxPlaySounds 255
  50. struct MENU_LINE
  51. {
  52. int id;
  53. char *name;
  54. char *hint;
  55. };
  56. Resource gResource;
  57. IniFile config("SHPLAY.INI");
  58. MENU_LINE SoundCardMenu[] =
  59. {
  60. { -1, "None", "No sound card" },
  61. { SoundBlaster, "SoundBlaster", "Sound Blaster, SB Pro, SB 16, or compatible" },
  62. { ProAudioSpectrum, "Pro Audio Spectrum", "Media Vision Pro Audio Sprectrum" },
  63. { SoundMan16, "Sound Man 16", "Logitech Sound Man 16" },
  64. { Awe32, "Awe 32", "Sound Blaster Awe 32" },
  65. { SoundScape, "SoundScape", "Ensoniq Sound Scape" },
  66. { UltraSound, "Gravis UltraSound", "Advanced Gravis UltraSound" },
  67. { SoundSource, "Disney Sound Source", "Disney Sound Source" },
  68. { TandySoundSource, "Tandy Sound Source", "Tandy Sound Source" },
  69. { 0, NULL, NULL },
  70. };
  71. MENU_LINE BlasterTypeMenu[] =
  72. {
  73. { fx_SB, "Sound Blaster or compatible", "Vanilla Sound Blaster Card" },
  74. { fx_SB20, "Sound Blaster 2.0", "Sound Blaster 2.0 Card" },
  75. { fx_SBPro, "Sound Blaster Pro (old)", "Original Sound Blaster Pro" },
  76. { fx_SBPro2, "Sound Blaster Pro (new)", "Newer Sound Blaster Pro" },
  77. { fx_SB16, "Sound Blaster 16 or AWE32", "Sound Blaster 16 or AWE 32" },
  78. { 0, NULL, NULL },
  79. };
  80. MENU_LINE BlasterAddressMenu[] =
  81. {
  82. { 0x210, "210h", "Sound Blaster Port Address at 210h" },
  83. { 0x220, "220h (default)", "Sound Blaster Port Address at 220h" },
  84. { 0x230, "230h", "Sound Blaster Port Address at 230h" },
  85. { 0x240, "240h", "Sound Blaster Port Address at 240h" },
  86. { 0x250, "250h", "Sound Blaster Port Address at 250h" },
  87. { 0x260, "260h", "Sound Blaster Port Address at 260h" },
  88. { 0x270, "270h", "Sound Blaster Port Address at 270h" },
  89. { 0x280, "280h", "Sound Blaster Port Address at 280h" },
  90. { 0, NULL, NULL },
  91. };
  92. MENU_LINE BlasterIRQMenu[] =
  93. {
  94. { 2, "IRQ 2", "Sound Blaster Set to IRQ 2" },
  95. { 3, "IRQ 3", "Sound Blaster Set to IRQ 3" },
  96. { 5, "IRQ 5 (default", "Sound Blaster Set to IRQ 5" },
  97. { 7, "IRQ 7", "Sound Blaster Set to IRQ 7" },
  98. { 10, "IRQ 10", "Sound Blaster Set to IRQ 10" },
  99. { 11, "IRQ 11", "Sound Blaster Set to IRQ 11" },
  100. { 12, "IRQ 12", "Sound Blaster Set to IRQ 12" },
  101. { 15, "IRQ 15", "Sound Blaster Set to IRQ 15" },
  102. { 0, NULL, NULL },
  103. };
  104. MENU_LINE BlasterDma8Menu[] =
  105. {
  106. { 0, "Channel 0", "8 bit DMA on channel 0" },
  107. { 1, "Channel 1 (default)", "8 bit DMA on channel 1" },
  108. { 3, "Channel 3", "8 bit DMA on channel 3" },
  109. { 0, NULL, NULL },
  110. };
  111. MENU_LINE BlasterDma16Menu[] =
  112. {
  113. { 5, "Channel 5 (default)", "16 bit DMA on channel 5" },
  114. { 6, "Channel 6", "16 bit DMA on channel 6" },
  115. { 7, "Channel 7", "16 bit DMA on channel 7" },
  116. { 0, NULL, NULL },
  117. };
  118. MENU_LINE BitMenu[] =
  119. {
  120. { 8, "8-bit", "8-bit low quality mixing" },
  121. { 16, "16-bit", "16-bit high quality mixing" },
  122. { 0, NULL, NULL },
  123. };
  124. MENU_LINE ChannelMenu[] =
  125. {
  126. { 1, "Mono", "Mono sound, no stereo imaging" },
  127. { 2, "Stereo", "Stereo sound, full stereo imaging" },
  128. { 0, NULL, NULL },
  129. };
  130. MENU_LINE VoiceMenu[] =
  131. {
  132. { 8, "8", "8 voice mixing" },
  133. { 16, "16", "16 voice mixing" },
  134. { 24, "24", "24 voice mixing" },
  135. { 32, "32", "32 voice mixing" },
  136. { 0, NULL, NULL },
  137. };
  138. MENU_LINE MixRateMenu[] =
  139. {
  140. { 11110, "11 KHz", "Mix at 11 KHz" },
  141. { 22220, "22 KHz", "Mix at 22 KHz" },
  142. { 44440, "44 KHz", "Mix at 44 KHz" },
  143. { 0, NULL, NULL },
  144. };
  145. BOOL bSoundWildcard = FALSE;
  146. RESHANDLE hSound[kMaxPlaySounds];
  147. char *soundName[kMaxPlaySounds];
  148. int soundCount = 0;
  149. int soundCursor = 0;
  150. BYTE *background = NULL, *stage = NULL, *video = (BYTE *)0xA0000;
  151. #define kNumBuffers 34
  152. BYTE *buffers[kNumBuffers];
  153. int gFXDevice;
  154. int gMixBits;
  155. int gChannels;
  156. int gMixVoices;
  157. int gMixRate;
  158. int gFXVolume;
  159. fx_blaster_config gSBConfig;
  160. int fHolography = TRUE;
  161. double gPinnaAngle = kAngle45;
  162. double nSat = 0.8;
  163. static int lVoice, rVoice;
  164. static int lPhase, rPhase, lVel, rVel, lVol, rVol;
  165. int x, y, xold, yold;
  166. volatile short gMouseX, gMouseY;
  167. BOOL gMouseDragging = FALSE;
  168. int gMouseShown = FALSE;
  169. EHF prevErrorHandler;
  170. /*---------------------------------------------------------------------
  171. ** Function Prototypes
  172. ---------------------------------------------------------------------*/
  173. void ProcessArgument(char *s);
  174. /*---------------------------------------------------------------------
  175. Function: USRHOOKS_GetMem
  176. Allocates the requested amount of memory and returns a pointer to
  177. its location, or NULL if an error occurs. NOTE: pointer is assumed
  178. to be dword aligned.
  179. ---------------------------------------------------------------------*/
  180. int USRHOOKS_GetMem( void **ptr, unsigned long size )
  181. {
  182. void *memory;
  183. memory = Resource::Alloc( size );
  184. if ( memory == NULL )
  185. {
  186. return( USRHOOKS_Error );
  187. }
  188. *ptr = memory;
  189. return( USRHOOKS_Ok );
  190. }
  191. /*---------------------------------------------------------------------
  192. Function: USRHOOKS_FreeMem
  193. Deallocates the memory associated with the specified pointer.
  194. ---------------------------------------------------------------------*/
  195. int USRHOOKS_FreeMem( void *ptr )
  196. {
  197. if ( ptr == NULL )
  198. {
  199. return( USRHOOKS_Error );
  200. }
  201. Resource::Free( ptr );
  202. return( USRHOOKS_Ok );
  203. }
  204. void mouGetPos( void );
  205. #pragma aux mouseGetPos =\
  206. "mov ax,03h",\
  207. "int 33h",\
  208. "shr cx,1",\
  209. "mov [gMouseX],cx",\
  210. "mov [gMouseY],dx",\
  211. modify [eax ecx edx ebx]\
  212. char mouseGetButtons( void );
  213. #pragma aux mouseGetButtons =\
  214. "mov ax,03h",\
  215. "int 33h",\
  216. "xor eax,eax",\
  217. "mov al,bl",\
  218. modify [eax ebx ecx edx]
  219. inline void pixel( BYTE *buffer, int x, int y, char c )
  220. {
  221. *(char *)(buffer + x + gYLookup[y]) = c;
  222. }
  223. /*---------------------------------------------------------------------
  224. Function: plot
  225. XORs a 4x4 block onto the screen.
  226. ---------------------------------------------------------------------*/
  227. void plot( BYTE *buffer, int x, int y, char c )
  228. {
  229. int i;
  230. long d = (c << 24) | (c << 16) | (c << 8) | c;
  231. long *v = (long *)(buffer + x + y * 320);
  232. for( i = 0; i < 4; i++)
  233. {
  234. *v = d;
  235. v += 80;
  236. }
  237. }
  238. double Vol3d(double angle, double vol)
  239. {
  240. if (fHolography)
  241. return vol * (1.0 - (1.0 - cos(angle)) * nSat / 2.0);
  242. else
  243. return vol * (cos(angle) * 0.5 + 0.5);
  244. }
  245. int Dist3d(int x, int y)
  246. {
  247. return sqrt(x * x + y * y);
  248. }
  249. double CalcAngle( int x, int y )
  250. {
  251. if (y < 0)
  252. return atan((double)x / (double)y);
  253. if (y > 0)
  254. return kPi + atan((double)x / (double)y);
  255. if (x < 0)
  256. return kPi / 2;
  257. return -kPi / 2;
  258. }
  259. /*******************************************************************************
  260. Here is the equation for determining the frequency shift of a sound:
  261. Ú ¿
  262. ³ v + vL ³
  263. fL = fS ³ ÄÄÄÄÄÄ ³
  264. ³ v - vS ³
  265. À Ù
  266. Where:
  267. fL = frequency as heard by the listener
  268. fS = frequency of the source
  269. v = velocity of sound
  270. vL = velocity of listener
  271. vS = velocity of source
  272. Speed of sound = 343 m/s = 1126 ft/s.
  273. *******************************************************************************/
  274. #define kSoundVelocity ((double)kSampleRate / kTimerRate)
  275. int Freq( int velocity )
  276. {
  277. return kSampleRate * kSoundVelocity / (ClipLow(kSoundVelocity - velocity, 1));
  278. }
  279. // ear distance (in samples)
  280. #define kEarLX (int)(-kIntraAuralTime / 2 * kSampleRate)
  281. #define kEarRX (int)(+kIntraAuralTime / 2 * kSampleRate)
  282. void MoveSource( void )
  283. {
  284. int lDist, rDist;
  285. double angle, vol;
  286. xold = x;
  287. yold = y;
  288. x = gMouseX - 160;
  289. y = gMouseY - 100;
  290. angle = CalcAngle(x, y);
  291. if (fHolography)
  292. {
  293. lDist = Dist3d(x - kEarLX, y);
  294. lVel = Dist3d(xold - kEarLX, yold) - lDist;
  295. vol = kVolScale * 255 / ClipLow(lDist, 1);
  296. lVol = Vol3d(angle - gPinnaAngle, vol);
  297. lPhase = lDist;
  298. rDist = Dist3d(x - kEarRX, y);
  299. rVel = Dist3d(xold - kEarRX, yold) - rDist;
  300. vol = kVolScale * 255 / ClipLow(rDist, 1);
  301. rVol = Vol3d(angle + gPinnaAngle, vol);
  302. rPhase = rDist;
  303. }
  304. else
  305. {
  306. vol = kVolScale * 255 / ClipLow(Dist3d(x, y), 1);
  307. if (vol > 255) vol = 255;
  308. lVol = Vol3d(angle - kAngle90, vol);
  309. rVol = Vol3d(angle + kAngle90, vol);
  310. lVel = rVel = 0;
  311. lPhase = rPhase = 0;
  312. }
  313. }
  314. /*---------------------------------------------------------------------
  315. Function: PlaySound
  316. Starts playback of a sound.
  317. ---------------------------------------------------------------------*/
  318. void PlaySound( int num )
  319. {
  320. int nLength;
  321. BYTE *vl, *vr;
  322. int id;
  323. BYTE *soundData = (BYTE *)gResource.Lock(hSound[num]);
  324. dassert(soundData != NULL);
  325. if ( mouseGetButtons() )
  326. gMouseDragging = TRUE;
  327. nLength = gResource.Size(hSound[num]);
  328. vl = (BYTE *)Resource::Alloc(nLength + lPhase);
  329. dassert (vl != NULL);
  330. memset(vl, 128, lPhase);
  331. memcpy(&vl[lPhase], soundData, nLength);
  332. vr = (BYTE *)Resource::Alloc(nLength + rPhase);
  333. dassert(vr != NULL);
  334. memset(vr, 128, rPhase);
  335. memcpy(&vr[rPhase], soundData, nLength);
  336. gResource.Unlock(hSound[num]);
  337. _disable(); // make sure the samples start in the same interval
  338. int nPriority = 1; // ASS seems to choke if this is less than 1, e.g., 0
  339. if ( lVol > nPriority )
  340. nPriority = lVol;
  341. if ( rVol > nPriority )
  342. nPriority = rVol;
  343. for (id = 0; buffers[id] != 0; id++);
  344. lVoice = FX_PlayRaw( vl, nLength, Freq(lVel), 0, lVol, lVol, 0, nPriority, id);
  345. if ( lVoice > FX_Ok )
  346. buffers[id] = vl;
  347. else
  348. Resource::Free(vl);
  349. for (id = 0; buffers[id] != 0; id++);
  350. rVoice = FX_PlayRaw( vr, nLength, Freq(rVel), 0, rVol, 0, rVol, nPriority, id);
  351. if ( rVoice > FX_Ok )
  352. buffers[id] = vr;
  353. else
  354. Resource::Free(vr);
  355. _enable(); // allow them to start
  356. }
  357. void UpdateSound( void )
  358. {
  359. if (lVoice > FX_Ok)
  360. {
  361. FX_SetPan(lVoice, lVol, lVol, 0);
  362. FX_SetFrequency(lVoice, Freq(lVel));
  363. }
  364. if (rVoice > FX_Ok)
  365. {
  366. FX_SetPan(rVoice, rVol, 0, rVol);
  367. FX_SetFrequency(rVoice, Freq(rVel));
  368. }
  369. }
  370. /*---------------------------------------------------------------------
  371. Function: DrawMark
  372. Displays the position the voice will appear in relation to the
  373. listener.
  374. ---------------------------------------------------------------------*/
  375. void DrawMark( int fErase )
  376. {
  377. static int lastx = 0;
  378. static int lasty = 0;
  379. static char c = 16;
  380. int newx, newy;
  381. BYTE *v;
  382. int i, j;
  383. newx = gMouseX - 1;
  384. if (newx < 0) newx = 0;
  385. if (newx > 317) newx = 317;
  386. newy = gMouseY - 1;
  387. if (newy < 0) newy = 0;
  388. if (newy > 197) newy = 197;
  389. // clear the old one
  390. if (fErase)
  391. {
  392. v = video + lastx + lasty * 320;
  393. for( i = 0; i < 3; i++)
  394. {
  395. for (j = 0; j < 3; j++)
  396. *v++ ^= c;
  397. v += 320 - 3;
  398. }
  399. }
  400. if (++c == 32)
  401. c = 16;
  402. // draw the new one
  403. v = video + newx + newy * 320;
  404. for( i = 0; i < 3; i++)
  405. {
  406. for (j = 0; j < 3; j++)
  407. *v++ ^= c;
  408. v += 320 - 3;
  409. }
  410. lastx = newx;
  411. lasty = newy;
  412. }
  413. /*---------------------------------------------------------------------
  414. Function: DrawDemoScreen
  415. Sets up our information screen for the user. Displays last error,
  416. what sounds are playing, and the volume levels.
  417. ---------------------------------------------------------------------*/
  418. void DrawDemoScreen( void )
  419. {
  420. int x;
  421. double a;
  422. double lSat, rSat, nCos, nSin;
  423. // make sure the mouse doesn't get drawn while we're setting up
  424. gMouseShown = FALSE;
  425. // clear screen
  426. memcpy(stage, background, 320 * 200);
  427. for ( a = 0.0; a < kAngle360; a += kAngle5 )
  428. {
  429. if (fHolography)
  430. {
  431. lSat = Vol3d(a - gPinnaAngle, 25);
  432. rSat = Vol3d(a + gPinnaAngle, 25);
  433. }
  434. else
  435. {
  436. lSat = Vol3d(a - kAngle90, 25);
  437. rSat = Vol3d(a + kAngle90, 25);
  438. }
  439. nSin = sin(a);
  440. nCos = cos(a);
  441. pixel(stage, 160 - nSin * lSat, 100 - nCos * lSat, 8);
  442. pixel(stage, 160 - nSin * rSat, 100 - nCos * rSat, 8);
  443. pixel(stage, 160 - nSin * (lSat + rSat) * kAspectAdjustX, 100 - nCos * (lSat + rSat), 14);
  444. }
  445. // draw the holography toggle
  446. plot( stage, 135, 6, fHolography ? 14 : 8 );
  447. // Draw volume display
  448. for( x = 0; x < 10; x++ )
  449. {
  450. plot( stage, 240 + x * 7, 6, gFXVolume > x ? 10 : 8 );
  451. }
  452. // draw the stage
  453. memcpy(video, stage, 320 * 200);
  454. // draw the initial cursor
  455. DrawMark(0);
  456. // okay, we're ready for the mouse
  457. gMouseShown = TRUE;
  458. }
  459. /*---------------------------------------------------------------------
  460. Function: TimerFunc
  461. ---------------------------------------------------------------------*/
  462. void TimerFunc( TASK * /* task */ )
  463. {
  464. mouseGetPos();
  465. if ( gMouseDragging && !mouseGetButtons() )
  466. gMouseDragging = FALSE;
  467. if (gMouseShown)
  468. {
  469. DrawMark(1);
  470. MoveSource();
  471. if ( gMouseDragging )
  472. {
  473. UpdateSound();
  474. }
  475. }
  476. }
  477. /*---------------------------------------------------------------------
  478. Function: SoundCallback
  479. When a sound fx stops playing, this function is called.
  480. ---------------------------------------------------------------------*/
  481. void SoundCallback(unsigned long id)
  482. {
  483. if (buffers[id] != NULL)
  484. {
  485. Resource::Free(buffers[id]);
  486. buffers[id] = NULL;
  487. }
  488. else
  489. dprintf("SoundCallback() called for non-playing sample\n");
  490. }
  491. void ShowSoundName( int n )
  492. {
  493. char zbuffer[60];
  494. sprintf(zbuffer,"%-12s : %i of %i", soundName[n], soundCursor + 1, soundCount );
  495. Video.SetColor(0);
  496. gfxFillBox(0, 192, 320, 200);
  497. gfxDrawText(0, 192, 15, zbuffer);
  498. }
  499. /*---------------------------------------------------------------------
  500. Function: CheckKeys
  501. Checks which keys are hit and responds appropriately.
  502. ---------------------------------------------------------------------*/
  503. BYTE CheckKeys( void )
  504. {
  505. BYTE key;
  506. key = keyGet();
  507. switch( key )
  508. {
  509. case KEY_SPACE:
  510. PlaySound(soundCursor);
  511. break;
  512. case KEY_1:
  513. case KEY_2:
  514. case KEY_3:
  515. case KEY_4:
  516. case KEY_5:
  517. case KEY_6:
  518. case KEY_7:
  519. case KEY_8:
  520. case KEY_9:
  521. case KEY_0:
  522. {
  523. int nSound = soundCursor + key - KEY_1;
  524. if (nSound < soundCount)
  525. PlaySound(nSound);
  526. break;
  527. }
  528. case KEY_UP:
  529. case KEY_PADUP:
  530. soundCursor = ClipLow(soundCursor - 1, 0);
  531. ShowSoundName(soundCursor);
  532. break;
  533. case KEY_DOWN:
  534. case KEY_PADDOWN:
  535. soundCursor = ClipHigh(soundCursor + 1, soundCount - 1);
  536. ShowSoundName(soundCursor);
  537. break;
  538. case KEY_PAGEUP:
  539. case KEY_PADPAGEUP:
  540. soundCursor = ClipLow(soundCursor - 10, 0);
  541. ShowSoundName(soundCursor);
  542. break;
  543. case KEY_PAGEDN:
  544. case KEY_PADPAGEDN:
  545. soundCursor = ClipHigh(soundCursor + 10, soundCount - 1);
  546. ShowSoundName(soundCursor);
  547. break;
  548. case KEY_MINUS:
  549. if (gFXVolume > 0)
  550. {
  551. gFXVolume--;
  552. FX_SetVolume(gFXVolume * 255 / 10);
  553. plot( video, 240 + gFXVolume * 7, 6, 8 );
  554. }
  555. break;
  556. case KEY_EQUALS:
  557. if (gFXVolume < 10)
  558. {
  559. plot( video, 240 + gFXVolume * 7, 6, 10 );
  560. gFXVolume++;
  561. FX_SetVolume(gFXVolume * 255 / 10);
  562. }
  563. break;
  564. case KEY_LBRACE:
  565. if ( gPinnaAngle > 0 )
  566. {
  567. gPinnaAngle -= kAngle5;
  568. DrawDemoScreen();
  569. }
  570. break;
  571. case KEY_RBRACE:
  572. if ( gPinnaAngle < kAngle90 )
  573. {
  574. gPinnaAngle += kAngle5;
  575. DrawDemoScreen();
  576. }
  577. break;
  578. case KEY_COMMA:
  579. if ( nSat > 0 )
  580. {
  581. nSat -= 0.05;
  582. DrawDemoScreen();
  583. }
  584. break;
  585. case KEY_PERIOD:
  586. if ( nSat < 1.0 )
  587. {
  588. nSat += 0.05;
  589. DrawDemoScreen();
  590. }
  591. break;
  592. case KEY_K:
  593. FX_StopAllSounds();
  594. break;
  595. case KEY_S:
  596. fHolography = !fHolography;
  597. DrawDemoScreen();
  598. break;
  599. }
  600. return key;
  601. }
  602. /*---------------------------------------------------------------------
  603. Function: DoDemo
  604. Main loop for our demo. Sets up a timer so that marker moves at
  605. same rate on any computer.
  606. ---------------------------------------------------------------------*/
  607. void DoDemo( void )
  608. {
  609. TASK *taskPtr = NULL;
  610. int Timer;
  611. int i;
  612. int nOldMode;
  613. int width = 320, height = 200;
  614. PALETTE palette;
  615. background = (BYTE *)Resource::Alloc(320 * 200);
  616. dassert(background != NULL);
  617. if (PCX_OKAY != ReadPCX("screen.pcx", palette, &width, &height, &background))
  618. ThrowError("Error reading screen.pcx\n", ES_ERROR);
  619. stage = (BYTE *)Resource::Alloc(320 * 200);
  620. dassert(stage != NULL);
  621. nOldMode = getvmode();
  622. if (!gFindMode(320, 200, 256, CHAIN256))
  623. ThrowError("Video driver not linked\n", ES_ERROR);
  624. Video.SetMode();
  625. gSetDACRange(0, 256, palette);
  626. taskPtr = TS_ScheduleTask( TimerFunc, kTimerRate, 1, &Timer );
  627. TS_Dispatch();
  628. Timer = 0;
  629. // set the current volume of the sound fx
  630. FX_SetVolume( gFXVolume * 255 / 10 );
  631. // Set up my keyboard handler.
  632. keyInstall();
  633. for (i = 0; i < kNumBuffers; i++)
  634. buffers[i] = NULL;
  635. // Set up our fx callback so we can display the sounds that are playing
  636. FX_SetCallBack(SoundCallback);
  637. soundCursor = 0;
  638. DrawDemoScreen();
  639. ShowSoundName(soundCursor);
  640. while ( CheckKeys() != KEY_ESC );
  641. // Stop the sound fx engine.
  642. FX_StopAllSounds();
  643. // Restore the system keyboard handler
  644. keyRemove();
  645. setvmode(nOldMode);
  646. // Terminate my timer. Failure to do so could be fatal!!!
  647. TS_Terminate(taskPtr);
  648. TS_Shutdown();
  649. Resource::Free(stage);
  650. }
  651. int SelectMenu( char *title, MENU_LINE list[], int nDefault )
  652. {
  653. int cols = 0, rows = 0;
  654. int pos = 0;
  655. int nListItems;
  656. // cols = strlen(title);
  657. (void)title;
  658. // find widest menu item text
  659. for ( int i = 0; list[i].name != NULL; i++)
  660. {
  661. int n = strlen(list[i].name);
  662. if (n > cols)
  663. cols = n;
  664. }
  665. nListItems = i;
  666. cols += 5;
  667. rows = nListItems + 3;
  668. char *saveUnder = (char *)Resource::Alloc(rows * cols * 2);
  669. dassert(saveUnder != NULL);
  670. int left = (tioScreenCols - cols) / 2;
  671. int top = (tioScreenRows - rows) / 2;
  672. tioSaveWindow(saveUnder, top, left, rows, cols);
  673. tioFrame(top, left, rows - 1, cols - 1, kFrameSingle, kColorBlue * 16 | kColorCyan);
  674. tioFill(top + 1, left + 1, rows - 3, cols - 3, ' ', kColorBlue * 16 | kColorYellow);
  675. tioFillShadow(top + rows - 1, left + 1, 1, cols - 1);
  676. tioFillShadow(top + 1, left + cols - 1, rows - 2, 1);
  677. for( i = 0; i < nListItems; i++ )
  678. {
  679. tioLeftString(top + i + 1, left + 2, list[i].name, kColorBlue * 16 | kColorYellow);
  680. if ( list[i].id == nDefault )
  681. pos = i;
  682. }
  683. int ch = 0;
  684. while( ch != 0x0D && ch != 0x20 )
  685. {
  686. // highlight current line and display hint
  687. tioFillAttr(top + pos + 1, left + 1, 1, cols - 3, kColorLightGray * 16 | kColorBlack);
  688. tioCenterString(tioScreenRows - 1, 0, tioScreenCols - 1,
  689. list[pos].hint, kColorRed * 16 | kColorYellow);
  690. ch = getch();
  691. if ( ch == 0 )
  692. {
  693. ch = 0x100 | getch();
  694. }
  695. // Up arrow
  696. if ( ch == 0x148 )
  697. {
  698. tioFillAttr(top + pos + 1, left + 1, 1, cols - 3, kColorBlue * 16 | kColorYellow);
  699. pos = DecRotate(pos, nListItems);
  700. }
  701. // Down arrow
  702. if ( ch == 0x150 )
  703. {
  704. tioFillAttr(top + pos + 1, left + 1, 1, cols - 3, kColorBlue * 16 | kColorYellow);
  705. pos = IncRotate(pos, nListItems);
  706. }
  707. // Esc
  708. if ( ch == 0x1B )
  709. {
  710. tioRestoreWindow(saveUnder, top, left, rows, cols);
  711. Resource::Free(saveUnder);
  712. return -1;
  713. }
  714. }
  715. tioRestoreWindow(saveUnder, top, left, rows, cols);
  716. Resource::Free(saveUnder);
  717. return list[pos].id;
  718. }
  719. /*---------------------------------------------------------------------
  720. Function: ReadSounds
  721. Sets up sound resources. Returns number of sounds available.
  722. ---------------------------------------------------------------------*/
  723. int ReadSounds( void )
  724. {
  725. int i;
  726. for ( i = 0; i < kMaxPlaySounds; i++)
  727. hSound[i] = NULL; // safer than a memset(soundHandle,0,sizeof(soundHandle)); ?
  728. gResource.Init(NULL, "*.RAW");
  729. if (!bSoundWildcard)
  730. ProcessArgument("*.RAW"); // default to local .RAW files
  731. for ( i = 0; i < soundCount; i++)
  732. {
  733. char zNameBuffer[_MAX_PATH2];
  734. char *name, *ext;
  735. _splitpath2(soundName[i], zNameBuffer, NULL, NULL, &name, &ext);
  736. hSound[i] = gResource.Lookup(name, ext);
  737. }
  738. return soundCount;
  739. }
  740. void GetSoundSettings( void )
  741. {
  742. FX_GetBlasterSettings(&gSBConfig);
  743. // Read current setup
  744. gFXDevice = config.GetKeyInt("Sound Setup", "FXDevice", SoundBlaster);
  745. gMixBits = config.GetKeyInt("Sound Setup", "Bits", 16);
  746. gChannels = config.GetKeyInt("Sound Setup", "Channels", 2);
  747. gMixVoices = config.GetKeyInt("Sound Setup", "Voices", 16);
  748. gMixRate = config.GetKeyInt("Sound Setup", "MixRate", 22050);
  749. gFXVolume = config.GetKeyInt("Sound Setup", "FXVolume", 8);
  750. gSBConfig.Address = config.GetKeyInt("Sound Setup", "BlasterAddress", gSBConfig.Address);
  751. gSBConfig.Type = config.GetKeyInt("Sound Setup", "BlasterType", gSBConfig.Type);
  752. gSBConfig.Interrupt = config.GetKeyInt("Sound Setup", "BlasterInterrupt", gSBConfig.Interrupt);
  753. gSBConfig.Dma8 = config.GetKeyInt("Sound Setup", "BlasterDma8", gSBConfig.Dma8);
  754. gSBConfig.Dma16 = config.GetKeyInt("Sound Setup", "BlasterDma16", gSBConfig.Dma16);
  755. gSBConfig.Emu = config.GetKeyInt("Sound Setup", "BlasterEmu", gSBConfig.Emu);
  756. }
  757. /*---------------------------------------------------------------------
  758. Function: GetSoundFXCard
  759. Asks the user which card to use for sound fx and initializes the
  760. sound fx routines.
  761. ---------------------------------------------------------------------*/
  762. BOOL SetupSoundCard( void )
  763. {
  764. // Draw our config screen
  765. tioCenterString(0, 0, tioScreenCols - 1, "Q Studios Sonic Holography Setup", kColorBlue * 16 | kColorYellow);
  766. tioFill( 1, 0, tioScreenRows - 2, tioScreenCols, '°', kColorBlack * 16 | kColorDarkGray);
  767. // Get Sound fx card
  768. gFXDevice = SelectMenu("Select a device for digital sound",
  769. SoundCardMenu, gFXDevice);
  770. if ( gFXDevice < 0 )
  771. return FALSE;
  772. switch ( gFXDevice )
  773. {
  774. case SoundBlaster:
  775. case ProAudioSpectrum:
  776. case SoundMan16:
  777. case Awe32:
  778. case WaveBlaster:
  779. case SoundScape:
  780. gMixBits = SelectMenu("Select the number of bits to use for mixing",
  781. BitMenu, gMixBits);
  782. if ( gMixBits < 0 )
  783. return FALSE;
  784. gChannels = SelectMenu("Select the channels mode",
  785. ChannelMenu, gChannels);
  786. if ( gChannels < 0 )
  787. return FALSE;
  788. gMixVoices = SelectMenu("Select the number of voices to use for mixing",
  789. VoiceMenu, gMixVoices);
  790. break;
  791. default:
  792. gMixBits = 8;
  793. gChannels = 1;
  794. gMixVoices = 2;
  795. break;
  796. }
  797. if ( gFXDevice == SoundBlaster || gFXDevice == Awe32 )
  798. {
  799. gSBConfig.Address = SelectMenu("Base I/O address",
  800. BlasterAddressMenu, gSBConfig.Address);
  801. gSBConfig.Type = SelectMenu("Type of Sound Blaster card",
  802. BlasterTypeMenu, gSBConfig.Type);
  803. gSBConfig.Interrupt = SelectMenu("Interrupt",
  804. BlasterIRQMenu, gSBConfig.Interrupt);
  805. gSBConfig.Dma8 = SelectMenu("8 bit DMA channel",
  806. BlasterDma8Menu, gSBConfig.Dma8);
  807. gSBConfig.Dma16 = SelectMenu("16 bit DMA channel",
  808. BlasterDma16Menu, gSBConfig.Dma16);
  809. }
  810. gMixRate = SelectMenu("Select the rate to use for mixing",
  811. MixRateMenu, gMixRate);
  812. if ( gMixRate < 0 )
  813. return FALSE;
  814. config.PutKeyInt("Sound Setup", "FXDevice", gFXDevice);
  815. config.PutKeyInt("Sound Setup", "Bits", gMixBits);
  816. config.PutKeyInt("Sound Setup", "Channels", gChannels);
  817. config.PutKeyInt("Sound Setup", "Voices", gMixVoices);
  818. config.PutKeyInt("Sound Setup", "MixRate", gMixRate);
  819. config.PutKeyHex("Sound Setup", "BlasterAddress", gSBConfig.Address);
  820. config.PutKeyInt("Sound Setup", "BlasterType", gSBConfig.Type);
  821. config.PutKeyInt("Sound Setup", "BlasterInterrupt", gSBConfig.Interrupt);
  822. config.PutKeyInt("Sound Setup", "BlasterDma8", gSBConfig.Dma8);
  823. config.PutKeyInt("Sound Setup", "BlasterDma16", gSBConfig.Dma16);
  824. config.PutKeyInt("Sound Setup", "BlasterEmu", gSBConfig.Emu);
  825. return TRUE;
  826. }
  827. void InitSoundDevice(void)
  828. {
  829. int status;
  830. // Do special Sound Blaster, AWE32 stuff
  831. if ( gFXDevice == SoundBlaster || gFXDevice == Awe32 )
  832. {
  833. int MaxVoices;
  834. int MaxBits;
  835. int MaxChannels;
  836. status = FX_SetupSoundBlaster(gSBConfig, &MaxVoices, &MaxBits, &MaxChannels);
  837. }
  838. else
  839. {
  840. fx_device device;
  841. status = FX_SetupCard( gFXDevice, &device );
  842. }
  843. if ( status != FX_Ok )
  844. ThrowError(FX_ErrorString(status), ES_ERROR);
  845. status = FX_Init( gFXDevice, gMixVoices, gChannels, gMixBits, gMixRate );
  846. if ( status != FX_Ok )
  847. ThrowError(FX_ErrorString(status), ES_ERROR);
  848. FX_SetVolume(gFXVolume);
  849. }
  850. /*******************************************************************************
  851. FUNCTION: ShowUsage()
  852. DESCRIPTION: Display command-line parameter usage, then exit
  853. *******************************************************************************/
  854. void ShowUsage(void)
  855. {
  856. tioPrint("Syntax: SHPLAY [options] wildcard[.RAW]");
  857. tioPrint(" /setup Reconfigure sound card");
  858. tioPrint(" /? This help");
  859. tioPrint("");
  860. exit(0);
  861. }
  862. int compare(const void *p1, const void *p2 )
  863. {
  864. const char **pp1 = (const char **)p1;
  865. const char **pp2 = (const char **)p2;
  866. return strcmp(*pp1, *pp2);
  867. }
  868. /***********************************************************************
  869. * Convert wildcard parameter to sound list
  870. **********************************************************************/
  871. void ProcessArgument(char *s)
  872. {
  873. char filespec[_MAX_PATH];
  874. char buffer[_MAX_PATH2];
  875. char path[_MAX_PATH];
  876. strcpy(filespec, s);
  877. AddExtension(filespec, ".RAW");
  878. char *drive, *dir;
  879. // separate the path from the filespec
  880. _splitpath2(s, buffer, &drive, &dir, NULL, NULL);
  881. _makepath(path, drive, dir, NULL, NULL);
  882. struct find_t fileinfo;
  883. unsigned r = _dos_findfirst(s, _A_NORMAL, &fileinfo);
  884. if (r != 0)
  885. printf("%s not found\n", s);
  886. while ( r == 0 && soundCount < kMaxPlaySounds )
  887. {
  888. strcpy(filespec, path);
  889. strcat(filespec, fileinfo.name);
  890. soundName[soundCount] = strdup(filespec);
  891. dassert( soundName[soundCount] != NULL ); // low memory condition
  892. soundCount++;
  893. r = _dos_findnext( &fileinfo );
  894. }
  895. _dos_findclose(&fileinfo);
  896. qsort(soundName, soundCount, sizeof(soundName[0]), compare);
  897. }
  898. /***********************************************************************
  899. * Process command line arguments
  900. **********************************************************************/
  901. void ParseOptions( void )
  902. {
  903. enum {
  904. kSwitchHelp,
  905. kSwitchSetup,
  906. kSwitchMap,
  907. kSwitchOmit,
  908. };
  909. static SWITCH switches[] = {
  910. { "?", kSwitchHelp, FALSE },
  911. { "SETUP", kSwitchSetup, FALSE },
  912. { NULL, 0, FALSE },
  913. };
  914. char buffer[256];
  915. int r;
  916. while ( (r = GetOptions(switches)) != GO_EOF )
  917. {
  918. switch (r)
  919. {
  920. case GO_INVALID:
  921. sprintf(buffer, "Invalid argument: %s", OptArgument);
  922. tioPrint(buffer);
  923. exit(1);
  924. case GO_FULL:
  925. ProcessArgument(OptArgument);
  926. bSoundWildcard = TRUE;
  927. break;
  928. case kSwitchSetup:
  929. if ( !SetupSoundCard() )
  930. exit(0);
  931. break;
  932. case kSwitchHelp:
  933. ShowUsage();
  934. break;
  935. }
  936. }
  937. }
  938. /*---------------------------------------------------------------------
  939. Function: GameErrorHandler
  940. Application error handler
  941. ---------------------------------------------------------------------*/
  942. ErrorResult GameErrorHandler( const Error& error )
  943. {
  944. setvmode(3);
  945. FX_Shutdown();
  946. tioTerm();
  947. // chain to the default error handler
  948. return prevErrorHandler(error);
  949. };
  950. /*---------------------------------------------------------------------
  951. Function: main
  952. Sets up sound cards, calls the demo, and then cleans up.
  953. ---------------------------------------------------------------------*/
  954. void main( void )
  955. {
  956. tioInit(0);
  957. GetSoundSettings();
  958. Resource::heap = new QHeap(dpmiDetermineMaxRealAlloc());
  959. if ( !config.KeyExists("Sound Setup", "FXDevice") )
  960. {
  961. if ( !SetupSoundCard() )
  962. {
  963. tioClearWindow();
  964. exit(0);
  965. }
  966. }
  967. ParseOptions();
  968. if ( !ReadSounds() )
  969. ThrowError("No sounds available",ES_ERROR);
  970. InitSoundDevice();
  971. // install our error handler
  972. prevErrorHandler = errSetHandler(GameErrorHandler);
  973. DoDemo();
  974. FX_Shutdown();
  975. tioClearWindow();
  976. tioPrint("Sonic Holography Player Version 2.0 Copyright (c) 1995 Q Studios Corporation");
  977. tioPrint("Written by Peter M. Freese");
  978. tioPrint("");
  979. tioPrint("Pinna angle: %3.0f degrees", gPinnaAngle * 360.0 / kAngle360);
  980. tioPrint("Saturation: %0.2f", nSat);
  981. tioTerm();
  982. config.PutKeyInt("Setup", "FXVolume", gFXVolume);
  983. config.Save();
  984. }