AUDIO.BAK2 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246
  1. /*
  2. ** Command & Conquer Red Alert(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /****************************************************************************
  19. *
  20. * C O N F I D E N T I A L -- W E S T W O O D S T U D I O S
  21. *
  22. *----------------------------------------------------------------------------
  23. *
  24. * PROJECT
  25. * VQA player library. (32-Bit protected mode)
  26. *
  27. * FILE
  28. * audio.c
  29. *
  30. * DESCRIPTION
  31. * Audio playback and timing.
  32. *
  33. * PROGRAMMER
  34. * Bill Randolph
  35. * Denzil E. Long, Jr.
  36. *
  37. * DATE
  38. * June 22, 1995
  39. *
  40. *----------------------------------------------------------------------------
  41. *
  42. * PUBLIC
  43. * VQA_StartTimerInt - Initialize system timer interrupt.
  44. * VQA_StopTimerInt - Remove system timer interrupt.
  45. * VQA_SetTimer - Resets current time to given tick value.
  46. * VQA_GetTime - Return current time.
  47. * VQA_TimerMethod - Get timer method being used.
  48. * VQA_OpenAudio - Open sound system.
  49. * VQA_CloseAudio - Close sound system
  50. * VQA_StartAudio - Starts audio playback
  51. * VQA_StopAudio - Stop audio playback.
  52. * CopyAudio - Copy data from Audio Temp buf into Audio play buf.
  53. *
  54. * PRIVATE
  55. * TimerCallback - Timer callback routine; called by HMI
  56. * AutoDetect - Auto detect the sound card.
  57. * AudioCallback - Sound system callback.
  58. *
  59. ****************************************************************************/
  60. #include <stdio.h>
  61. #include <stdlib.h>
  62. #include <malloc.h>
  63. #include <mem.h>
  64. #include <sys\timeb.h>
  65. #include "vqaplayp.h"
  66. #include <vqm32\all.h>
  67. #ifdef __WATCOMC__
  68. #pragma pack(4);
  69. #endif
  70. #if(VQAAUDIO_ON)
  71. #include "sos.h"
  72. #endif
  73. /*---------------------------------------------------------------------------
  74. * AUDIO DEFINITIONS
  75. *-------------------------------------------------------------------------*/
  76. #define HMI_SAMPLE 0x1000
  77. #define HMI_DEFAULT_RATE 22050
  78. #define HMI_UNINIT 0 /* Unitialize state. */
  79. #define HMI_VQAINIT 1 /* VQA initialized */
  80. #define HMI_APPINIT 2 /* Application initialized */
  81. #if(VQAAUDIO_ON)
  82. /*---------------------------------------------------------------------------
  83. * PROTOTYPES
  84. *-------------------------------------------------------------------------*/
  85. long AutoDetect(long bitsize, long channels);
  86. void far TimerCallback(void);
  87. void far cdecl AudioCallback(WORD wDriverHandle, WORD wAction,
  88. WORD wSampleID);
  89. /*---------------------------------------------------------------------------
  90. * GLOBAL DATA
  91. *-------------------------------------------------------------------------*/
  92. /* HMI Start Sample structure:
  93. *
  94. * lpSamplePtr - Pointer to audio buffer.
  95. * dwSampleSize - Size of buffer.
  96. * wLoopCount - Number of time to loop (-1 = infinate)
  97. * wChannel - Channel to play sample on.
  98. * wVolume - Volume level to play at.
  99. * wSampleID - ID for sample
  100. * lpCallback - Callback function pointer.
  101. * wSamplePort - Port to use for non-DMA
  102. * wSampleFlags - Flags
  103. * dwSampleByteLength - Total length including loops
  104. * dwSampleLoopPoint -
  105. * dwSampleLoopLength -
  106. * dwSamplePitchAdd - Pitch shifting component.
  107. * wSamplePitchFraction - Pitch shifting component.
  108. * wSamplePanLocation - For panning
  109. * wSamplePanSpeed - For panning
  110. * wSamplePanDirection - For panning
  111. * wSamplePanStart - For panning.
  112. * wSamplePanEnd - For panning.
  113. * wSampleDelayBytes - Delay part.
  114. * wSampleDelayRepeat - Delay part.
  115. * dwSampleADPCMPredicted - For compression
  116. * wSampleADPCMIndex - For compression
  117. * wSampleRootNoteMIDI - Root note for pitch shift
  118. * dwFiller[3] - Filler for future upgrades.
  119. */
  120. _SOS_START_SAMPLE sSOSSampleData = {
  121. _NULL,
  122. 0L,
  123. 0,
  124. _CENTER_CHANNEL,
  125. 0x7FFF,
  126. HMI_SAMPLE,
  127. AudioCallback,
  128. 0,
  129. 0,
  130. 0L,
  131. 0L,0L,
  132. 0L,0,
  133. 0,0,0,0,0,
  134. 0,0,
  135. 0L,0,
  136. 0,
  137. 0L,0L,0L
  138. };
  139. /* HMI Driver structure: Driver initialization structure.
  140. *
  141. * wBufferSize - Size of play buffer.
  142. * lpBuffer - Pointer to buffer.
  143. * wAllocateBuffer - Allocate buffer flag.
  144. * wSampleRate - Playback rate.
  145. * wParam -
  146. * dwParam -
  147. * lpFillHandler -
  148. * lpDriverMemory -
  149. * lpTimerMemory -
  150. * wTimerID -
  151. */
  152. _SOS_INIT_DRIVER sSOSInitDriver = {
  153. 0,
  154. _NULL,
  155. _TRUE,
  156. HMI_DEFAULT_RATE,
  157. 19,
  158. 0L,
  159. _NULL,
  160. _NULL,
  161. _NULL,
  162. _SOS_NORMAL_TIMER
  163. };
  164. static _SOS_CAPABILITIES DigiCaps;
  165. static _SOS_HARDWARE DigiHardware;
  166. static WORD DigiHandle = -1;
  167. static WORD DigiTimer = 0;
  168. static WORD SampleHandle;
  169. static long DigiInitFlag = HMI_UNINIT;
  170. static long PlayingFlag = 0;
  171. static VQAHandleP *VQAP;
  172. static unsigned long CurBlock = 0;
  173. static unsigned long NextBlock;
  174. static WORD VQATimer = 0;
  175. static long TimerMethod;
  176. static long TimerInitFlag = HMI_UNINIT;
  177. static long HMITimerFlag = HMI_UNINIT;
  178. static long VQATickCount = 0;
  179. #endif /* VQAAUDIO_ON */
  180. static long TickOffset = 0;
  181. char *HMIDevName = "<none>";
  182. #if(VQAAUDIO_ON)
  183. /****************************************************************************
  184. *
  185. * NAME
  186. * VQA_StartTimerInt - Initialize system timer interrupt.
  187. *
  188. * SYNOPSIS
  189. * Error = VQA_StartTimerInt(Init)
  190. *
  191. * long VQA_StartTimerInt(long);
  192. *
  193. * FUNCTION
  194. * Initialize the HMI timer system if it hasn't been already then add
  195. * our own timer event.
  196. *
  197. * INPUTS
  198. * Init - Initialize HMI timer system flag. (TRUE = Initialize)
  199. *
  200. * RESULT
  201. * Error - 0 if successful, -1 if error.
  202. *
  203. ****************************************************************************/
  204. long VQA_StartTimerInt(long init)
  205. {
  206. /* Does the caller want me to initialize the timer system? */
  207. if (init == 0) {
  208. /* If the timer system is uninitialized then it is the players
  209. * responsibility to do it.
  210. */
  211. if (TimerInitFlag == HMI_UNINIT) {
  212. sosTIMERInitSystem(_TIMER_DOS_RATE, _SOS_DEBUG_NORMAL);
  213. TimerInitFlag = HMI_VQAINIT;
  214. }
  215. } else {
  216. TimerInitFlag = HMI_APPINIT;
  217. }
  218. /* Register the VQA_TickCount timer event. */
  219. if (HMITimerFlag == HMI_UNINIT) {
  220. if (sosTIMERRegisterEvent(VQA_TIMETICKS,TimerCallback,&VQATimer) == 0) {
  221. HMITimerFlag = HMI_VQAINIT;
  222. } else {
  223. return (-1);
  224. }
  225. }
  226. return (0);
  227. }
  228. /****************************************************************************
  229. *
  230. * NAME
  231. * VQA_StopTimerInt - Remove system timer interrupt.
  232. *
  233. * SYNOPSIS
  234. * VQA_StopTimerInt()
  235. *
  236. * void VQA_StopTimerInt(void);
  237. *
  238. * FUNCTION
  239. * Remove our timer event from the HMI timer system. Uninitialize the
  240. * HMI timer system if we initialized it.
  241. *
  242. * INPUTS
  243. * NONE
  244. *
  245. * RESULT
  246. * NONE
  247. *
  248. ****************************************************************************/
  249. void VQA_StopTimerInt(void)
  250. {
  251. /* Remove the timer event */
  252. if (HMITimerFlag == HMI_VQAINIT) {
  253. sosTIMERRemoveEvent(VQATimer);
  254. }
  255. HMITimerFlag = HMI_UNINIT;
  256. if (TimerInitFlag == HMI_VQAINIT) {
  257. sosTIMERUnInitSystem(0);
  258. }
  259. TimerInitFlag = HMI_UNINIT;
  260. }
  261. /****************************************************************************
  262. *
  263. * NAME
  264. * TimerCallback - VQA timer event. (Called by HMI)
  265. *
  266. * SYNOPSIS
  267. * TimerCallback()
  268. *
  269. * void TimerCallback(void);
  270. *
  271. * FUNCTION
  272. * Our custom timer event. This is the timer event that we register with
  273. * HMI for system timing.
  274. *
  275. * INPUTS
  276. * NONE
  277. *
  278. * RESULT
  279. * NONE
  280. *
  281. ****************************************************************************/
  282. void far TimerCallback(void)
  283. {
  284. VQATickCount++;
  285. }
  286. #endif /* VQAAUDIO_ON */
  287. /****************************************************************************
  288. *
  289. * NAME
  290. * VQA_SetTimer - Resets current time to given tick value.
  291. *
  292. * SYNOPSIS
  293. * VQA_SetTimer(Time, Method)
  294. *
  295. * void VQA_SetTimer(long, long);
  296. *
  297. * FUNCTION
  298. * Sets 'TickOffset' to a value that will make the current time look like
  299. * the time passed in. This function allows the player to be "paused",
  300. * by recording the time of the pause, and then setting the timer to
  301. * that time. The timer method used by the player is also set. The method
  302. * selected is not neccesarily the method that will be used because some
  303. * timer methods work with only certain playback conditions. (EX: The
  304. * audio DMA timer method cannot be used if there is not any audio
  305. * playing.)
  306. *
  307. * INPUTS
  308. * Time - Value to set current time to.
  309. * Method - Timer method to use.
  310. *
  311. * RESULT
  312. * NONE
  313. *
  314. ****************************************************************************/
  315. void VQA_SetTimer(long time, long method)
  316. {
  317. unsigned long curtime;
  318. #if(VQAAUDIO_ON)
  319. /* If the client does not have a preferencee then pick a method
  320. * based on the state of the player.
  321. */
  322. if (method == VQA_TMETHOD_DEFAULT) {
  323. /* If we are playing audio, use the audio DMA position. */
  324. if (PlayingFlag) {
  325. method = VQA_TMETHOD_AUDIO;
  326. }
  327. /* Otherwise use the HMI timer if it is initialized. */
  328. else if (HMITimerFlag) {
  329. method = VQA_TMETHOD_INT;
  330. }
  331. /* If all else fails resort the the "jerky" DOS time. */
  332. else {
  333. method = VQA_TMETHOD_DOS;
  334. }
  335. } else {
  336. /* We cannot use the DMA position if there isn't any audio playing. */
  337. if (!PlayingFlag && (method == VQA_TMETHOD_AUDIO)) {
  338. method = VQA_TMETHOD_INT;
  339. }
  340. /* We cannot use the timer if it has not been initialized. */
  341. if (!HMITimerFlag && (method == VQA_TMETHOD_INT)) {
  342. method = VQA_TMETHOD_DOS;
  343. }
  344. }
  345. TimerMethod = method;
  346. #endif
  347. TickOffset = 0L;
  348. curtime = VQA_GetTime();
  349. TickOffset = (time - curtime);
  350. }
  351. /****************************************************************************
  352. *
  353. * NAME
  354. * VQA_GetTime - Return current time.
  355. *
  356. * SYNOPSIS
  357. * Time = VQA_GetTime()
  358. *
  359. * unsigned long VQA_GetTime(void);
  360. *
  361. * FUNCTION
  362. * This routine returns timer ticks computed one of 3 ways:
  363. *
  364. * 1) If audio is playing, the timer is based on the DMA buffer position:
  365. * Compute the number of audio samples that have actually been played.
  366. * The following internal HMI variables are used:
  367. *
  368. * _lpSOSDMAFillCount[drv_handle]: current DMA buffer position
  369. * _lpSOSSampleList[drv_handle][samp_handle]:
  370. * sampleTotalBytes: total bytes sent by HMI to the DMA buffer
  371. * sampleLastFill: HMI's last fill position in DMA buffer
  372. *
  373. * So, the number of samples actually played is:
  374. *
  375. * sampleTotalBytes - <DMA_diff>
  376. * where <DMA_diff> is how far ahead sampleLastFill is in front of
  377. * _lpSOSDMAFillCount: (sampleLastFill - _lpSOSDMAFillCount)
  378. *
  379. * These values are indices into a circular DMA buffer, so:
  380. *
  381. * if (sampleLastFill >= _lpSOSDMAFillCount)
  382. * <DMA_diff> = sampleLastFill - _lpSOSDMAFillCount
  383. * else
  384. * <DMA_diff> = (DMA_BUF_SIZE - lpSOSDMAFillCount) + sampleLastFill
  385. *
  386. * Note that, if using the stereo driver with mono data, you must
  387. * divide LastFill & FillCount by 2, but not TotalBytes. If using the
  388. * stereo driver with stereo data, you must divide all 3 variables
  389. * by 2.
  390. *
  391. * 2) If no audio is playing, but the timer interrupt is running,
  392. * VQATickCount is used as the timer
  393. *
  394. * 3) If no audio is playing & no timer interrupt is going, the DOS 18.2
  395. * system timer is used.
  396. *
  397. * Regardless of the method, TickOffset is used as an offset from the
  398. * computed time.
  399. *
  400. * INPUTS
  401. * NONE
  402. *
  403. * RESULT
  404. * Time - Time in VQA_TIMETICKS
  405. *
  406. ****************************************************************************/
  407. unsigned long VQA_GetTime(void)
  408. {
  409. #if(VQAAUDIO_ON)
  410. VQAAudio *audio;
  411. unsigned long bytesleft;
  412. unsigned long samples;
  413. #endif
  414. long ticks;
  415. #if(VQAAUDIO_ON)
  416. switch (TimerMethod) {
  417. /* If Audio is playing then timing is based on the audio DMA buffer
  418. * position.
  419. */
  420. case VQA_TMETHOD_AUDIO:
  421. /* Dereference commonly used data members for quicker access. */
  422. audio = &VQAP->VQABuf->Audio;
  423. /* Compute our current position in the audio track by getting the
  424. * bytes processed by HMI. Then adjust the bytes processed by the
  425. * position of the DMA fill handler, this should tell us exactly
  426. * where we are in the audio track.
  427. */
  428. samples = _lpSOSSampleList[DigiHandle][SampleHandle]->sampleTotalBytes;
  429. bytesleft = _lpSOSSampleList[DigiHandle][SampleHandle]->sampleBytesLeft;
  430. /* Adjust the bytes processed from the bytes left to DMA in the
  431. * buffer.
  432. */
  433. if (samples >= bytesleft) {
  434. samples -= bytesleft;
  435. }
  436. /* Calculate the number of samples by taking the total number of
  437. * bytes processed and divide it by the number of channels and
  438. * bits per sample.
  439. */
  440. samples -= (audio->NumSkipped * VQAP->Config.HMIBufSize);
  441. samples /= (audio->Channels * (audio->BitsPerSample >> 3));
  442. /* The elapsed ticks is calculated by the number of samples
  443. * processed times the tick resolution per second divided by the
  444. * sample rate.
  445. */
  446. ticks = (long)((samples * VQA_TIMETICKS) / audio->SampleRate);
  447. ticks += TickOffset;
  448. break;
  449. /* No audio playing, but timer interrupt is going; use VQATickCount */
  450. case VQA_TMETHOD_INT:
  451. ticks = (VQATickCount + TickOffset);
  452. break;
  453. /* No interrupts are going at all; use DOS's time */
  454. default:
  455. case VQA_TMETHOD_DOS:
  456. {
  457. struct timeb mytime;
  458. ftime(&mytime);
  459. ticks = (unsigned long)mytime.time*1000L+(unsigned long)mytime.millitm;
  460. ticks = ((ticks * VQA_TIMETICKS) / 1000L);
  461. ticks += TickOffset;
  462. }
  463. break;
  464. }
  465. #else
  466. {
  467. struct timeb mytime;
  468. ftime(&mytime);
  469. ticks = (unsigned long)mytime.time*1000L+(unsigned long)mytime.millitm;
  470. ticks = ((ticks * VQA_TIMETICKS) / 1000L);
  471. ticks += TickOffset;
  472. }
  473. #endif
  474. return (ticks);
  475. }
  476. /****************************************************************************
  477. *
  478. * NAME
  479. * VQA_TimerMethod - Get timer method being used.
  480. *
  481. * SYNOPSIS
  482. * Method = VQA_TimerMethod()
  483. *
  484. * long VQA_TimerMethod(void);
  485. *
  486. * FUNCTION
  487. * Returns the ID of the current timer method being used.
  488. *
  489. * INPUTS
  490. * NONE
  491. *
  492. * RESULT
  493. * Method - Method used for the timer.
  494. *
  495. ****************************************************************************/
  496. long VQA_TimerMethod(void)
  497. {
  498. #if(VQAAUDIO_ON)
  499. return (TimerMethod);
  500. #else
  501. return (VQA_TMETHOD_DOS);
  502. #endif
  503. }
  504. #if(VQAAUDIO_ON)
  505. /****************************************************************************
  506. *
  507. * NAME
  508. * VQA_OpenAudio - Open sound system.
  509. *
  510. * SYNOPSIS
  511. * Error = VQA_OpenAudio(VQAHandleP)
  512. *
  513. * long VQA_OpenAudio(VQAHandleP *);
  514. *
  515. * FUNCTION
  516. * Initialize the sound system by setting the DigiHandle to and HMI
  517. * driver handle. The timer must first be initialized before calling
  518. * this function.
  519. *
  520. * INPUTS
  521. * VQAHandleP - Pointer to private VQAHandle.
  522. *
  523. * RESULT
  524. * Error - 0 if successful, -1 if error.
  525. *
  526. ****************************************************************************/
  527. long VQA_OpenAudio(VQAHandleP *vqap)
  528. {
  529. VQAData *vqabuf;
  530. VQAAudio *audio;
  531. VQAConfig *config;
  532. VQAHeader *header;
  533. unsigned char *driver_path;
  534. WORD port;
  535. long rc;
  536. /* Return if already initialized */
  537. if (DigiInitFlag != HMI_UNINIT) {
  538. return (0);
  539. }
  540. /* Dereference data memebers for quicker access. */
  541. config = &vqap->Config;
  542. header = &vqap->Header;
  543. vqabuf = vqap->VQABuf;
  544. audio = &vqabuf->Audio;
  545. /* Fail if no audio buffer or DigiCard is 0 (no sound) */
  546. if ((audio->Buffer == NULL) || (config->DigiCard == 0)) {
  547. return (-1);
  548. }
  549. /* Reset the buffer position to the beginning. */
  550. CurBlock = 0;
  551. /*-------------------------------------------------------------------------
  552. * Compute the playback rate:
  553. *
  554. * - If Config->AudioRate is -1, use HMI_DATA_RATE scaled to the specified
  555. * frame rate (so the audio plays faster if we're playing faster)
  556. * - otherwise, use the specified rate
  557. *-----------------------------------------------------------------------*/
  558. if (config->AudioRate != -1) {
  559. sSOSInitDriver.wSampleRate = config->AudioRate;
  560. }
  561. else if (config->FrameRate != header->FPS) {
  562. sSOSInitDriver.wSampleRate = ((audio->SampleRate * config->FrameRate)
  563. / (unsigned long)header->FPS);
  564. config->AudioRate = sSOSInitDriver.wSampleRate;
  565. } else {
  566. sSOSInitDriver.wSampleRate = audio->SampleRate;
  567. config->AudioRate = audio->SampleRate;
  568. }
  569. /* If the application has already initialized HMI then set the
  570. * necessary variables. Otherwise we must setup HMI ourself.
  571. */
  572. if (config->OptionFlags & VQAOPTF_HMIINIT) {
  573. /* The application MUST provide the card type! */
  574. if (config->DigiCard == -1) {
  575. return (-1);
  576. }
  577. /* Init the detection system */
  578. driver_path = (unsigned char *)".\\";
  579. if ((rc = sosDIGIDetectInit(driver_path)) != 0) {
  580. return (rc);
  581. }
  582. /* Get the capabilities of the card being used. */
  583. rc = sosDIGIDetectGetCaps(config->DigiCard, &DigiCaps);
  584. sosDIGIDetectUnInit();
  585. if (rc != 0) {
  586. return (rc);
  587. }
  588. DigiHandle = config->DigiHandle;
  589. HMIDevName = "App-Specific";
  590. TimerInitFlag = HMI_APPINIT;
  591. DigiInitFlag = HMI_APPINIT;
  592. } else {
  593. /* Init the detection system */
  594. driver_path = (unsigned char *)".\\";
  595. if ((rc = sosDIGIDetectInit(driver_path)) != 0) {
  596. return (rc);
  597. }
  598. /*-----------------------------------------------------------------------
  599. * Initialize DigiHardware with port, IRQ, and DMA, and make sure
  600. * Config.DigiCard contains the HMI ID we want to use:
  601. *
  602. * - If Config.DigiCard is -1, auto-detect the hardware; then, do a
  603. * FindHardware so the GetSettings will work
  604. * - If Config.DigiCard is filled in, but port, IRQ or DMA is -1, use
  605. * FindHardware & GetSettings to get the settings
  606. * - If all are filled in, just use them as they are
  607. *---------------------------------------------------------------------*/
  608. /* Auto-Detect */
  609. if (config->DigiCard == -1) {
  610. /* Version 1 VQA's have only 8 bit mono audio streams. */
  611. if (header->Version == VQAHD_VER1) {
  612. config->DigiCard = AutoDetect(8, 1);
  613. } else {
  614. config->DigiCard = AutoDetect(audio->BitsPerSample, audio->Channels);
  615. /* Resort to 8bit mono */
  616. if (config->DigiCard == -1) {
  617. config->DigiCard = AutoDetect(8, 1);
  618. }
  619. }
  620. if (config->DigiCard == -1) {
  621. sosDIGIDetectUnInit();
  622. return (-1);
  623. }
  624. }
  625. /* Do a FindHardware & GetSettings */
  626. if (config->DigiPort == -1) {
  627. if (sosDIGIDetectFindHardware(config->DigiCard, &DigiCaps, &port)) {
  628. sosDIGIDetectUnInit();
  629. return (-1);
  630. }
  631. if (sosDIGIDetectGetSettings(&DigiHardware)) {
  632. sosDIGIDetectUnInit();
  633. return (-1);
  634. }
  635. config->DigiPort = DigiHardware.wPort;
  636. config->DigiIRQ = DigiHardware.wIRQ;
  637. config->DigiDMA = DigiHardware.wDMA;
  638. HMIDevName = (char *)DigiCaps.szDeviceName;
  639. } else {
  640. DigiHardware.wPort = config->DigiPort;
  641. DigiHardware.wIRQ = config->DigiIRQ;
  642. DigiHardware.wDMA = config->DigiDMA;
  643. HMIDevName = "App-Specific";
  644. }
  645. sosDIGIDetectUnInit();
  646. /* Initialize the DIGI system & driver */
  647. sosDIGIInitSystem(driver_path, _SOS_DEBUG_NORMAL);
  648. sSOSInitDriver.wBufferSize = config->HMIBufSize;
  649. sSOSInitDriver.lpBuffer = NULL;
  650. sSOSInitDriver.lpFillHandler = NULL;
  651. sSOSInitDriver.lpDriverMemory = NULL;
  652. sSOSInitDriver.lpTimerMemory = NULL;
  653. DigiHandle = -1;
  654. if ((rc = sosDIGIInitDriver(config->DigiCard, &DigiHardware,
  655. &sSOSInitDriver, &DigiHandle)) != 0) {
  656. return (rc);
  657. }
  658. /*-----------------------------------------------------------------------
  659. * Register the timer event
  660. *---------------------------------------------------------------------*/
  661. /* If the timer hasn't been init'd, do it now */
  662. if (TimerInitFlag == HMI_UNINIT) {
  663. sosTIMERInitSystem(_TIMER_DOS_RATE ,_SOS_DEBUG_NORMAL);
  664. TimerInitFlag = HMI_VQAINIT;
  665. }
  666. /* Register the event */
  667. rc = sosTIMERRegisterEvent(VQA_TIMETICKS, sSOSInitDriver.lpFillHandler,
  668. &DigiTimer);
  669. if (rc) {
  670. sosDIGIUnInitDriver(DigiHandle, _TRUE, _TRUE);
  671. sosDIGIUnInitSystem();
  672. return (rc);
  673. }
  674. config->DigiHandle = DigiHandle;
  675. DigiInitFlag = HMI_VQAINIT;
  676. }
  677. return (0);
  678. }
  679. /****************************************************************************
  680. *
  681. * NAME
  682. * AutoDetect - Auto detect the sound card.
  683. *
  684. * SYNOPSIS
  685. * CardID = AutoDetect(BitSize, Channels)
  686. *
  687. * long AutoDetect(long, long);
  688. *
  689. * FUNCTION
  690. * Autodetects the type of sound card present in the system.
  691. *
  692. * INPUTS
  693. * BitSize - Bits per sample size.
  694. * Channels - Number of desired channels.
  695. *
  696. * RESULT
  697. * CardID - HMI ID of the sound card found, -1 if none.
  698. *
  699. ****************************************************************************/
  700. long AutoDetect(long bitsize, long channels)
  701. {
  702. long device_id = -1;
  703. WORD port;
  704. long i;
  705. long rc;
  706. /* Search for an 8-bit mono device */
  707. if (sosDIGIDetectFindFirst(&DigiCaps, &port)) {
  708. return (-1);
  709. }
  710. channels--;
  711. i = 0;
  712. while (i < 6) {
  713. i++;
  714. if ((DigiCaps.wBitsPerSample == bitsize)
  715. && (DigiCaps.wChannels == channels)) {
  716. break;
  717. }
  718. if (sosDIGIDetectFindNext(&DigiCaps, &port)) {
  719. return (-1);
  720. }
  721. }
  722. /* Exit if failed to find the required device */
  723. if ((DigiCaps.wBitsPerSample != bitsize)
  724. || (DigiCaps.wChannels != channels)) {
  725. return (-1);
  726. }
  727. /* Stash the ID */
  728. device_id = DigiCaps.wDeviceID;
  729. /* Now that we have handled the initial pass, verify that if we found an
  730. * _ADLIB_GOLD_8_MONO that it is not a Logitech Sound Wave man in disguise.
  731. */
  732. if ((WORD)DigiCaps.wDeviceID == _ADLIB_GOLD_8_MONO) {
  733. rc = sosDIGIDetectFindNext(&DigiCaps, &port);
  734. while ((i < 6) && (rc == 0)) {
  735. i++;
  736. if ((DigiCaps.wBitsPerSample == 8) && (DigiCaps.wChannels == 0)) {
  737. break;
  738. }
  739. if ((rc = sosDIGIDetectFindNext(&DigiCaps, &port)) != 0) {
  740. break;
  741. }
  742. }
  743. /* If we don't have an error use the secondary device ID, after all,
  744. * anything's better than an Adlib Gold. If we do have an error or there
  745. * is nothing we can use then the device ID is already set to the adlib
  746. * gold so just let it rip.
  747. */
  748. if ((rc == 0) && ((WORD)DigiCaps.wDeviceID == _SBPRO_8_MONO)) {
  749. return (DigiCaps.wDeviceID);
  750. }
  751. }
  752. return (device_id);
  753. }
  754. /****************************************************************************
  755. *
  756. * NAME
  757. * VQA_CloseAudio - Close sound system
  758. *
  759. * SYNOPSIS
  760. * VQA_CloseAudio()
  761. *
  762. * void VQA_CloseAudio(void);
  763. *
  764. * FUNCTION
  765. * Removes VQA's involvement in the audio system.
  766. *
  767. * INPUTS
  768. * NONE
  769. *
  770. * RESULT
  771. * NONE
  772. *
  773. ****************************************************************************/
  774. void VQA_CloseAudio(void)
  775. {
  776. /* Remove the Digi event */
  777. if (DigiInitFlag == HMI_VQAINIT) {
  778. sosTIMERRemoveEvent(DigiTimer);
  779. }
  780. /* Un-init timer if necessary */
  781. if (TimerInitFlag == HMI_VQAINIT) {
  782. sosTIMERUnInitSystem(0);
  783. }
  784. TimerInitFlag = HMI_UNINIT;
  785. /* Remove the driver */
  786. if (DigiInitFlag == HMI_VQAINIT) {
  787. sosDIGIUnInitDriver(DigiHandle, _TRUE, _TRUE);
  788. sosDIGIUnInitSystem();
  789. }
  790. DigiInitFlag = HMI_UNINIT;
  791. if (PlayingFlag) {
  792. PlayingFlag = 0;
  793. }
  794. }
  795. /****************************************************************************
  796. *
  797. * NAME
  798. * VQA_StartAudio - Starts audio playback
  799. *
  800. * SYNOPSIS
  801. * Error = VQA_StartAudio(VQA)
  802. *
  803. * long VQA_StartAudio(VQAHandleP *);
  804. *
  805. * FUNCTION
  806. * Start the audio playback for the movie.
  807. *
  808. * INPUTS
  809. * VQA - Pointer to private VQA handle.
  810. *
  811. * RESULT
  812. * Error - 0 if successful, or -1 error code.
  813. *
  814. ****************************************************************************/
  815. long VQA_StartAudio(VQAHandleP *vqap)
  816. {
  817. VQAConfig *config;
  818. VQAAudio *audio;
  819. /* Save buffers for the callback routine */
  820. VQAP = vqap;
  821. /* Dereference commonly used data members for quicker access. */
  822. config = &vqap->Config;
  823. audio = &vqap->VQABuf->Audio;
  824. /* Return if already playing */
  825. if (audio->Flags & VQAAUDF_ISPLAYING) {
  826. return (-1);
  827. }
  828. /* Set my driver handle */
  829. if (config->DigiHandle != -1) {
  830. DigiHandle = config->DigiHandle;
  831. }
  832. if (DigiHandle == (WORD)-1) {
  833. return (-1);
  834. }
  835. /*-------------------------------------------------------------------------
  836. * Initialize the sample structure.
  837. *-----------------------------------------------------------------------*/
  838. memset(&sSOSSampleData, 0, sizeof(_SOS_START_SAMPLE));
  839. sSOSSampleData.lpSamplePtr = (unsigned char *)vqap->VQABuf->Audio.Buffer;
  840. sSOSSampleData.dwSampleSize = config->HMIBufSize;
  841. sSOSSampleData.wVolume = (config->Volume << 7);
  842. sSOSSampleData.wSampleID = HMI_SAMPLE;
  843. sSOSSampleData.lpCallback = AudioCallback;
  844. /* Set the channel flags for the type of data we have. */
  845. if (audio->Channels == 2) {
  846. sSOSSampleData.wChannel = _INTERLEAVED;
  847. } else {
  848. sSOSSampleData.wChannel = _CENTER_CHANNEL;
  849. }
  850. /* If the card is unable to handle stereo data the we must notify the
  851. * sound system to convert the stereo data to mono data during playback.
  852. */
  853. if ((audio->Channels - 1) > DigiCaps.wChannels) {
  854. sSOSSampleData.wSampleFlags |= _STEREOTOMONO;
  855. }
  856. /* If the card is unable to handle the sample size of the audio data
  857. * then we must notify the sound system to convert the audio data to
  858. * the proper format.
  859. */
  860. if (audio->BitsPerSample != DigiCaps.wBitsPerSample) {
  861. if (audio->BitsPerSample > DigiCaps.wBitsPerSample) {
  862. sSOSSampleData.wSampleFlags |= _TRANSLATE16TO8;
  863. } else {
  864. sSOSSampleData.wSampleFlags |= _TRANSLATE8TO16;
  865. }
  866. }
  867. /* Start playback */
  868. SampleHandle = sosDIGIStartSample(DigiHandle, &sSOSSampleData);
  869. audio->Flags |= VQAAUDF_ISPLAYING;
  870. PlayingFlag = 1;
  871. return (0);
  872. }
  873. /****************************************************************************
  874. *
  875. * NAME
  876. * VQA_StopAudio - Stop audio playback.
  877. *
  878. * SYNOPSIS
  879. * VQA_StopAudio(VQA)
  880. *
  881. * void VQA_StopAudio(VQAHandleP *);
  882. *
  883. * FUNCTION
  884. * Halts the currently playing audio stream.
  885. *
  886. * INPUTS
  887. * VQA - Pointer to private VQAHandle.
  888. *
  889. * RESULT
  890. * NONE
  891. *
  892. ****************************************************************************/
  893. void VQA_StopAudio(VQAHandleP *vqap)
  894. {
  895. /* Just return if not playing */
  896. if (vqap->VQABuf->Audio.Flags & VQAAUDF_ISPLAYING) {
  897. vqap->VQABuf->Audio.Flags &= ~VQAAUDF_ISPLAYING;
  898. sosDIGIStopSample(DigiHandle, SampleHandle);
  899. }
  900. }
  901. /****************************************************************************
  902. *
  903. * NAME
  904. * AudioCallback - Sound system callback.
  905. *
  906. * SYNOPSIS
  907. * AudioCallback(DriverHandle, Action, SampleID)
  908. *
  909. * void AudioCallback(WORD, WORD, WORD);
  910. *
  911. * FUNCTION
  912. * Our custom audio callback routine that services HMI.
  913. *
  914. * INPUTS
  915. * DriverHandle - HMI driver handle.
  916. * Action - Action taken.
  917. * SampleID - ID of sample.
  918. *
  919. * RESULT
  920. * NONE
  921. *
  922. ****************************************************************************/
  923. void far cdecl AudioCallback(WORD wDriverHandle,WORD wAction,WORD wSampleID)
  924. {
  925. VQAAudio *audio;
  926. VQAConfig *config;
  927. /* Dereference commonly used data members for quicker access. */
  928. audio = &VQAP->VQABuf->Audio;
  929. config = &VQAP->Config;
  930. /* Suppress compiler warnings */
  931. wDriverHandle = wDriverHandle;
  932. wSampleID = wSampleID;
  933. /* See if we're called because the buffer is empty */
  934. if (wAction == _SAMPLE_PROCESSED) {
  935. /* Compute the 'NextBlock' index */
  936. NextBlock = CurBlock + 1;
  937. if (NextBlock >= audio->NumAudBlocks) {
  938. NextBlock = 0;
  939. }
  940. /* See if the next block has data in it; if so, update the audio
  941. * buffer play position & the 'CurBlock' value.
  942. * If not, don't change anything and replay this block.
  943. */
  944. if (audio->IsLoaded[NextBlock] == 1) {
  945. /* Update this block's status to loadable (0) */
  946. audio->IsLoaded[CurBlock] = 0;
  947. /* Update position within audio buffer */
  948. audio->PlayPosition += config->HMIBufSize;
  949. CurBlock++;
  950. if (audio->PlayPosition >= config->AudioBufSize) {
  951. audio->PlayPosition = 0;
  952. CurBlock = 0;
  953. }
  954. } else {
  955. audio->NumSkipped++;
  956. }
  957. /* Start the new buffer playing */
  958. sSOSSampleData.lpSamplePtr = (unsigned char *)(audio->Buffer)
  959. + audio->PlayPosition;
  960. sosDIGIContinueSample(DigiHandle, SampleHandle, &sSOSSampleData);
  961. audio->SamplesPlayed += config->HMIBufSize;
  962. }
  963. }
  964. /****************************************************************************
  965. *
  966. * NAME
  967. * CopyAudio - Copy data from Audio Temp buffer into Audio play buffer.
  968. *
  969. * SYNOPSIS
  970. * Error = CopyAudio(VQA)
  971. *
  972. * long CopyAudio(VQAHandleP *);
  973. *
  974. * FUNCTION
  975. * This routine just copies the data in the TempBuf into the correct
  976. * spots in the audio play buffer. If there is no room available in the
  977. * audio play buffer, the routine returns VQAERR_SLEEPING, which will put
  978. * the whole Loader to "sleep" while it waits for a free buffer.
  979. *
  980. * If there's no data in the TempBuf to copy, the routine just returns 0.
  981. *
  982. * INPUTS
  983. * VQA - Pointer to private VQAHandle structure.
  984. *
  985. * RESULT
  986. * Error - 0 if successful or VQAERR_??? error code.
  987. *
  988. ****************************************************************************/
  989. long CopyAudio(VQAHandleP *vqap)
  990. {
  991. VQAAudio *audio;
  992. VQAConfig *config;
  993. long startblock;
  994. long endblock;
  995. long len1,len2;
  996. long i;
  997. /* Dereference commonly used data members for quicker access. */
  998. audio = &vqap->VQABuf->Audio;
  999. config = &vqap->Config;
  1000. /* If audio is disabled, or if we're playing from a VOC file, or if
  1001. * there's no Audio Buffer, or if there's no data to copy, just return 0
  1002. */
  1003. #if(VQAVOC_ON && VQAAUDIO_ON)
  1004. if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1)
  1005. || (audio->Buffer == NULL) || (audio->TempBufLen == 0)) {
  1006. #else /* VQAVOC_ON */
  1007. if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (audio->Buffer == NULL)
  1008. || (audio->TempBufLen == 0)) {
  1009. #endif /* VQAVOC_ON */
  1010. return (0);
  1011. }
  1012. /* Compute start & end blocks to copy into */
  1013. startblock = (audio->AudBufPos / config->HMIBufSize);
  1014. endblock = (audio->AudBufPos + audio->TempBufLen) / config->HMIBufSize;
  1015. if (endblock >= audio->NumAudBlocks) {
  1016. endblock -= audio->NumAudBlocks;
  1017. }
  1018. /* If 'endblock' hasn't played yet, return VQAERR_SLEEPING */
  1019. if (audio->IsLoaded[endblock] == 1) {
  1020. return (VQAERR_SLEEPING);
  1021. }
  1022. /* Copy the data:
  1023. *
  1024. * - If 'startblock' < 'endblock', copy the entire buffer
  1025. * - Otherwise, fill to the end of the buffer with part of the data, then
  1026. * copy the rest to the beginning of the buffer
  1027. */
  1028. if (startblock <= endblock) {
  1029. /* Copy data */
  1030. memcpy((audio->Buffer + audio->AudBufPos), audio->TempBuf,
  1031. audio->TempBufLen);
  1032. /* Adjust current load position */
  1033. audio->AudBufPos += audio->TempBufLen;
  1034. /* Mark buffer as empty */
  1035. audio->TempBufLen = 0;
  1036. /* Set all blocks to loaded */
  1037. for (i = startblock; i < endblock; i++) {
  1038. audio->IsLoaded[i] = 1;
  1039. }
  1040. return (0);
  1041. } else {
  1042. /* Compute length of each piece */
  1043. len1 = config->AudioBufSize - audio->AudBufPos;
  1044. len2 = audio->TempBufLen - len1;
  1045. /* Copy 1st piece into end of Audio Buffer */
  1046. memcpy((audio->Buffer + audio->AudBufPos), audio->TempBuf, len1);
  1047. /* Copy 2nd piece into start of Audio Buffer */
  1048. memcpy(audio->Buffer, audio->TempBuf + len1, len2);
  1049. /* Adjust load position */
  1050. audio->AudBufPos = len2;
  1051. /* Mark buffer as empty */
  1052. audio->TempBufLen = 0;
  1053. /* Set blocks to loaded */
  1054. for (i = startblock; i < audio->NumAudBlocks; i++) {
  1055. audio->IsLoaded[i] = 1;
  1056. }
  1057. for (i = 0; i < endblock; i++) {
  1058. audio->IsLoaded[i] = 1;
  1059. }
  1060. return (0);
  1061. }
  1062. }
  1063. #ifdef __WATCOMC__
  1064. #pragma pack(1);
  1065. #endif
  1066. #endif /* VQAAUDIO_ON */