SOUNDIO.CPP 90 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207
  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. /* $Id: soundio.cpp 1.41 1994/06/20 15:01:39 joe_bostic Exp $ */
  19. /***********************************************************************************************
  20. ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S **
  21. ***********************************************************************************************
  22. * *
  23. * Project Name : Sound Library *
  24. * *
  25. * File Name : SOUND.CPP *
  26. * *
  27. * Programmer : Joe L. Bostic *
  28. * *
  29. * Start Date : July 22, 1991 *
  30. * *
  31. *---------------------------------------------------------------------------------------------*
  32. * Functions: *
  33. * Load_Long_Sample -- Loads a sample into XMS for double buffer system. *
  34. * Read_Long_Sample -- Loads/Processes/Formats/Builds offset. *
  35. * Save_Table_Entry -- Put an entry in the offset table. *
  36. * Play_Long_Sample -- Calls Init_Long_Sample and Start_Long_Sample. *
  37. * Start_Long_Sample -- Starts a sample playing that has be initialized. *
  38. * Get_Table_Entry -- Gets next entry in table. *
  39. * Long_Sample_Ticks -- Gets number of ticks in sample if in header. *
  40. * Long_Sample_Status -- Returns the status of the sample. *
  41. * Find_Table_Entry -- Finds next entry in table that matches mask. *
  42. * Get_Table_Start -- Returns a pointer to first entry in table. *
  43. * Long_Sample_Ticks_Played -- Number of ticks since sample started. *
  44. * Install_Sample_Driver_Callback -- Pokes callback function into JM driver *
  45. * Stop_Long_Sample -- Stops current long sample from playing. *
  46. * Long_Sample_Loaded_Size -- Max buffer size to load a long sample. *
  47. * Sound_Callback -- Audio driver callback function. *
  48. * DigiCallback -- Low level double buffering handler. *
  49. * Load_Sample_Into_Buffer -- Loads a digitized sample into a buffer. *
  50. * Stream_Sample -- Streams a sample directly from a file. *
  51. * Sample_Read -- Reads sample data from an openned file. *
  52. * Continue_Sample -- Tags another block of data onto the currently playing. *
  53. * Sample_Copy -- Copies sound data from source format to raw format. *
  54. * File_Stream_Preload -- Handles initial proload of a streaming samples bu*
  55. * Sample_Length -- returns length of a sample in ticks *
  56. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  57. extern void Colour_Debug (int call_number);
  58. #pragma pack(4)
  59. #define WIN32
  60. #ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check
  61. #define _WIN32
  62. #endif // _WIN32
  63. #include <windows.h>
  64. #include <windowsx.h>
  65. #include "dsound.h"
  66. #include <mem.h>
  67. #include <wwmem.h>
  68. #include "soundint.h"
  69. #include <stdio.h>
  70. #include <string.h>
  71. #include <direct.h>
  72. #include <stdlib.h>
  73. #include <process.h>
  74. #include <keyboard.h>
  75. #include <file.h>
  76. #include <bios.h>
  77. #include <timer.h>
  78. #include <math.h>
  79. #pragma pack(1)
  80. #include "audio.h"
  81. #pragma pack(4)
  82. LPDIRECTSOUNDBUFFER DumpBuffer;
  83. HANDLE SoundThreadHandle = NULL;
  84. BOOL SoundThreadActive = FALSE;
  85. /*
  86. ** If this is defined, then the streaming audio buffer will be filled
  87. ** to maximum whenever filling is to occur. If undefined, it will fill
  88. ** the streaming buffer in smaller chunks.
  89. */
  90. #define SIMPLE_FILLING
  91. /*
  92. ** This is the number of times per sec that the maintenance callback gets called.
  93. */
  94. #define MAINTENANCE_RATE 40 //30 times per sec plus a safety margin
  95. /*
  96. ** Size of the temporary buffer in XMS/EMS that direct file
  97. ** streaming of sounds will allocate.
  98. */
  99. //#define STREAM_BUFFER_SIZE (128L*1024L)
  100. #define STREAM_BUFFER_SIZE (128L*1024L)
  101. /*
  102. ** Define the number of "StreamBufferSize" blocks that are read in
  103. ** at a minimum when the streaming sample load callback routine
  104. ** is called. We will IGNORE loads that are less that this in order
  105. ** to avoid constant seeking on the CD.
  106. */
  107. #define STREAM_CUSHION_BLOCKS 4
  108. /*
  109. ** This is the maximum size that a sonarc block can be. All sonarc blocks
  110. ** must be either a multiple of this value or a binary root of this value.
  111. */
  112. #define LARGEST_SONARC_BLOCK 2048
  113. //////////////////////////////////////////////////////////////////////////////////////
  114. ////////////////////////////////////// structs ///////////////////////////////////////
  115. //void *DigiBuffer = NULL;
  116. static BOOL StartingFileStream = FALSE;
  117. int StreamLowImpact = FALSE;
  118. MemoryFlagType StreamBufferFlag = MEM_NORMAL;
  119. int Misc;
  120. SFX_Type SoundType;
  121. Sample_Type SampleType;
  122. int ReverseChannels = FALSE;
  123. LPDIRECTSOUND SoundObject; //Direct sound object
  124. LPDIRECTSOUNDBUFFER PrimaryBufferPtr; //Pointer to the buffer that the
  125. unsigned SoundTimerHandle=0; //Windows Handle for sound timer
  126. WAVEFORMATEX DsBuffFormat; //format of direct sound buffer
  127. DSBUFFERDESC BufferDesc; //Buffer description for creating buffers
  128. WAVEFORMATEX PrimaryBuffFormat; //Copy of format of direct sound primary buffer
  129. DSBUFFERDESC PrimaryBufferDesc; //Copy of buffer description for re-creating primary buffer
  130. CRITICAL_SECTION GlobalAudioCriticalSection;
  131. void *FileStreamBuffer = NULL; //Buffer for streaming audio from CD
  132. /*
  133. ** Function to call if we detect focus loss
  134. */
  135. extern void (*Audio_Focus_Loss_Function)(void) = NULL;
  136. /*=========================================================================*/
  137. /* The following PRIVATE functions are in this file: */
  138. /*=========================================================================*/
  139. static BOOL File_Callback(short id, short *odd, void **buffer, long *size);
  140. static int __cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback)(short id, short *odd, void **buffer, long *size), int volume, int handle);
  141. void CALLBACK Sound_Timer_Callback ( UINT, UINT, DWORD, DWORD, DWORD );
  142. //static int __cdecl Stream_Sample(void *buffer, long size, BOOL (*callback)(short id, short *odd, void **buffer, long *size));
  143. void Sound_Thread (void *);
  144. volatile BOOL AudioDone;
  145. /*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
  146. // This callback is called whenever the queue buffer playback has begun
  147. // and another buffer is needed for queuing up. Returns TRUE if there
  148. // is more data to read from the file.
  149. static BOOL File_Callback(short id, short *odd, void **buffer, long *size)
  150. {
  151. SampleTrackerType *st; // Pointer to sample playback control struct.
  152. void *ptr; // Pointer to working portion of file buffer.
  153. if (id != -1) {
  154. st = &LockedData.SampleTracker[id];
  155. ptr = st->FileBuffer;
  156. if (ptr) {
  157. /*
  158. ** Move the next pending block into the primary
  159. ** position. Do this only if the queue pointer is
  160. ** null.
  161. */
  162. if (!AudioDone){
  163. EnterCriticalSection(&GlobalAudioCriticalSection);
  164. }
  165. st->DontTouch = TRUE;
  166. if (!AudioDone){
  167. LeaveCriticalSection(&GlobalAudioCriticalSection);
  168. }
  169. if (!*buffer && st->FilePending) {
  170. *buffer = Add_Long_To_Pointer(ptr, (long)(*odd % LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize);
  171. st->FilePending--;
  172. *odd = (short)(*odd + 1);
  173. if (!st->FilePending) {
  174. *size = st->FilePendingSize;
  175. } else {
  176. *size = LockedData.StreamBufferSize;
  177. }
  178. }
  179. if (!AudioDone){
  180. EnterCriticalSection(&GlobalAudioCriticalSection);
  181. }
  182. st->DontTouch = FALSE;
  183. if (!AudioDone){
  184. LeaveCriticalSection(&GlobalAudioCriticalSection);
  185. }
  186. Sound_Timer_Callback(0,0,0,0,0); //Shouldnt block as we are calling it from the same thread
  187. /*
  188. ** If the file handle is still valid, then read in the next
  189. ** block and add it to the next pending slot available.
  190. */
  191. if (st->FilePending <
  192. (StreamLowImpact ? (LockedData.StreamBufferCount>>1) : ((LockedData.StreamBufferCount-3))) && st->FileHandle != WW_ERROR) {
  193. int num_empty_buffers;
  194. #ifdef SIMPLE_FILLING
  195. num_empty_buffers = (LockedData.StreamBufferCount-2) - st->FilePending;
  196. #else
  197. //
  198. // num_empty_buffers will be from 1 to StreamBufferCount
  199. //
  200. if (StreamLowImpact) {
  201. num_empty_buffers = MIN((LockedData.StreamBufferCount >> 1)+STREAM_CUSHION_BLOCKS, (LockedData.StreamBufferCount - 2) - st->FilePending);
  202. }
  203. else {
  204. num_empty_buffers = (LockedData.StreamBufferCount - 2) - st->FilePending;
  205. }
  206. #endif
  207. while (num_empty_buffers && (st->FileHandle != WW_ERROR)) {
  208. int tofill;
  209. long psize;
  210. tofill = (*odd + st->FilePending) % LockedData.StreamBufferCount;
  211. ptr = Add_Long_To_Pointer(st->FileBuffer, (long)tofill * (long)LockedData.StreamBufferSize);
  212. psize = Read_File(st->FileHandle, ptr, LockedData.StreamBufferSize);
  213. /*
  214. ** If less than the requested amount of data was read, this
  215. ** indicates that the source file is exhausted. Flag the source
  216. ** file as closed so that no further reading is attempted.
  217. */
  218. if (psize != LockedData.StreamBufferSize) {
  219. Close_File(st->FileHandle);
  220. st->FileHandle = WW_ERROR;
  221. }
  222. /*
  223. ** If any real data went into the pending buffer, then flag
  224. ** that this buffer is valid.
  225. */
  226. if (psize) {
  227. if (!AudioDone){
  228. EnterCriticalSection(&GlobalAudioCriticalSection);
  229. }
  230. st->DontTouch = TRUE;
  231. st->FilePendingSize = psize;
  232. st->FilePending++;
  233. st->DontTouch = FALSE;
  234. if (!AudioDone){
  235. LeaveCriticalSection(&GlobalAudioCriticalSection);
  236. }
  237. Sound_Timer_Callback(0,0,0,0,0); //Shouldnt block as we are calling it from the same thread
  238. }
  239. num_empty_buffers--;
  240. }
  241. /*
  242. ** After filling all pending buffers, check to see if the queue buffer
  243. ** is empty. If so, then assign the first available pending buffer to the
  244. ** queue.
  245. */
  246. if (!AudioDone){
  247. EnterCriticalSection(&GlobalAudioCriticalSection);
  248. }
  249. st->DontTouch = TRUE;
  250. if (!st->QueueBuffer && st->FilePending) {
  251. st->QueueBuffer = Add_Long_To_Pointer(st->FileBuffer, (long)(st->Odd%LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize);
  252. st->FilePending--;
  253. st->Odd++;
  254. if (!st->FilePending) {
  255. st->QueueSize = st->FilePendingSize;
  256. } else {
  257. st->QueueSize = LockedData.StreamBufferSize;
  258. }
  259. }
  260. st->DontTouch = FALSE;
  261. if (!AudioDone){
  262. LeaveCriticalSection(&GlobalAudioCriticalSection);
  263. }
  264. Sound_Timer_Callback(0,0,0,0,0); //Shouldnt block as we are calling it from the same thread
  265. }
  266. /*
  267. ** If there are no more buffers that the callback routine
  268. ** can slot into the primary position, then signal that
  269. ** no furthur callbacks are needed.
  270. */
  271. if (st->FilePending) {
  272. //LeaveCriticalSection(&GlobalAudioCriticalSection);
  273. return(TRUE);
  274. }
  275. }
  276. //LeaveCriticalSection(&GlobalAudioCriticalSection);
  277. }
  278. return(FALSE);
  279. }
  280. // Generic streaming sample playback initialization.
  281. static int __cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback)(short id, short *odd, void **buffer, long *size), int volume, int handle)
  282. {
  283. int playid=-1; // Sample play ID.
  284. SampleTrackerType *st; // Working pointer to sample control structure.
  285. long oldsize; // Copy of original sound size.
  286. AUDHeaderType header;
  287. if (!AudioDone && buffer && size && LockedData.DigiHandle != -1) {
  288. /*
  289. ** Start the first section of the sound playing.
  290. */
  291. Mem_Copy(buffer, &header, sizeof(header));
  292. oldsize = header.Size;
  293. header.Size = size-sizeof(header);
  294. Mem_Copy(&header, buffer, sizeof(header));
  295. playid = Play_Sample_Handle(buffer, 0xFF, volume, 0x0, handle);
  296. header.Size = oldsize;
  297. Mem_Copy(&header, buffer, sizeof(header));
  298. /*
  299. ** If the sample actually started playing, then flag this
  300. ** sample as a streaming type and signal for a callback
  301. ** to occur.
  302. */
  303. if (playid != -1) {
  304. st = &LockedData.SampleTracker[playid];
  305. st->Callback = callback;
  306. st->Odd = 0;
  307. // ServiceSomething = TRUE;
  308. }
  309. }
  310. return (playid);
  311. }
  312. #if (0)
  313. static int __cdecl Stream_Sample(void *buffer, long size, BOOL (*callback)(short id, short *odd, void **buffer, long *size), int handle)
  314. {
  315. return Stream_Sample_Vol(buffer, size, callback, 0xFF, handle);
  316. }
  317. #endif
  318. /***********************************************************************************************
  319. * File_Stream_Sample -- Streams a sample directly from a file. *
  320. * *
  321. * This will take the file specified and play it directly from disk. *
  322. * It performs this by allocating a temporary buffer in XMS/EMS and *
  323. * then keeping this buffer filled by the Sound_Callback() routine. *
  324. * *
  325. * INPUT: filename -- The name of the file to play. *
  326. * *
  327. * OUTPUT: Returns the handle to the sound -- just like Play_Sample(). *
  328. * *
  329. * WARNINGS: The temporary buffer is allocated when this routine is *
  330. * called and then freed when the sound is finished. Keep *
  331. * this in mind. *
  332. * *
  333. * HISTORY: *
  334. * 01/06/1994 JLB : Created. *
  335. *=============================================================================================*/
  336. int File_Stream_Sample(char const *filename, BOOL real_time_start)
  337. {
  338. return File_Stream_Sample_Vol(filename, 0xFF, real_time_start);
  339. }
  340. /***************************************************************************
  341. * FILE_STREAM_PRELOAD -- Handles initial proload of streaming samples *
  342. * *
  343. * This function is called before a sample which streams from disk is *
  344. * started. It can be called to either fill the buffer in small chunks *
  345. * from the call back routine or to fill the entire buffer at once. This *
  346. * is wholely dependant on whether the Loading bit is set within the *
  347. * sample tracker. *
  348. * *
  349. * INPUT:LockedData.SampleTracker * to the header which tracks this samples*
  350. * processing.*
  351. * *
  352. * OUTPUT: *
  353. * *
  354. * WARNINGS: *
  355. * *
  356. * HISTORY: *
  357. * 06/05/1995 PWG : Created. *
  358. *=========================================================================*/
  359. void File_Stream_Preload(int handle)
  360. {
  361. SampleTrackerType *st = &LockedData.SampleTracker[handle];
  362. int fh = st->FileHandle;
  363. int maxnum = (LockedData.StreamBufferCount >> 1) + STREAM_CUSHION_BLOCKS;
  364. void *buffer = st->FileBuffer;
  365. int num;
  366. /*
  367. ** Figure just how much we need to load. If we are doing the load in progress
  368. ** then we will only load two blocks.
  369. */
  370. if (st->Loading) {
  371. num = st->FilePending + 2;
  372. num = MIN(num, maxnum);
  373. } else {
  374. num = maxnum;
  375. }
  376. //EnterCriticalSection(&GlobalAudioCriticalSection);
  377. /*
  378. ** Loop through the blocks and load up the number we need.
  379. */
  380. for (int index = st->FilePending; index < num; index++) {
  381. long s = Read_File(fh, Add_Long_To_Pointer(buffer, (long)index * (long)LockedData.StreamBufferSize), LockedData.StreamBufferSize);
  382. if (s) {
  383. st->FilePendingSize = s;
  384. st->FilePending++;
  385. }
  386. if (s < LockedData.StreamBufferSize) break;
  387. }
  388. Sound_Timer_Callback(0,0,0,0,0); //Shouldnt block as we are calling it from the same thread
  389. /*
  390. ** If the last block was incomplete (ie. it didn't completely fill the buffer) or
  391. ** we have now filled up as much of the Streaming Buffer as we need to, then now is
  392. ** the time to kick off the sample.
  393. */
  394. if (st->FilePendingSize < LockedData.StreamBufferSize || index == maxnum) {
  395. /*
  396. ** Actually start the sample playing, and don't worry about the file callback
  397. ** it won't be called for a while.
  398. */
  399. int old = LockedData.SoundVolume;
  400. int size = (st->FilePending == 1) ? st->FilePendingSize : LockedData.StreamBufferSize;
  401. LockedData.SoundVolume = LockedData.ScoreVolume;
  402. StartingFileStream = TRUE;
  403. Stream_Sample_Vol(buffer, size, File_Callback, st->Volume, handle);
  404. StartingFileStream = FALSE;
  405. LockedData.SoundVolume = old;
  406. /*
  407. ** The Sample is finished loading (if it was loading in small pieces) so record that
  408. ** so that it will now use the active logic in the file call back.
  409. */
  410. st->Loading = FALSE;
  411. /*
  412. ** Decrement the file pending because the first block is already playing thanks
  413. ** to the play sample call above.
  414. */
  415. st->FilePending--;
  416. /*
  417. ** If File pending is now a zero, then we only preloaded one block and there
  418. ** is nothing more to play. So clear the sample tracing structure of the
  419. ** information it no longer needs.
  420. */
  421. if (!st->FilePending) {
  422. st->Odd = 0;
  423. st->QueueBuffer = 0;
  424. st->QueueSize = 0;
  425. st->FilePendingSize = 0;
  426. st->Callback = NULL;
  427. Close_File(fh);
  428. } else {
  429. /*
  430. ** The QueueBuffer counts as an already played block so remove it from the total.
  431. ** Note: We didn't remove it before because there might not have been one.
  432. */
  433. st->FilePending--;
  434. /*
  435. ** When we start loading we need to start past the first two blocks. Why this
  436. ** is called Odd, I haven't got the slightest.
  437. */
  438. st->Odd = 2;
  439. /*
  440. ** If the file pending size is less than the stream buffer, then the last block
  441. ** we loaded was the last block period. So close the file and reset the handle.
  442. */
  443. if (st->FilePendingSize != LockedData.StreamBufferSize) {
  444. Close_File(fh);
  445. st->FileHandle = WW_ERROR;
  446. }
  447. /*
  448. ** The Queue buffer needs to point at the next block to be processed. The size
  449. ** of the queue is dependant on how many more blocks there are.
  450. */
  451. st->QueueBuffer = Add_Long_To_Pointer(buffer, LockedData.StreamBufferSize);
  452. if (!st->FilePending) {
  453. st->QueueSize = st->FilePendingSize;
  454. } else {
  455. st->QueueSize = LockedData.StreamBufferSize;
  456. }
  457. }
  458. }
  459. //LeaveCriticalSection(&GlobalAudioCriticalSection);
  460. }
  461. /***********************************************************************************************
  462. * File_Stream_Sample_Vol -- Streams a sample directly from a file. *
  463. * *
  464. * This will take the file specified and play it directly from disk. *
  465. * It performs this by allocating a temporary buffer in XMS/EMS and *
  466. * then keeping this buffer filled by the Sound_Callback() routine. *
  467. * *
  468. * INPUT: filename -- The name of the file to play. *
  469. * *
  470. * OUTPUT: Returns the handle to the sound -- just like Play_Sample(). *
  471. * *
  472. * WARNINGS: The temporary buffer is allocated when this routine is *
  473. * called and then freed when the sound is finished. Keep *
  474. * this in mind. *
  475. * *
  476. * HISTORY: *
  477. *=============================================================================================*/
  478. int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start)
  479. {
  480. SampleTrackerType *st;
  481. int fh;
  482. int handle = -1;
  483. int index;
  484. if (!AudioDone && LockedData.DigiHandle != -1 && filename && Find_File(filename)) {
  485. /*
  486. ** Make sure all sample tracker structures point to the same
  487. ** upper memory buffer. This allocation only occurs if at
  488. ** least one sample gets "streamed".
  489. */
  490. if (!FileStreamBuffer) {
  491. FileStreamBuffer = Alloc(LockedData.StreamBufferSize * LockedData.StreamBufferCount, (MemoryFlagType)(StreamBufferFlag | MEM_TEMP | MEM_LOCK));
  492. for (index = 0; index < MAX_SFX; index++) {
  493. LockedData.SampleTracker[index].FileBuffer = FileStreamBuffer;
  494. }
  495. }
  496. /*
  497. ** If after trying to allocate the buffer we still fail then
  498. ** we can stream this sample.
  499. */
  500. if (!FileStreamBuffer) return(-1);
  501. /*
  502. ** Lets see if we can sucessfully open up the file. If we can't,
  503. ** then there is no point in going any farther.
  504. */
  505. if ((fh = Open_File(filename, READ)) == -1) {
  506. return (-1);
  507. }
  508. /*
  509. ** Reserve a handle so that we can fill in the sample tracker
  510. ** with the needed information. If we dont get valid handle then
  511. ** we might as well give up.
  512. */
  513. if ((unsigned)(handle = Get_Free_Sample_Handle(0xFF)) >= MAX_SFX) {
  514. return(-1);
  515. }
  516. /*
  517. ** Now lets get a pointer to the proper sample handler and start
  518. ** our manipulations.
  519. */
  520. st = &LockedData.SampleTracker[handle];
  521. st->IsScore = TRUE;
  522. st->FilePending = 0;
  523. st->FilePendingSize = 0;
  524. st->Loading = real_time_start;
  525. st->Volume = volume;
  526. st->FileHandle = fh;
  527. /*
  528. ** Now that we have setup our initial data properly, let load up
  529. ** the beginning of the sample we intend to stream.
  530. */
  531. File_Stream_Preload(handle);
  532. }
  533. return (handle);
  534. }
  535. /***********************************************************************************************
  536. * Sound_Callback -- Audio driver callback function. *
  537. * *
  538. * Maintains the audio buffers. This routine must be called at least *
  539. * 11 times per second or else audio glitches will occur. *
  540. * *
  541. * INPUT: none *
  542. * *
  543. * OUTPUT: none *
  544. * *
  545. * WARNINGS: If this routine is not called often enough then audio *
  546. * glitches will occur. *
  547. * *
  548. * HISTORY: *
  549. * 01/06/1994 JLB : Created. *
  550. *=============================================================================================*/
  551. void __cdecl Sound_Callback(void)
  552. {
  553. int index;
  554. SampleTrackerType *st;
  555. if (!AudioDone && LockedData.DigiHandle != -1) {
  556. /*
  557. ** Call the timer callback now as we may block it in this function
  558. */
  559. Sound_Timer_Callback(0,0,0,0,0);
  560. st = &LockedData.SampleTracker[0];
  561. for (index = 0; index < MAX_SFX; index++) {
  562. if (st->Loading) {
  563. File_Stream_Preload(index);
  564. } else {
  565. /*
  566. ** General service routine to handle moving small blocks from the
  567. ** source into the low RAM staging buffers.
  568. */
  569. if (st->Active) {
  570. /*
  571. ** Special check to see if the sample is a fading one AND
  572. ** it has faded to silence, then stop it here.
  573. */
  574. if (st->Reducer && !st->Volume) {
  575. //EnterCriticalSection(&GlobalAudioCriticalSection);
  576. Stop_Sample(index);
  577. //LeaveCriticalSection(&GlobalAudioCriticalSection);
  578. } else {
  579. /*
  580. ** Fill the queuebuffer if it is currently empty
  581. ** and there is a callback function defined to fill it.
  582. **
  583. ** PWG/CDY & CO: We should be down by at least two blocks
  584. ** before we bother with this
  585. */
  586. if ((!st->QueueBuffer ||
  587. (st->FileHandle != WW_ERROR && st->FilePending < LockedData.StreamBufferCount-3)) &&
  588. st->Callback) {
  589. if (!st->Callback((short)index, (short int *)&st->Odd, &st->QueueBuffer, &st->QueueSize)) {
  590. st->Callback = NULL;
  591. }
  592. }
  593. }
  594. } else {
  595. /*
  596. ** This catches the case where a streaming sample gets
  597. ** aborted prematurely because of failure to call the
  598. ** callback function frequently enough. In this case, the
  599. ** sample will be flagged as inactive, but the file handle
  600. ** will not have been closed.
  601. */
  602. if (st->FileHandle != WW_ERROR) {
  603. //EnterCriticalSection(&GlobalAudioCriticalSection);
  604. Close_File(st->FileHandle);
  605. st->FileHandle = WW_ERROR;
  606. //LeaveCriticalSection(&GlobalAudioCriticalSection);
  607. }
  608. }
  609. }
  610. /*
  611. ** Advance to the next sample control structure.
  612. */
  613. st++;
  614. }
  615. }
  616. }
  617. /***********************************************************************************************
  618. * Load_Sample -- Loads a digitized sample into RAM. *
  619. * *
  620. * This routine loads a digitized sample into RAM. *
  621. * *
  622. * INPUT: filename -- Name of the sound file to load. *
  623. * *
  624. * OUTPUT: Returns with a pointer to the loaded sound file. This pointer *
  625. * is passed to Play_Sample when playback is desired. *
  626. * *
  627. * WARNINGS: If there is insufficient memory to load the sample, then *
  628. * NULL will be returned. *
  629. * *
  630. * HISTORY: *
  631. * 04/17/1992 JLB : Created. *
  632. * 01/06/1994 JLB : HMI version. *
  633. *=============================================================================================*/
  634. void *Load_Sample(char const *filename)
  635. {
  636. void *buffer = NULL;
  637. long size;
  638. int fh;
  639. if (LockedData.DigiHandle == -1 || !filename || !Find_File(filename)) {
  640. return (NULL);
  641. }
  642. fh = Open_File(filename, READ);
  643. if (fh != WW_ERROR) {
  644. size = File_Size(fh)+sizeof(AUDHeaderType);
  645. buffer = Alloc(size, MEM_NORMAL);
  646. if (buffer) {
  647. Sample_Read(fh, buffer, size);
  648. }
  649. Close_File(fh);
  650. Misc = size;
  651. }
  652. return(buffer);
  653. }
  654. /***********************************************************************************************
  655. * Load_Sample_Into_Buffer -- Loads a digitized sample into a buffer. *
  656. * *
  657. * This routine is used to load a digitized sample into a buffer *
  658. * provided by the programmer. This buffer can be in XMS or EMS. *
  659. * *
  660. * INPUT: filename -- The filename to load. *
  661. * *
  662. * buffer -- Pointer to the buffer to load into. *
  663. * *
  664. * size -- The size of the buffer to load into. *
  665. * *
  666. * OUTPUT: Returns the number of bytes actually used in the buffer. *
  667. * *
  668. * WARNINGS: This routine will not overflow the buffer provided. This *
  669. * means that the buffer must be big enough to hold the data *
  670. * or else the sound will be cut short. *
  671. * *
  672. * HISTORY: *
  673. * 01/06/1994 JLB : Created. *
  674. *=============================================================================================*/
  675. long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size)
  676. {
  677. int fh;
  678. /*
  679. ** Verify legality of parameters.
  680. */
  681. if (!buffer || !size || LockedData.DigiHandle == -1 || !filename || !Find_File(filename)) {
  682. return (NULL);
  683. }
  684. fh = Open_File(filename, READ);
  685. if (fh != WW_ERROR) {
  686. size = Sample_Read(fh, buffer, size);
  687. Close_File(fh);
  688. } else {
  689. return(0);
  690. }
  691. return(size);
  692. }
  693. /***********************************************************************************************
  694. * Sample_Read -- Reads sample data from an openned file. *
  695. * *
  696. * This routine reads a sample file. It is presumed that the file is *
  697. * already positioned at the start of the sample. From this, it can *
  698. * determine if it is a VOC or raw data and proceed accordingly. *
  699. * *
  700. * INPUT: fh -- File handle of already openned sample file. *
  701. * *
  702. * buffer -- Pointer to the buffer to load data into. *
  703. * *
  704. * size -- The size of the buffer. *
  705. * *
  706. * OUTPUT: Returns the number of bytes actually used in the buffer. *
  707. * *
  708. * WARNINGS: none *
  709. * *
  710. * HISTORY: *
  711. * 01/06/1994 JLB : Created. *
  712. *=============================================================================================*/
  713. long Sample_Read(int fh, void *buffer, long size)
  714. {
  715. AUDHeaderType RawHeader;
  716. void *outbuffer; // Pointer to start of raw data.
  717. long actual_bytes_read; // Actual bytes read in, including header
  718. /*
  719. ** Conversion formula for TCrate and Hz rate.
  720. **
  721. ** TC = 256 - 1m/rate
  722. ** rate = 1m / (256-TC)
  723. */
  724. if (!buffer || fh == WW_ERROR || size <= sizeof(RawHeader)) return(NULL);
  725. size -= sizeof(RawHeader);
  726. outbuffer = Add_Long_To_Pointer(buffer, sizeof(RawHeader));
  727. actual_bytes_read = Read_File(fh, &RawHeader, sizeof(RawHeader));
  728. actual_bytes_read +=Read_File(fh, outbuffer, MIN(size, RawHeader.Size));
  729. Mem_Copy(&RawHeader, buffer, sizeof(RawHeader));
  730. return(actual_bytes_read);
  731. }
  732. /***********************************************************************************************
  733. * Free_Sample -- Frees a previously loaded digitized sample. *
  734. * *
  735. * Use this routine to free the memory allocated by a previous call to *
  736. * Load_Sample. *
  737. * *
  738. * INPUT: sample -- Pointer to the sample to be freed. *
  739. * *
  740. * OUTPUT: none *
  741. * *
  742. * WARNINGS: none *
  743. * *
  744. * HISTORY: *
  745. * 04/17/1992 JLB : Created. *
  746. *=============================================================================================*/
  747. void Free_Sample(void const *sample)
  748. {
  749. if (sample) Free((void *)sample);
  750. }
  751. /***********************************************************************************************
  752. * Sound_Timer_Callback -- windows timer callback for sound maintenance *
  753. * *
  754. * *
  755. * *
  756. * INPUT: Nothing *
  757. * *
  758. * OUTPUT: Nothing *
  759. * *
  760. * WARNINGS: None *
  761. * *
  762. * HISTORY: *
  763. * 11/2/95 4:01PM ST : Created *
  764. *=============================================================================================*/
  765. void CALLBACK Sound_Timer_Callback ( UINT, UINT, DWORD, DWORD, DWORD )
  766. {
  767. //if (!InTimerCallback){
  768. //InTimerCallback++;
  769. //Colour_Debug (5);
  770. if (!AudioDone){
  771. EnterCriticalSection(&GlobalAudioCriticalSection);
  772. maintenance_callback();
  773. LeaveCriticalSection(&GlobalAudioCriticalSection);
  774. }
  775. //Colour_Debug (0);
  776. //InTimerCallback--;
  777. //}
  778. }
  779. void Sound_Thread (void *)
  780. {
  781. DuplicateHandle (GetCurrentProcess(), GetCurrentThread() , GetCurrentProcess() ,&SoundThreadHandle , THREAD_ALL_ACCESS , TRUE , 0);
  782. SetThreadPriority (SoundThreadHandle, THREAD_PRIORITY_TIME_CRITICAL);
  783. SoundThreadActive = TRUE;
  784. while (!AudioDone){
  785. EnterCriticalSection(&GlobalAudioCriticalSection);
  786. maintenance_callback();
  787. LeaveCriticalSection(&GlobalAudioCriticalSection);
  788. Sleep(1000/40);
  789. }
  790. SoundThreadActive = FALSE;
  791. }
  792. /***********************************************************************************************
  793. * Set_Primary_Buffer_Format -- set the format of the primary sound buffer *
  794. * *
  795. * *
  796. * *
  797. * INPUT: Nothing *
  798. * *
  799. * OUTPUT: TRUE if successfully set *
  800. * *
  801. * WARNINGS: None *
  802. * *
  803. * HISTORY: *
  804. * 12/22/95 4:06PM ST : Created *
  805. *=============================================================================================*/
  806. BOOL Set_Primary_Buffer_Format(void)
  807. {
  808. if (SoundObject && PrimaryBufferPtr){
  809. return (PrimaryBufferPtr->SetFormat ( &PrimaryBuffFormat ) == DS_OK);
  810. }
  811. return (FALSE);
  812. }
  813. /***********************************************************************************************
  814. * Print_Sound_Error -- show error messages from failed sound initialisation *
  815. * *
  816. * *
  817. * *
  818. * INPUT: error text *
  819. * handle to window *
  820. * *
  821. * OUTPUT: Nothing *
  822. * *
  823. * WARNINGS: None *
  824. * *
  825. * HISTORY: *
  826. * 2/7/96 10:17AM ST : Created *
  827. *=============================================================================================*/
  828. void Print_Sound_Error(char *sound_error, HWND window)
  829. {
  830. MessageBox(window, sound_error, "Command & Conquer", MB_ICONEXCLAMATION|MB_OK);
  831. }
  832. /***********************************************************************************************
  833. * Audio_Init -- Initialise the sound system *
  834. * *
  835. * *
  836. * *
  837. * INPUT: window - window to send callback messages to *
  838. * maximum bits_per_sample - 8 or 16 *
  839. * stereo - will stereo samples be played *
  840. * rate - maximum sample rate required *
  841. * reverse_channels *
  842. * *
  843. * OUTPUT: TRUE if correctly initialised *
  844. * *
  845. * WARNINGS: None *
  846. * *
  847. * HISTORY: *
  848. * Unknown.... *
  849. * 08-24-95 10:01am ST : Modified for Windows 95 Direct Sound *
  850. *=============================================================================================*/
  851. BOOL Audio_Init( HWND window , int bits_per_sample, BOOL stereo , int rate , int reverse_channels)
  852. {
  853. int index;
  854. int sample=1;
  855. short old_bits_per_sample;
  856. short old_block_align;
  857. long old_bytes_per_sec;
  858. Init_Locked_Data();
  859. FileStreamBuffer = NULL;
  860. memset(&LockedData.SampleTracker[0], 0, sizeof(LockedData.SampleTracker));
  861. if ( !SoundObject ){
  862. /*
  863. ** Create the direct sound object
  864. */
  865. if ( DirectSoundCreate (NULL,&SoundObject,NULL) !=DS_OK ) {
  866. Print_Sound_Error("Warning - Unable to create Direct Sound Object",window);
  867. return (FALSE);
  868. }
  869. /*
  870. ** Give ourselves exclusive access to it
  871. */
  872. if ( SoundObject->SetCooperativeLevel( window, DSSCL_PRIORITY ) != DS_OK){
  873. Print_Sound_Error("Warning - Unable to set Direct Sound cooperative level",window);
  874. SoundObject->Release();
  875. SoundObject = NULL;
  876. return (FALSE);
  877. }
  878. /*
  879. ** Set up the primary buffer structure
  880. */
  881. memset (&BufferDesc , 0 , sizeof(DSBUFFERDESC));
  882. BufferDesc.dwSize=sizeof(DSBUFFERDESC);
  883. BufferDesc.dwFlags=DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
  884. /*
  885. ** Set up the primary buffer format
  886. */
  887. memset (&DsBuffFormat , 0 , sizeof(WAVEFORMATEX));
  888. DsBuffFormat.wFormatTag = WAVE_FORMAT_PCM;
  889. DsBuffFormat.nChannels = (unsigned short) (1 + stereo);
  890. DsBuffFormat.nSamplesPerSec = rate;
  891. DsBuffFormat.wBitsPerSample = (short) bits_per_sample;
  892. DsBuffFormat.nBlockAlign = (unsigned short)( (DsBuffFormat.wBitsPerSample/8) * DsBuffFormat.nChannels);
  893. DsBuffFormat.nAvgBytesPerSec= DsBuffFormat.nSamplesPerSec * DsBuffFormat.nBlockAlign;
  894. DsBuffFormat.cbSize = 0;
  895. /*
  896. ** Make a copy of the primary buffer description so we can reset its format later
  897. */
  898. memcpy (&PrimaryBufferDesc , &BufferDesc , sizeof(DSBUFFERDESC));
  899. memcpy (&PrimaryBuffFormat , &DsBuffFormat , sizeof(WAVEFORMATEX));
  900. /*
  901. ** Create the primary buffer object
  902. */
  903. if ( SoundObject->CreateSoundBuffer (&PrimaryBufferDesc ,
  904. &PrimaryBufferPtr ,
  905. NULL ) !=DS_OK ){
  906. Print_Sound_Error("Warning - Unable to create Direct Sound primary buffer",window);
  907. SoundObject->Release();
  908. SoundObject = NULL;
  909. return (FALSE);
  910. }
  911. /*
  912. ** Set the format of the primary sound buffer
  913. **
  914. */
  915. if (!Set_Primary_Buffer_Format()){
  916. /*
  917. ** If we failed to create a 16 bit primary buffer - try for an 8bit one
  918. */
  919. if (DsBuffFormat.wBitsPerSample == 16){
  920. /*
  921. ** Save the old values
  922. */
  923. old_bits_per_sample = DsBuffFormat.wBitsPerSample;
  924. old_block_align = DsBuffFormat.nBlockAlign;
  925. old_bytes_per_sec = DsBuffFormat.nAvgBytesPerSec;
  926. /*
  927. ** Set up the 8-bit ones
  928. */
  929. DsBuffFormat.wBitsPerSample = 8;
  930. DsBuffFormat.nBlockAlign = (unsigned short)( (DsBuffFormat.wBitsPerSample/8) * DsBuffFormat.nChannels);
  931. DsBuffFormat.nAvgBytesPerSec= DsBuffFormat.nSamplesPerSec * DsBuffFormat.nBlockAlign;
  932. /*
  933. ** Make a copy of the primary buffer description so we can reset its format later
  934. */
  935. memcpy (&PrimaryBufferDesc , &BufferDesc , sizeof(DSBUFFERDESC));
  936. memcpy (&PrimaryBuffFormat , &DsBuffFormat , sizeof(WAVEFORMATEX));
  937. }
  938. if (!Set_Primary_Buffer_Format()){
  939. /*
  940. ** We failed to set any useful format so print up an error message and give up
  941. */
  942. PrimaryBufferPtr->Release();
  943. PrimaryBufferPtr = NULL;
  944. SoundObject->Release();
  945. SoundObject = NULL;
  946. Print_Sound_Error("Warning - Your sound card does not meet the products audio requirements",window);
  947. return (FALSE);
  948. }else{
  949. /*
  950. ** OK, got an 8bit sound buffer. Not perfect but it will do
  951. ** We still want 16 bit secondary buffers so restore those values
  952. */
  953. DsBuffFormat.wBitsPerSample = old_bits_per_sample;
  954. DsBuffFormat.nBlockAlign = old_block_align;
  955. DsBuffFormat.nAvgBytesPerSec = old_bytes_per_sec;
  956. }
  957. }
  958. /*
  959. ** Start the primary sound buffer playing
  960. **
  961. */if ( PrimaryBufferPtr->Play(0,0,DSBPLAY_LOOPING) != DS_OK ){
  962. Print_Sound_Error("Unable to play Direct Sound primary buffer",window);
  963. PrimaryBufferPtr->Release();
  964. PrimaryBufferPtr = NULL;
  965. SoundObject->Release();
  966. SoundObject = NULL;
  967. return (FALSE);
  968. }
  969. LockedData.DigiHandle=1;
  970. /*
  971. ** Initialise the global critical section object for sound thread syncronisation
  972. */
  973. InitializeCriticalSection(&GlobalAudioCriticalSection);
  974. /*
  975. ** Initialise the Windows timer system to provide us with a callback
  976. **
  977. */
  978. SoundTimerHandle = timeSetEvent ( 1000/MAINTENANCE_RATE , 1 , Sound_Timer_Callback , 0 , TIME_PERIODIC);
  979. AudioDone = FALSE;
  980. //_beginthread(&Sound_Thread, NULL, 16*1024, NULL);
  981. /*
  982. ** Define the format for the secondary sound buffers
  983. */
  984. BufferDesc.dwFlags=DSBCAPS_CTRLVOLUME;
  985. BufferDesc.dwBufferBytes=SECONDARY_BUFFER_SIZE;
  986. BufferDesc.lpwfxFormat = (LPWAVEFORMATEX) &DsBuffFormat;
  987. /*
  988. ** Allocate a decompression buffer equal to the size of a SONARC frame
  989. ** block.
  990. */
  991. LockedData.UncompBuffer = Alloc(LARGEST_SONARC_BLOCK + 50, (MemoryFlagType)(MEM_NORMAL|MEM_CLEAR|MEM_LOCK));
  992. /*
  993. ** Allocate once secondary direct sound buffer for each simultaneous sound effect
  994. **
  995. */
  996. for (index = 0; index < MAX_SFX; index++) {
  997. SoundObject->CreateSoundBuffer (&BufferDesc , &LockedData.SampleTracker[index].PlayBuffer , NULL);
  998. LockedData.SampleTracker[index].PlaybackRate = rate;
  999. LockedData.SampleTracker[index].Stereo = (stereo) ? AUD_FLAG_STEREO : 0;
  1000. LockedData.SampleTracker[index].BitSize = (bits_per_sample == 16) ? AUD_FLAG_16BIT : 0;
  1001. LockedData.SampleTracker[index].FileHandle = WW_ERROR;
  1002. LockedData.SampleTracker[index].QueueBuffer = NULL;
  1003. InitializeCriticalSection (&LockedData.SampleTracker[index].AudioCriticalSection);
  1004. }
  1005. SoundType = (SFX_Type)sample;
  1006. SampleType = (Sample_Type)sample;
  1007. ReverseChannels = reverse_channels;
  1008. }
  1009. return(TRUE);
  1010. }
  1011. /***********************************************************************************************
  1012. * Sound_End -- Uninitializes the sound driver. *
  1013. * *
  1014. * This routine will uninitialize the sound driver (if any was *
  1015. * installed). This routine must be called at program termination *
  1016. * time. *
  1017. * *
  1018. * INPUT: none *
  1019. * *
  1020. * OUTPUT: none *
  1021. * *
  1022. * WARNINGS: none *
  1023. * *
  1024. * HISTORY: *
  1025. * 07/23/1991 JLB : Created. *
  1026. * 11/02/1995 ST : Modified for Direct Sound *
  1027. *=============================================================================================*/
  1028. void Sound_End(void)
  1029. {
  1030. int index;
  1031. /*
  1032. ** Remove the Windows timer event we installed for the sound callback
  1033. */
  1034. if (SoundTimerHandle){
  1035. timeKillEvent(SoundTimerHandle);
  1036. SoundTimerHandle = 0;
  1037. }
  1038. if (SoundObject && PrimaryBufferPtr){
  1039. /*
  1040. ** Stop all sounds and release the Direct Sound secondary sound buffers
  1041. */
  1042. for (index=0 ; index < MAX_SFX; index++){
  1043. if ( LockedData.SampleTracker[index].PlayBuffer ){
  1044. Stop_Sample (index);
  1045. LockedData.SampleTracker[index].PlayBuffer->Stop();
  1046. LockedData.SampleTracker[index].PlayBuffer->Release();
  1047. LockedData.SampleTracker[index].PlayBuffer = NULL;
  1048. DeleteCriticalSection(&LockedData.SampleTracker[index].AudioCriticalSection);
  1049. }
  1050. }
  1051. }
  1052. AudioDone = TRUE;
  1053. if (FileStreamBuffer){
  1054. Free (FileStreamBuffer);
  1055. FileStreamBuffer = NULL;
  1056. }
  1057. /*
  1058. ** Stop and release the direct sound primary buffer
  1059. */
  1060. if (PrimaryBufferPtr){
  1061. PrimaryBufferPtr->Stop();
  1062. PrimaryBufferPtr->Release();
  1063. PrimaryBufferPtr = NULL;
  1064. }
  1065. /*
  1066. ** Release the Direct Sound Object
  1067. */
  1068. if (SoundObject){
  1069. SoundObject->Release();
  1070. SoundObject = NULL;
  1071. }
  1072. if (LockedData.UncompBuffer) {
  1073. Free(LockedData.UncompBuffer);
  1074. LockedData.UncompBuffer = 0;
  1075. }
  1076. /*
  1077. ** Since the timer has stopped, we are finished with our global critical section.
  1078. */
  1079. DeleteCriticalSection(&GlobalAudioCriticalSection);
  1080. }
  1081. /***********************************************************************************************
  1082. * Stop_Sample -- Stops any currently playing sampled sound. *
  1083. * *
  1084. * *
  1085. * *
  1086. * INPUT: *
  1087. * *
  1088. * OUTPUT: *
  1089. * *
  1090. * WARNINGS: *
  1091. * *
  1092. * HISTORY: *
  1093. * 06/02/1992 JLB : Created. *
  1094. * 11/2/95 4:09PM ST : Modified for Direct Sound *
  1095. *=============================================================================================*/
  1096. void Stop_Sample(int handle)
  1097. {
  1098. if (LockedData.DigiHandle != -1 && (unsigned)handle < MAX_SFX) {
  1099. if (AudioDone) return;
  1100. EnterCriticalSection (&GlobalAudioCriticalSection);
  1101. if (LockedData.SampleTracker[handle].Active || LockedData.SampleTracker[handle].Loading) {
  1102. LockedData.SampleTracker[handle].Active = FALSE;
  1103. if (!LockedData.SampleTracker[handle].IsScore) {
  1104. LockedData.SampleTracker[handle].Original = NULL;
  1105. }
  1106. LockedData.SampleTracker[handle].Priority = 0;
  1107. /*
  1108. ** Stop the sample if it is playing.
  1109. */
  1110. if (!LockedData.SampleTracker[handle].Loading) {
  1111. LockedData.SampleTracker[handle].PlayBuffer->Stop();
  1112. }
  1113. LockedData.SampleTracker[handle].Loading = FALSE;
  1114. /*
  1115. ** If this is a streaming sample, then close the source file.
  1116. */
  1117. if (LockedData.SampleTracker[handle].FileHandle != WW_ERROR) {
  1118. Close_File(LockedData.SampleTracker[handle].FileHandle);
  1119. LockedData.SampleTracker[handle].FileHandle = WW_ERROR;
  1120. }
  1121. LockedData.SampleTracker[handle].QueueBuffer = NULL;
  1122. }
  1123. LeaveCriticalSection (&GlobalAudioCriticalSection);
  1124. }
  1125. }
  1126. /***********************************************************************************************
  1127. * Sample_Status -- Queries the current playing sample status (if any). *
  1128. * *
  1129. * *
  1130. * *
  1131. * INPUT: *
  1132. * *
  1133. * OUTPUT: *
  1134. * *
  1135. * WARNINGS: *
  1136. * *
  1137. * HISTORY: *
  1138. * 06/02/1992 JLB : Created. *
  1139. *=============================================================================================*/
  1140. BOOL Sample_Status(int handle)
  1141. {
  1142. DWORD status;
  1143. if (AudioDone) return (FALSE);
  1144. /*
  1145. ** If its an invalid handle or we do not have a sound driver then
  1146. ** the sample in question is not playing.
  1147. */
  1148. if (LockedData.DigiHandle == -1 || (unsigned)handle >= MAX_SFX) return(FALSE);
  1149. /*
  1150. ** If the sample is loading, then for all intents and purposes the
  1151. ** sample is playing.
  1152. */
  1153. if (LockedData.SampleTracker[handle].Loading) return(TRUE);
  1154. /*
  1155. ** If the sample is not active, then it is not playing
  1156. */
  1157. if (!LockedData.SampleTracker[handle].Active) return(FALSE);
  1158. /*
  1159. ** If we made it this far, then the Sample is still playing if sos says
  1160. ** that it is.
  1161. */
  1162. DumpBuffer = LockedData.SampleTracker[handle].PlayBuffer;
  1163. if (LockedData.SampleTracker[handle].PlayBuffer->GetStatus( &status ) == DS_OK){
  1164. return ( (DSBSTATUS_PLAYING & status) || (DSBSTATUS_LOOPING & status) );
  1165. }else{
  1166. return (TRUE);
  1167. }
  1168. }
  1169. /***********************************************************************************************
  1170. * Is_Sample_Playing -- returns the play state of a sample *
  1171. * *
  1172. * *
  1173. * *
  1174. * INPUT: ptr to sample data *
  1175. * *
  1176. * OUTPUT: TRUE if sample is currently playing *
  1177. * *
  1178. * WARNINGS: None *
  1179. * *
  1180. * HISTORY: *
  1181. * 11/2/95 4:11PM ST : Commented *
  1182. *=============================================================================================*/
  1183. BOOL Is_Sample_Playing(void const * sample)
  1184. {
  1185. int index;
  1186. if (AudioDone) return (FALSE);
  1187. //EnterCriticalSection(&GlobalAudioCriticalSection);
  1188. if (!sample) {
  1189. //LeaveCriticalSection(&GlobalAudioCriticalSection);
  1190. return FALSE;
  1191. }
  1192. for (index = 0; index < MAX_SFX; index++) {
  1193. if (LockedData.SampleTracker[index].Original == sample && Sample_Status(index)) {
  1194. //LeaveCriticalSection(&GlobalAudioCriticalSection);
  1195. return (TRUE);
  1196. }
  1197. }
  1198. //LeaveCriticalSection(&GlobalAudioCriticalSection);
  1199. return (FALSE);
  1200. }
  1201. /***********************************************************************************************
  1202. * Stop_Sample_Playing -- stops a playing sample *
  1203. * *
  1204. * *
  1205. * *
  1206. * INPUT: ptr to sample data *
  1207. * *
  1208. * OUTPUT: Nothing *
  1209. * *
  1210. * WARNINGS: None *
  1211. * *
  1212. * HISTORY: *
  1213. * 11/2/95 4:13PM ST : Commented *
  1214. *=============================================================================================*/
  1215. void Stop_Sample_Playing(void const * sample)
  1216. {
  1217. int index;
  1218. if (sample) {
  1219. for (index = 0; index < MAX_SFX; index++) {
  1220. if (LockedData.SampleTracker[index].Original == sample) {
  1221. Stop_Sample(index);
  1222. break;
  1223. }
  1224. }
  1225. }
  1226. }
  1227. /***********************************************************************************************
  1228. * Get_Free_Sample_Handle -- finds a free slot in which to play a new sample *
  1229. * *
  1230. * *
  1231. * *
  1232. * INPUT: priority of sample we want to play *
  1233. * *
  1234. * OUTPUT: Handle or -1 if none free *
  1235. * *
  1236. * WARNINGS: None *
  1237. * *
  1238. * HISTORY: *
  1239. * 11/2/95 4:14PM ST : Added function header *
  1240. *=============================================================================================*/
  1241. int Get_Free_Sample_Handle(int priority)
  1242. {
  1243. int id;
  1244. /*
  1245. ** Find a free SFX holding buffer slot.
  1246. */
  1247. for (id = MAX_SFX - 1; id >= 0; id--) {
  1248. if (!LockedData.SampleTracker[id].Active && !LockedData.SampleTracker[id].Loading) {
  1249. if (!StartingFileStream && LockedData.SampleTracker[id].IsScore) {
  1250. StartingFileStream = TRUE; // Ensures only one channel is kept free for scores.
  1251. continue;
  1252. }
  1253. break;
  1254. }
  1255. }
  1256. if (id < 0) {
  1257. for (id = 0; id < MAX_SFX; id++) {
  1258. if (LockedData.SampleTracker[id].Priority <= priority) break;
  1259. }
  1260. if (id == MAX_SFX) {
  1261. return(-1); // Cannot play!
  1262. }
  1263. Stop_Sample(id); // This sample gets clobbered.
  1264. }
  1265. if (id == -1) {
  1266. return -1;
  1267. }
  1268. if (LockedData.SampleTracker[id].FileHandle != WW_ERROR) {
  1269. Close_File(LockedData.SampleTracker[id].FileHandle);
  1270. LockedData.SampleTracker[id].FileHandle = WW_ERROR;
  1271. }
  1272. if (LockedData.SampleTracker[id].Original && !LockedData.SampleTracker[id].IsScore) {
  1273. LockedData.SampleTracker[id].Original = NULL;
  1274. }
  1275. LockedData.SampleTracker[id].IsScore = FALSE;
  1276. return(id);
  1277. }
  1278. int Play_Sample(void const *sample, int priority, int volume, signed short panloc)
  1279. {
  1280. return(Play_Sample_Handle(sample, priority, volume, panloc, Get_Free_Sample_Handle(priority)));
  1281. }
  1282. /***********************************************************************************************
  1283. * Attempt_Audio_Restore -- tries to restore the direct sound buffers *
  1284. * *
  1285. * *
  1286. * *
  1287. * INPUT: ptr to direct sound buffer *
  1288. * *
  1289. * OUTPUT: TRUE if buffer was successfully restored *
  1290. * *
  1291. * WARNINGS: None *
  1292. * *
  1293. * HISTORY: *
  1294. * 3/20/96 9:47AM ST : Created *
  1295. *=============================================================================================*/
  1296. BOOL Attempt_Audio_Restore (LPDIRECTSOUNDBUFFER sound_buffer)
  1297. {
  1298. int return_code;
  1299. DWORD play_status;
  1300. int restore_attempts=0;
  1301. if (AudioDone){
  1302. return (FALSE);
  1303. }
  1304. /*
  1305. ** Call the audio focus loss function if it has been set up
  1306. */
  1307. if (Audio_Focus_Loss_Function){
  1308. Audio_Focus_Loss_Function();
  1309. }
  1310. /*
  1311. ** Try to restore the sound buffer
  1312. */
  1313. do{
  1314. Restore_Sound_Buffers();
  1315. return_code = sound_buffer->GetStatus ( &play_status );
  1316. } while (restore_attempts++<2 && return_code == DSERR_BUFFERLOST);
  1317. return ((BOOL) ~(return_code == DSERR_BUFFERLOST));
  1318. }
  1319. /***********************************************************************************************
  1320. * Convert_HMI_To_Direct_Sound_Volume -- Converts a linear volume value into an expotential *
  1321. * value *
  1322. * *
  1323. * This function converts a linear C&C volume in the range 0-255 (255 loudest) to a direct *
  1324. * sound volume in the range 0 to -10000 (with 0 being the loadest) *
  1325. * *
  1326. * INPUT: volume in range 0-255 *
  1327. * *
  1328. * OUTPUT: volume in range -10000 to 0 *
  1329. * *
  1330. * WARNINGS: None *
  1331. * *
  1332. * Note: The 27.685 value comes from 255 divided by the log of 10001 *
  1333. * *
  1334. * HISTORY: *
  1335. * 9/18/96 11:36AM ST : Created *
  1336. *=============================================================================================*/
  1337. int Convert_HMI_To_Direct_Sound_Volume(int volume)
  1338. {
  1339. if (volume == 0) return -10000;
  1340. if (volume == 255) return 0;
  1341. float vol = (float)volume;
  1342. float retval = exp ( (255.0-vol)/27.68597374) -1;
  1343. return ((int)-retval);
  1344. }
  1345. /***********************************************************************************************
  1346. * Play_Sample_Vol -- Plays a digitized sample. *
  1347. * *
  1348. * Use this routine to play a previously loaded digitized sample. *
  1349. * *
  1350. * INPUT: sample -- Sample pointer as returned from Load_Sample. *
  1351. * *
  1352. * volume -- The volume to play (0..255 with 255=loudest). *
  1353. * *
  1354. * OUTPUT: none *
  1355. * *
  1356. * WARNINGS: none *
  1357. * *
  1358. * HISTORY: *
  1359. * 04/17/1992 JLB : Created. *
  1360. * 05/24/1992 JLB : Volume support -- Soundblaster Pro *
  1361. * 04/22/1994 JLB : Multiple sample playback rates. *
  1362. * 11/02/1995 ST : Windows Direct Sound support *
  1363. *=============================================================================================*/
  1364. extern BOOL Any_Locked(void);
  1365. int Play_Sample_Handle(void const *sample, int priority, int volume, signed short , int id)
  1366. {
  1367. AUDHeaderType RawHeader;
  1368. SampleTrackerType *st=NULL; // Working pointer to sample tracker structure.
  1369. LPVOID play_buffer_ptr; //pointer to locked direct sound buffer
  1370. LPVOID dummy_buffer_ptr; //dummy pointer to second area of locked direct sound buffer
  1371. DWORD lock_length1;
  1372. DWORD lock_length2;
  1373. DWORD play_status;
  1374. HRESULT return_code;
  1375. int retries=0;
  1376. if (Any_Locked()) return(0);
  1377. if (AudioDone) return (0);
  1378. st = &LockedData.SampleTracker[id];
  1379. //EnterCriticalSection (&GlobalAudioCriticalSection);
  1380. if (!sample || LockedData.DigiHandle == -1) {
  1381. //LeaveCriticalSection (&GlobalAudioCriticalSection);
  1382. return(-1);
  1383. }
  1384. if (id == -1) {
  1385. //LeaveCriticalSection (&GlobalAudioCriticalSection);
  1386. return -1;
  1387. }
  1388. /*
  1389. ** Fetch the control bytes from the start of the sample data.
  1390. */
  1391. Mem_Copy((void *)sample, (void *)&RawHeader, sizeof(RawHeader));
  1392. /*
  1393. ** Fudge the sample rate to 22k
  1394. */
  1395. if (RawHeader.Rate <24000 && RawHeader.Rate >20000) RawHeader.Rate = 22050;
  1396. /*
  1397. ** Prepare the sample tracker structure for processing of this
  1398. ** sample. Fill the structure with data that can be determined
  1399. ** before the sample is started.
  1400. */
  1401. if (!AudioDone){
  1402. EnterCriticalSection(&GlobalAudioCriticalSection);
  1403. }
  1404. st->Compression = (SCompressType) ((unsigned char)RawHeader.Compression);
  1405. st->Original = sample;
  1406. st->OriginalSize = RawHeader.Size + sizeof(RawHeader);
  1407. st->Priority = (short)priority;
  1408. st->DontTouch = TRUE;
  1409. st->Odd = 0;
  1410. st->Reducer = 0;
  1411. st->Restart = FALSE;
  1412. st->QueueBuffer = NULL;
  1413. st->QueueSize = NULL;
  1414. st->TrailerLen = 0;
  1415. st->Remainder = RawHeader.Size;
  1416. st->Source = Add_Long_To_Pointer((void *)sample, sizeof(RawHeader));
  1417. st->Service = FALSE;
  1418. if (!AudioDone){
  1419. LeaveCriticalSection(&GlobalAudioCriticalSection);
  1420. }
  1421. /*
  1422. ** If the code in question using HMI based compression then we need
  1423. ** to set up for uncompressing it.
  1424. */
  1425. if (st->Compression == SCOMP_SOS) {
  1426. st->sosinfo.wChannels = (RawHeader.Flags & AUD_FLAG_STEREO) ? 2 : 1;
  1427. st->sosinfo.wBitSize = (RawHeader.Flags & AUD_FLAG_16BIT) ? 16 : 8;
  1428. st->sosinfo.dwCompSize = RawHeader.Size;
  1429. st->sosinfo.dwUnCompSize = RawHeader.Size * ( st->sosinfo.wBitSize / 4 );
  1430. sosCODECInitStream(&st->sosinfo);
  1431. }
  1432. /*
  1433. ** If the sample rate , bits per sample or stereo capabilities of the buffer do not
  1434. ** match the sample then reallocate the direct sound buffer with the required capabilities
  1435. */
  1436. if ( ( RawHeader.Rate != st->PlaybackRate ) ||
  1437. ( ( RawHeader.Flags & AUD_FLAG_16BIT ) != ( st->BitSize & AUD_FLAG_16BIT ) ) ||
  1438. ( ( RawHeader.Flags & AUD_FLAG_STEREO) != ( st->Stereo & AUD_FLAG_STEREO ) ) ) {
  1439. st->Active=0;
  1440. st->Service=0;
  1441. st->MoreSource=0;
  1442. /*
  1443. ** Stop the sound buffer playing
  1444. */
  1445. DumpBuffer = st->PlayBuffer;
  1446. do {
  1447. return_code = st->PlayBuffer->GetStatus ( &play_status );
  1448. if (return_code==DSERR_BUFFERLOST){
  1449. if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1);
  1450. }
  1451. }while (return_code == DSERR_BUFFERLOST);
  1452. if (play_status & (DSBSTATUS_PLAYING | DSBSTATUS_LOOPING) ){
  1453. st->PlayBuffer->Stop();
  1454. if (return_code==DSERR_BUFFERLOST){
  1455. if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1);
  1456. }
  1457. }
  1458. st->PlayBuffer->Release();
  1459. st->PlayBuffer=NULL;
  1460. DsBuffFormat.nSamplesPerSec = (unsigned short int) RawHeader.Rate;
  1461. DsBuffFormat.nChannels = (RawHeader.Flags & AUD_FLAG_STEREO) ? 2 : 1 ;
  1462. DsBuffFormat.wBitsPerSample = (RawHeader.Flags & AUD_FLAG_16BIT) ? 16 : 8 ;
  1463. DsBuffFormat.nBlockAlign = (short) ((DsBuffFormat.wBitsPerSample/8) * DsBuffFormat.nChannels);
  1464. DsBuffFormat.nAvgBytesPerSec= DsBuffFormat.nSamplesPerSec * DsBuffFormat.nBlockAlign;
  1465. /*
  1466. ** Create the new sound buffer
  1467. */
  1468. return_code= SoundObject->CreateSoundBuffer (&BufferDesc , &st->PlayBuffer , NULL);
  1469. if (return_code==DSERR_BUFFERLOST){
  1470. if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1);
  1471. }
  1472. /*
  1473. ** Just return if the create failed unexpectedly
  1474. **
  1475. ** If we failed then flag the buffer as having an impossible format so it wont match
  1476. ** any sample. This will ensure that we try and create the buffer again next time its used.
  1477. */
  1478. if (return_code!=DS_OK && return_code!=DSERR_BUFFERLOST){
  1479. st->PlaybackRate = 0;
  1480. st->Stereo = 0;
  1481. st->BitSize = 0;
  1482. return(-1);
  1483. }
  1484. /*
  1485. ** Remember the format of the new buffer
  1486. */
  1487. st->PlaybackRate = RawHeader.Rate;
  1488. st->Stereo = RawHeader.Flags & AUD_FLAG_STEREO;
  1489. st->BitSize = RawHeader.Flags & AUD_FLAG_16BIT;
  1490. }
  1491. /*
  1492. ** Fill in 3/4 of the play buffer.
  1493. */
  1494. //
  1495. // Stop the sound buffer playing before we lock it
  1496. //
  1497. do {
  1498. DumpBuffer = st->PlayBuffer;
  1499. return_code = st->PlayBuffer->GetStatus ( &play_status );
  1500. if (return_code==DSERR_BUFFERLOST){
  1501. if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1);
  1502. }
  1503. } while (return_code==DSERR_BUFFERLOST);
  1504. if (play_status & (DSBSTATUS_PLAYING | DSBSTATUS_LOOPING) ){
  1505. st->Active=0;
  1506. st->Service=0;
  1507. st->MoreSource=0;
  1508. st->PlayBuffer->Stop();
  1509. }
  1510. //
  1511. // Lock the direct sound buffer so we can write to it
  1512. //
  1513. do {
  1514. return_code = st->PlayBuffer->Lock ( 0 ,
  1515. SECONDARY_BUFFER_SIZE,
  1516. &play_buffer_ptr,
  1517. &lock_length1,
  1518. &dummy_buffer_ptr,
  1519. &lock_length2,
  1520. 0 );
  1521. if (return_code==DSERR_BUFFERLOST){
  1522. if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1);
  1523. }
  1524. } while (return_code==DSERR_BUFFERLOST);
  1525. if (return_code != DS_OK) {
  1526. //LeaveCriticalSection (&GlobalAudioCriticalSection);
  1527. return (-1);
  1528. }
  1529. //
  1530. // Decompress the sample into the direct sound buffer
  1531. //
  1532. st->DestPtr=(void*)Sample_Copy ( st,
  1533. &st->Source,
  1534. &st->Remainder,
  1535. &st->QueueBuffer,
  1536. &st->QueueSize,
  1537. play_buffer_ptr,
  1538. SECONDARY_BUFFER_SIZE*1/4,
  1539. st->Compression,
  1540. &st->Trailer[0],
  1541. &st->TrailerLen);
  1542. if ( st->DestPtr==(void*) (SECONDARY_BUFFER_SIZE*1/4) ){
  1543. // Must be more data to copy so we dont need to zero the buffer
  1544. st->MoreSource=TRUE;
  1545. st->Service=TRUE;
  1546. st->OneShot=FALSE;
  1547. } else {
  1548. // Whole sample is in the buffer so flag that we dont need to
  1549. // copy more. Clear out the end of the buffer so that it
  1550. // goes quiet if we play past the end
  1551. st->MoreSource=FALSE;
  1552. st->OneShot=TRUE;
  1553. st->Service=TRUE; //We still need to service it so that we can stop it when
  1554. // it plays past the end of the sample data
  1555. //memset ( (char*)( (unsigned)play_buffer_ptr + (unsigned)st->DestPtr ), 0 , SECONDARY_BUFFER_SIZE - (unsigned)st->DestPtr );
  1556. memset ( (char*)( (unsigned)play_buffer_ptr + (unsigned)st->DestPtr ), 0 , SECONDARY_BUFFER_SIZE/4);
  1557. }
  1558. st->PlayBuffer->Unlock( play_buffer_ptr,
  1559. lock_length1,
  1560. dummy_buffer_ptr,
  1561. lock_length2);
  1562. /*
  1563. **
  1564. ** Set the volume of the sample.
  1565. **
  1566. */
  1567. st->Volume = (volume << 7);
  1568. do {
  1569. //return_code = st->PlayBuffer->SetVolume ( Convert_HMI_To_Direct_Sound_Volume(volume));
  1570. st->PlayBuffer->SetVolume ( Convert_HMI_To_Direct_Sound_Volume( ( LockedData.SoundVolume*volume)/256) );
  1571. //return_code = st->PlayBuffer->SetVolume (- ( ( (32768- ( (st->Volume >> 8) *LockedData.SoundVolume) )
  1572. // *1000) >>15 ) );
  1573. if (return_code==DSERR_BUFFERLOST){
  1574. if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1);
  1575. }
  1576. } while (return_code==DSERR_BUFFERLOST);
  1577. /*
  1578. ** Make sure the primary sound buffer is playing
  1579. */
  1580. if (!Start_Primary_Sound_Buffer(FALSE)){
  1581. //LeaveCriticalSection (&GlobalAudioCriticalSection);
  1582. return(-1);
  1583. }
  1584. /*
  1585. ** Set the buffers play pointer to the beginning of the buffer
  1586. */
  1587. do {
  1588. return_code = st->PlayBuffer->SetCurrentPosition (0);
  1589. if (return_code==DSERR_BUFFERLOST){
  1590. if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1);
  1591. }
  1592. } while (return_code==DSERR_BUFFERLOST);
  1593. /*
  1594. ** Start the sample playing now.
  1595. */
  1596. do
  1597. {
  1598. return_code = st->PlayBuffer->Play (0,0,DSBPLAY_LOOPING);
  1599. switch (return_code){
  1600. case DS_OK :
  1601. if (!AudioDone){
  1602. EnterCriticalSection (&GlobalAudioCriticalSection);
  1603. }
  1604. st->Active=TRUE;
  1605. st->Handle=(short)id;
  1606. st->DontTouch = FALSE;
  1607. if (!AudioDone){
  1608. LeaveCriticalSection (&GlobalAudioCriticalSection);
  1609. }
  1610. return (st->Handle);
  1611. case DSERR_BUFFERLOST :
  1612. if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1);
  1613. break;
  1614. default:
  1615. st->Active=FALSE;
  1616. //LeaveCriticalSection (&GlobalAudioCriticalSection);
  1617. return (st->Handle);
  1618. }
  1619. } while (return_code==DSERR_BUFFERLOST);
  1620. //LeaveCriticalSection (&GlobalAudioCriticalSection);
  1621. return (st->Handle);
  1622. }
  1623. /***********************************************************************************************
  1624. * Restore_Sound_Buffers -- restore the sound buffers *
  1625. * *
  1626. * *
  1627. * *
  1628. * INPUT: Nothing *
  1629. * *
  1630. * OUTPUT: Nothing *
  1631. * *
  1632. * WARNINGS: None *
  1633. * *
  1634. * HISTORY: *
  1635. * 11/3/95 3:53PM ST : Created *
  1636. *=============================================================================================*/
  1637. void Restore_Sound_Buffers ( void )
  1638. {
  1639. if (PrimaryBufferPtr){
  1640. PrimaryBufferPtr->Restore();
  1641. }
  1642. for ( int index = 0; index < MAX_SFX; index++) {
  1643. if (LockedData.SampleTracker[index].PlayBuffer){
  1644. LockedData.SampleTracker[index].PlayBuffer->Restore();
  1645. }
  1646. }
  1647. }
  1648. /***********************************************************************************************
  1649. * Set_Sound_Vol -- sets the overall volume for sampled sounds *
  1650. * *
  1651. * *
  1652. * *
  1653. * INPUT: volume *
  1654. * *
  1655. * OUTPUT: the previous volume setting *
  1656. * *
  1657. * WARNINGS: None *
  1658. * *
  1659. * HISTORY: *
  1660. * 11/2/95 4:19PM ST : Added function header *
  1661. *=============================================================================================*/
  1662. int Set_Sound_Vol(int volume)
  1663. {
  1664. int old;
  1665. old = LockedData.SoundVolume;
  1666. LockedData.SoundVolume = volume & 0xFF;
  1667. return(old);
  1668. }
  1669. /***********************************************************************************************
  1670. * Set_Score_Vol -- sets the overall volume for music scores *
  1671. * *
  1672. * *
  1673. * *
  1674. * INPUT: volume *
  1675. * *
  1676. * OUTPUT: the previous volume setting *
  1677. * *
  1678. * WARNINGS: None *
  1679. * *
  1680. * HISTORY: *
  1681. * 11/2/95 4:19PM ST : Added function header *
  1682. *=============================================================================================*/
  1683. int Set_Score_Vol(int volume)
  1684. {
  1685. int old;
  1686. SampleTrackerType *st; //ptr to SampleTracker structure
  1687. old = LockedData.ScoreVolume;
  1688. LockedData.ScoreVolume = volume & 0xFF;
  1689. for (int index=0 ; index<MAX_SFX ; index++){
  1690. st = &LockedData.SampleTracker[index];
  1691. if (st->IsScore && st->Active){
  1692. //st->PlayBuffer->SetVolume (- ( ( (32768- ( (st->Volume >> 8) *LockedData.ScoreVolume) )
  1693. // *1000) >>15 ) );
  1694. //st->PlayBuffer->SetVolume ( Convert_HMI_To_Direct_Sound_Volume ( st->Volume >>7 ) );
  1695. st->PlayBuffer->SetVolume ( Convert_HMI_To_Direct_Sound_Volume( ( LockedData.ScoreVolume*(st->Volume >>7))/256) );
  1696. }
  1697. }
  1698. return(old);
  1699. }
  1700. /***********************************************************************************************
  1701. * Fade_Sample -- Start a sample fading *
  1702. * *
  1703. * *
  1704. * *
  1705. * INPUT: Sample handle *
  1706. * fade rate *
  1707. * *
  1708. * OUTPUT: Nothing *
  1709. * *
  1710. * WARNINGS: None *
  1711. * *
  1712. * HISTORY: *
  1713. * 11/2/95 4:21PM ST : Added function header *
  1714. *=============================================================================================*/
  1715. void Fade_Sample(int handle, int ticks)
  1716. {
  1717. if (Sample_Status(handle)) {
  1718. if (!ticks || LockedData.SampleTracker[handle].Loading) {
  1719. Stop_Sample(handle);
  1720. } else {
  1721. SampleTrackerType * st;
  1722. st = &LockedData.SampleTracker[handle];
  1723. st->Reducer = (short) ((st->Volume / ticks)+1);
  1724. }
  1725. }
  1726. }
  1727. int Get_Digi_Handle(void)
  1728. {
  1729. return(LockedData.DigiHandle);
  1730. }
  1731. /***************************************************************************
  1732. * SAMPLE_LENGTH -- returns length of a sample in ticks *
  1733. * *
  1734. * INPUT: void const *sample - pointer to the sample to get length of. *
  1735. * *
  1736. * OUTPUT: long - length of the sample in ticks (60/sec) *
  1737. * *
  1738. * HISTORY: *
  1739. * 07/05/1995 PWG : Created. *
  1740. *=========================================================================*/
  1741. long Sample_Length(void const *sample)
  1742. {
  1743. AUDHeaderType RawHeader;
  1744. if (!sample) return(0);
  1745. Mem_Copy((void *)sample, (void *)&RawHeader, sizeof(RawHeader));
  1746. long time = RawHeader.UncompSize;
  1747. /*
  1748. ** If the sample is a 16 bit sample, then it will take only half
  1749. ** as long to play.
  1750. */
  1751. if (RawHeader.Flags & AUD_FLAG_16BIT) {
  1752. time >>= 1;
  1753. }
  1754. /*
  1755. ** If the sample is a stereo sample, then it will take only half
  1756. ** as long to play.
  1757. */
  1758. if (RawHeader.Flags & AUD_FLAG_STEREO) {
  1759. time >>= 1;
  1760. }
  1761. if (RawHeader.Rate/60) {
  1762. time /= (RawHeader.Rate/60);
  1763. }
  1764. return(time);
  1765. }
  1766. /***********************************************************************************************
  1767. * Start_Primary_Sound_Buffer -- start the primary sound buffer playing *
  1768. * *
  1769. * *
  1770. * *
  1771. * INPUT: Nothing *
  1772. * *
  1773. * OUTPUT: Nothing *
  1774. * *
  1775. * WARNINGS: None *
  1776. * *
  1777. * HISTORY: *
  1778. * 2/1/96 12:28PM ST : Created *
  1779. *=============================================================================================*/
  1780. extern BOOL GameInFocus;
  1781. BOOL Start_Primary_Sound_Buffer (BOOL forced)
  1782. {
  1783. DWORD status;
  1784. if (PrimaryBufferPtr && GameInFocus){
  1785. if (forced){
  1786. PrimaryBufferPtr->Play(0,0,DSBPLAY_LOOPING);
  1787. return (TRUE);
  1788. } else {
  1789. if (PrimaryBufferPtr->GetStatus (&status) == DS_OK){
  1790. if (! ((status & DSBSTATUS_PLAYING) || (status & DSBSTATUS_LOOPING))){
  1791. PrimaryBufferPtr->Play(0,0,DSBPLAY_LOOPING);
  1792. return (TRUE);
  1793. }else{
  1794. return (TRUE);
  1795. }
  1796. }
  1797. }
  1798. }
  1799. return (FALSE);
  1800. }
  1801. /***********************************************************************************************
  1802. * Stop_Primary_Sound_Buffer -- stops the primary sound buffer from playing. *
  1803. * *
  1804. * *
  1805. * *
  1806. * INPUT: Nothing *
  1807. * *
  1808. * OUTPUT: Nothing *
  1809. * *
  1810. * WARNINGS: This stops all sound playback *
  1811. * *
  1812. * HISTORY: *
  1813. * 2/1/96 12:28PM ST : Created *
  1814. *=============================================================================================*/
  1815. void Stop_Primary_Sound_Buffer (void)
  1816. {
  1817. if (PrimaryBufferPtr){
  1818. PrimaryBufferPtr->Stop();
  1819. PrimaryBufferPtr->Stop(); // Oh I
  1820. PrimaryBufferPtr->Stop(); // Hate Direct Sound
  1821. PrimaryBufferPtr->Stop(); // So much.....
  1822. }
  1823. for ( int index = 0; index < MAX_SFX; index++) {
  1824. Stop_Sample(index);
  1825. }
  1826. }
  1827. void Suspend_Audio_Thread(void)
  1828. {
  1829. if (SoundThreadActive){
  1830. //SuspendThread(SoundThreadHandle);
  1831. timeKillEvent(SoundTimerHandle);
  1832. SoundTimerHandle = NULL;
  1833. SoundThreadActive = FALSE;
  1834. }
  1835. }
  1836. void Resume_Audio_Thread(void)
  1837. {
  1838. if (!SoundThreadActive){
  1839. //ResumeThread(SoundThreadHandle);
  1840. SoundTimerHandle = timeSetEvent ( 1000/MAINTENANCE_RATE , 1 , Sound_Timer_Callback , 0 , TIME_PERIODIC);
  1841. SoundThreadActive = TRUE;
  1842. }
  1843. }