AUDIO.CPP 34 KB

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