AUD_Cache.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. /*
  2. ** Command & Conquer Generals(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. ** Westwood Studios Pacific. **
  21. ** **
  22. ** Confidential Information **
  23. ** Copyright (C) 2000 - All Rights Reserved **
  24. ** **
  25. ******************************************************************************
  26. ** **
  27. ** Project: Dune Emperor **
  28. ** **
  29. ** Module: <module> (<prefix>_) **
  30. ** **
  31. ** Version: $ID$ **
  32. ** **
  33. ** File name: audcache.cpp **
  34. ** **
  35. ** Created by: 04/30/99 TR **
  36. ** **
  37. ** Description: <description> **
  38. ** **
  39. *****************************************************************************/
  40. /*****************************************************************************
  41. ** Includes **
  42. *****************************************************************************/
  43. #include <string.h>
  44. #include <memory.h>
  45. #include <wpaudio/altypes.h> // always include this header first
  46. #include <wpaudio/memory.h>
  47. #include <wpaudio/list.h>
  48. #include <wpaudio/source.h>
  49. #include <wpaudio/cache.h>
  50. #include <wpaudio/profiler.h>
  51. #include <wsys/File.h>
  52. // 'assignment within condition expression'.
  53. #pragma warning(disable : 4706)
  54. DBG_DECLARE_TYPE ( AudioCache );
  55. DBG_DECLARE_TYPE ( AudioCacheItem );
  56. /*****************************************************************************
  57. ** Externals **
  58. *****************************************************************************/
  59. /*****************************************************************************
  60. ** Defines **
  61. *****************************************************************************/
  62. #define DEBUG_CACHE 0
  63. /*****************************************************************************
  64. ** Private Types **
  65. *****************************************************************************/
  66. /*****************************************************************************
  67. ** Private Data **
  68. *****************************************************************************/
  69. /*****************************************************************************
  70. ** Public Data **
  71. *****************************************************************************/
  72. /*****************************************************************************
  73. ** Private Prototypes **
  74. *****************************************************************************/
  75. /*****************************************************************************
  76. ** Private Functions **
  77. *****************************************************************************/
  78. /******************************************************************/
  79. /* */
  80. /* */
  81. /******************************************************************/
  82. static void audioCacheAssetClose ( AudioCache *cache )
  83. {
  84. if ( cache->assetFile )
  85. {
  86. cache->assetFile->close();
  87. cache->assetFile = NULL;
  88. }
  89. cache->assetBytesLeft = 0;
  90. }
  91. /******************************************************************/
  92. /* */
  93. /* */
  94. /******************************************************************/
  95. static int audioCacheAssetOpen ( AudioCache *cache, const char *name )
  96. {
  97. audioCacheAssetClose ( cache );
  98. if ( name == NULL || cache->openAssetCB == NULL )
  99. {
  100. return FALSE;
  101. }
  102. cache->assetFile = cache->openAssetCB( name );
  103. if ( !cache->assetFile )
  104. {
  105. return FALSE;
  106. }
  107. if ( !AudioFormatReadWaveFile ( cache->assetFile, &cache->assetFormat, &cache->assetBytesLeft ))
  108. {
  109. audioCacheAssetClose ( cache );
  110. return FALSE;
  111. }
  112. return TRUE;
  113. }
  114. /******************************************************************/
  115. /* */
  116. /* */
  117. /******************************************************************/
  118. int audioCacheAssetRead ( AudioCache *cache, void *data, int bytes )
  119. {
  120. if ( bytes > cache->assetBytesLeft )
  121. {
  122. bytes = cache->assetBytesLeft;
  123. }
  124. return cache->assetFile ? cache->assetFile->read ( data, bytes ) : 0 ;
  125. }
  126. /*****************************************************************************
  127. ** Public Functions **
  128. *****************************************************************************/
  129. /******************************************************************/
  130. /* */
  131. /* */
  132. /******************************************************************/
  133. AudioCache* AudioCacheCreate ( int cacheSize, int maxItems, int frameSize )
  134. {
  135. AudioCache *cache;
  136. int frameBytes;
  137. int pages;
  138. ALLOC_STRUCT ( cache, AudioCache );
  139. DBG_SET_TYPE ( cache, AudioCache );
  140. cache->frameSize = frameSize;
  141. frameBytes = sizeof ( AudioFrame ) + frameSize;
  142. pages = cacheSize/frameBytes;
  143. cache->framePool = MemoryPoolCreate ( pages, frameBytes );
  144. cache->itemPool = MemoryPoolCreate ( maxItems, sizeof ( AudioCacheItem ) );
  145. ListInit ( &cache->items );
  146. cache->assetFile = NULL;
  147. AudioFormatInit ( &cache->assetFormat );
  148. ProfCacheInit ( &cache->profile, pages, frameSize );
  149. ProfCacheUpdateInterval ( &cache->profile, 10 ); // every ten milliseconds
  150. if ( !cache->framePool || !cache->itemPool )
  151. {
  152. AudioCacheDestroy ( cache );
  153. cache = NULL;
  154. }
  155. return cache;
  156. }
  157. /******************************************************************/
  158. /* */
  159. /* */
  160. /******************************************************************/
  161. void AudioCacheDestroy ( AudioCache *cache )
  162. {
  163. AudioCacheItem *item;
  164. DBG_ASSERT_TYPE ( cache, AudioCache );
  165. while ( ( item = (AudioCacheItem *) ListNodeNext ( &cache->items )) )
  166. {
  167. AudioCacheItemFree ( item );
  168. }
  169. if ( cache->framePool )
  170. {
  171. MemoryPoolDestroy ( cache->framePool );
  172. }
  173. if ( cache->itemPool )
  174. {
  175. MemoryPoolDestroy ( cache->itemPool );
  176. }
  177. DBG_INVALIDATE_TYPE ( cache );
  178. AudioMemFree ( cache );
  179. }
  180. /******************************************************************/
  181. /* */
  182. /* */
  183. /******************************************************************/
  184. AudioCacheItem* AudioCacheGetItem ( AudioCache *cache, const char *name )
  185. {
  186. AudioCacheItem *item, *head;
  187. DBG_ASSERT_TYPE ( cache, AudioCache );
  188. item = head = (AudioCacheItem *) &cache->items ;
  189. while ( (item = (AudioCacheItem *) item->nd.next ) != head )
  190. {
  191. if ( item->valid && item->name == name )
  192. {
  193. return item;
  194. }
  195. }
  196. return NULL;
  197. }
  198. /******************************************************************/
  199. /* */
  200. /* */
  201. /******************************************************************/
  202. void AudioCacheInvalidate ( AudioCache *cache )
  203. {
  204. AudioCacheItem *item, *head;
  205. DBG_ASSERT_TYPE ( cache, AudioCache );
  206. item = head = (AudioCacheItem *) &cache->items ;
  207. while ( (item = (AudioCacheItem *) item->nd.next ) != head )
  208. {
  209. item->valid = FALSE;
  210. }
  211. }
  212. /******************************************************************/
  213. /* */
  214. /* */
  215. /******************************************************************/
  216. AudioCacheOpenCB* AudioCacheSetOpenCB ( AudioCache *cache, AudioCacheOpenCB *cb )
  217. {
  218. DBG_ASSERT_TYPE ( cache, AudioCache );
  219. AudioCacheOpenCB *old = cache->openAssetCB;
  220. cache->openAssetCB = cb;
  221. return old;
  222. }
  223. /******************************************************************/
  224. /* */
  225. /* */
  226. /******************************************************************/
  227. AudioCacheItem* AudioCacheLoadItem ( AudioCache *cache, const char *name )
  228. {
  229. AudioCacheItem *item;
  230. int error;
  231. DBG_ASSERT_TYPE ( cache, AudioCache );
  232. if ( (item = AudioCacheGetItem ( cache, name )))
  233. {
  234. #if DEBUG_CACHE
  235. DBGPRINTF (("ACACHE: %10s - Cached\n", item->name ));
  236. #endif
  237. ListNodeRemove ( &item->nd );
  238. ListNodeAppend ( &cache->items, &item->nd );
  239. ProfCacheHit ( &cache->profile );
  240. return item;
  241. }
  242. ProfCacheMiss ( &cache->profile );
  243. // item is not in the cache so load it
  244. // see first if the sample exists
  245. #if DEBUG_CACHE
  246. DBGPRINTF (("ACACHE: %10s - Loading\n", name ));
  247. #endif
  248. ProfCacheLoadStart ( &cache->profile, 0 );
  249. if ( !audioCacheAssetOpen( cache, name ))
  250. {
  251. #if DEBUG_CACHE
  252. DBGPRINTF (("does not exist\n"));
  253. #endif
  254. goto none;
  255. }
  256. item = (AudioCacheItem *) MemoryPoolGetItem ( cache->itemPool );
  257. if ( !item )
  258. {
  259. // free the oldest item so that we can use it's item struct
  260. AudioCacheFreeOldestItem ( cache );
  261. item = (AudioCacheItem *) MemoryPoolGetItem ( cache->itemPool );
  262. if ( !item )
  263. {
  264. // the oldest item could not be freed because it was still playing
  265. DBGPRINTF (("Audio cache overflow\n"));
  266. audioCacheAssetClose ( cache );
  267. goto none;
  268. }
  269. }
  270. // prepare item for use
  271. item->name = name;
  272. item->cache = cache;
  273. ListNodeInit ( &item->nd );
  274. LockInit ( &item->lock );
  275. AudioSampleInit ( &item->sample );
  276. AudioSampleSetName ( &item->sample, name );
  277. AudioFormatInit ( &item->format );
  278. item->sample.Format = &item->format;
  279. DBG_SET_TYPE ( item, AudioCacheItem );
  280. error = FALSE;
  281. // ok load sample data in to cache
  282. {
  283. int bytesToTransfer;
  284. int bytes;
  285. int bytesTransfered;
  286. bytesToTransfer = cache->assetBytesLeft;
  287. while ( bytesToTransfer )
  288. {
  289. AudioFrame *frame;
  290. void *data;
  291. if ( (bytes = cache->frameSize ) > bytesToTransfer )
  292. {
  293. bytes = bytesToTransfer;
  294. }
  295. while ( ! (frame = (AudioFrame *) MemoryPoolGetItem ( cache->framePool )))
  296. {
  297. if ( !AudioCacheFreeOldestItem ( cache ) )
  298. {
  299. break;
  300. }
  301. }
  302. if ( !frame )
  303. {
  304. error = TRUE;
  305. break;
  306. }
  307. data = (void *) ( (uint) frame + sizeof ( AudioFrame ));
  308. AudioFrameInit ( frame, data, bytes );
  309. AudioSampleAddFrame ( &item->sample, frame );
  310. bytesTransfered = audioCacheAssetRead ( cache, data, bytes );
  311. ProfCacheAddLoadBytes ( &cache->profile, bytesTransfered );
  312. ProfCacheAddPage ( &cache->profile );
  313. ProfCacheFill ( &cache->profile, bytesTransfered );
  314. if ( bytesTransfered != bytes )
  315. {
  316. error = TRUE;
  317. break;
  318. }
  319. bytesToTransfer -= bytesTransfered;
  320. }
  321. }
  322. if ( error )
  323. {
  324. #if DEBUG_CACHE
  325. DBGPRINTF (("FAILED\n"));
  326. #endif
  327. AudioCacheItemFree ( item );
  328. goto none;
  329. }
  330. #if DEBUG_CACHE
  331. DBGPRINTF (("done\n"));
  332. #endif
  333. // update the format structure
  334. memcpy ( &item->format, &cache->assetFormat, sizeof ( AudioFormat) );
  335. ListNodeAppend ( &cache->items, &item->nd );
  336. item->valid = TRUE;
  337. audioCacheAssetClose ( cache );
  338. ProfCacheLoadEnd ( &cache->profile );
  339. return item;
  340. none:
  341. ProfCacheLoadEnd ( &cache->profile );
  342. return NULL;
  343. }
  344. /******************************************************************/
  345. /* */
  346. /* */
  347. /******************************************************************/
  348. // AudioCacheFreeOldestItem()
  349. // Free the oldest UNUSED item in the list. These should tend to be
  350. // towards the end of the list, as old events should expire and unlock.
  351. // But not always (e.g. loops), so don't count on it.
  352. int AudioCacheFreeOldestItem( AudioCache *cache )
  353. {
  354. AudioCacheItem *item;
  355. DBG_ASSERT_TYPE( cache, AudioCache );
  356. item = (AudioCacheItem*) ListNodePrev( &cache->items );
  357. while ( item )
  358. {
  359. if ( !AudioCacheItemInUse( item ) )
  360. {
  361. AudioCacheItemFree( item );
  362. return TRUE;
  363. }
  364. item = (AudioCacheItem*) ListNodePrev( (ListNode*) item );
  365. }
  366. return FALSE;
  367. }
  368. /******************************************************************/
  369. /* */
  370. /* */
  371. /******************************************************************/
  372. void AudioCacheItemLock ( AudioCacheItem *item )
  373. {
  374. DBG_ASSERT_TYPE ( item, AudioCacheItem );
  375. LockAcquire ( &item->lock );
  376. }
  377. /******************************************************************/
  378. /* */
  379. /* */
  380. /******************************************************************/
  381. void AudioCacheItemUnlock ( AudioCacheItem *item )
  382. {
  383. DBG_ASSERT_TYPE ( item, AudioCacheItem );
  384. LockRelease ( &item->lock );
  385. }
  386. /******************************************************************/
  387. /* */
  388. /* */
  389. /******************************************************************/
  390. int AudioCacheItemInUse ( AudioCacheItem *item )
  391. {
  392. DBG_ASSERT_TYPE ( item, AudioCacheItem );
  393. return Locked ( &item->lock );
  394. }
  395. /******************************************************************/
  396. /* */
  397. /* */
  398. /******************************************************************/
  399. void AudioCacheItemFree ( AudioCacheItem *item )
  400. {
  401. DBG_ASSERT_TYPE ( item, AudioCacheItem );
  402. if ( ListNodeInList ( &item->nd ))
  403. {
  404. ListNodeRemove ( &item->nd );
  405. }
  406. DBG_MSGASSERT ( !AudioCacheItemInUse ( item ), ("cache item is still in use"));
  407. #if DEBUG_CACHE
  408. DBGPRINTF (("ACACHE: %10s - Freeing\n", AudioBagGetItemName ( item->cache->bag, item->id )));
  409. #endif
  410. // return frames to frame pool
  411. {
  412. AudioFrame *frame;
  413. while ( (frame = (AudioFrame *) ListNodeNext ( &item->sample.Frames )) )
  414. {
  415. ListNodeRemove ( &frame->nd );
  416. ProfCacheRemove ( &item->cache->profile, frame->Bytes );
  417. AudioFrameDeinit ( frame );
  418. ProfCacheRemovePage ( &item->cache->profile );
  419. MemoryPoolReturnItem ( item->cache->framePool, frame );
  420. }
  421. }
  422. AudioSampleDeinit ( &item->sample );
  423. DBG_INVALIDATE_TYPE ( item );
  424. MemoryPoolReturnItem ( item->cache->itemPool, item );
  425. }
  426. /******************************************************************/
  427. /* */
  428. /* */
  429. /******************************************************************/
  430. AudioSample* AudioCacheItemSample ( AudioCacheItem *item )
  431. {
  432. DBG_ASSERT_TYPE ( item, AudioCacheItem );
  433. return &item->sample;
  434. }