Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2394 lines
70 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "audio_pch.h"
  9. #include "datacache/idatacache.h"
  10. #include "utllinkedlist.h"
  11. #include "utldict.h"
  12. #include "filesystem/IQueuedLoader.h"
  13. #include "cdll_int.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. extern IVEngineClient *engineClient;
  17. extern IFileSystem *g_pFileSystem;
  18. extern IDataCache *g_pDataCache;
  19. extern double realtime;
  20. // console streaming buffer implementation, appropriate for high latency and low memory
  21. // shift this many buffers through the wave
  22. #define STREAM_BUFFER_COUNT 2
  23. // duration of audio samples per buffer, 200ms is 2x the worst frame rate (10Hz)
  24. // the engine then has at least 400ms to deliver a new buffer or pop (assuming 2 buffers)
  25. #define STREAM_BUFFER_TIME 0.200f
  26. // force a single buffer when streaming waves smaller than this
  27. #define STREAM_BUFFER_DATASIZE XBOX_DVD_ECC_SIZE
  28. // PC single buffering implementation
  29. // UNDONE: Allocate this in cache instead?
  30. #define SINGLE_BUFFER_SIZE 16384
  31. // Force a small cache for debugging cache issues.
  32. // #define FORCE_SMALL_MEMORY_CACHE_SIZE ( 6 * 1024 * 1024 )
  33. #define DEFAULT_WAV_MEMORY_CACHE ( 16 * 1024 * 1024 )
  34. #define DEFAULT_XBOX_WAV_MEMORY_CACHE ( 16 * 1024 * 1024 )
  35. #define TF_XBOX_WAV_MEMORY_CACHE ( 24 * 1024 * 1024 ) // Team Fortress uses a larger cache
  36. // Dev builds will be missing soundcaches and hitch sometimes, we only care if its being properly launched from steam where sound caches should be complete.
  37. ConVar snd_async_spew_blocking( "snd_async_spew_blocking", "1", 0, "Spew message to console any time async sound loading blocks on file i/o. ( 0=Off, 1=With -steam only, 2=Always" );
  38. ConVar snd_async_spew( "snd_async_spew", "0", 0, "Spew all async sound reads, including success" );
  39. ConVar snd_async_fullyasync( "snd_async_fullyasync", "0", 0, "All playback is fully async (sound doesn't play until data arrives)." );
  40. ConVar snd_async_stream_spew( "snd_async_stream_spew", "0", 0, "Spew streaming info ( 0=Off, 1=streams, 2=buffers" );
  41. static bool SndAsyncSpewBlocking()
  42. {
  43. int pref = snd_async_spew_blocking.GetInt();
  44. return ( pref >= 2 ) || ( pref == 1 && CommandLine()->FindParm( "-steam" ) != 0 );
  45. }
  46. #define SndAlignReads() 1
  47. void MaybeReportMissingWav( char const *wav );
  48. //-----------------------------------------------------------------------------
  49. // Purpose:
  50. //-----------------------------------------------------------------------------
  51. struct asyncwaveparams_t
  52. {
  53. asyncwaveparams_t() : bPrefetch( false ), bCanBeQueued( false ) {}
  54. FileNameHandle_t hFilename; // handle to sound item name (i.e. not with sound\ prefix)
  55. int datasize;
  56. int seekpos;
  57. int alignment;
  58. bool bPrefetch;
  59. bool bCanBeQueued;
  60. };
  61. //-----------------------------------------------------------------------------
  62. // Purpose: Builds a cache of the data bytes for a specific .wav file
  63. //-----------------------------------------------------------------------------
  64. class CAsyncWaveData
  65. {
  66. public:
  67. explicit CAsyncWaveData();
  68. // APIS required by CManagedDataCacheClient
  69. void DestroyResource();
  70. CAsyncWaveData *GetData();
  71. unsigned int Size();
  72. static void AsyncCallback( const FileAsyncRequest_t &asyncRequest, int numReadBytes, FSAsyncStatus_t err );
  73. static void QueuedLoaderCallback( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError );
  74. static CAsyncWaveData *CreateResource( const asyncwaveparams_t &params );
  75. static unsigned int EstimatedSize( const asyncwaveparams_t &params );
  76. void OnAsyncCompleted( const FileAsyncRequest_t* asyncFilePtr, int numReadBytes, FSAsyncStatus_t err );
  77. bool BlockingCopyData( void *destbuffer, int destbufsize, int startoffset, int count );
  78. bool BlockingGetDataPointer( void **ppData );
  79. void SetAsyncPriority( int priority );
  80. void StartAsyncLoading( const asyncwaveparams_t& params );
  81. bool GetPostProcessed();
  82. void SetPostProcessed( bool proc );
  83. bool IsCurrentlyLoading();
  84. char const *GetFileName();
  85. // Data
  86. public:
  87. int m_nDataSize; // bytes requested
  88. int m_nReadSize; // bytes actually read
  89. void *m_pvData; // target buffer
  90. byte *m_pAlloc; // memory of buffer (base may not match)
  91. FileAsyncRequest_t m_async;
  92. FSAsyncControl_t m_hAsyncControl;
  93. float m_start; // time at request invocation
  94. float m_arrival; // time at data arrival
  95. FileNameHandle_t m_hFileNameHandle;
  96. int m_nBufferBytes; // size of any pre-allocated target buffer
  97. BufferHandle_t m_hBuffer; // used to dequeue the buffer after lru
  98. unsigned int m_bLoaded : 1;
  99. unsigned int m_bMissing : 1;
  100. unsigned int m_bPostProcessed : 1;
  101. };
  102. //-----------------------------------------------------------------------------
  103. // Purpose: C'tor
  104. //-----------------------------------------------------------------------------
  105. CAsyncWaveData::CAsyncWaveData() :
  106. m_nDataSize( 0 ),
  107. m_nReadSize( 0 ),
  108. m_pvData( 0 ),
  109. m_pAlloc( 0 ),
  110. m_hBuffer( INVALID_BUFFER_HANDLE ),
  111. m_nBufferBytes( 0 ),
  112. m_hAsyncControl( NULL ),
  113. m_bLoaded( false ),
  114. m_bMissing( false ),
  115. m_start( 0.0 ),
  116. m_arrival( 0.0 ),
  117. m_bPostProcessed( false ),
  118. m_hFileNameHandle( 0 )
  119. {
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Purpose: // APIS required by CDataLRU
  123. //-----------------------------------------------------------------------------
  124. void CAsyncWaveData::DestroyResource()
  125. {
  126. if ( IsPC() )
  127. {
  128. if ( m_hAsyncControl )
  129. {
  130. if ( !m_bLoaded && !m_bMissing )
  131. {
  132. // NOTE: We CANNOT call AsyncAbort since if the file is actually being read we'll end
  133. // up still getting a callback, but our this ptr (deleted below) will be feeefeee and we'll trash the heap
  134. // pretty bad. So we call AsyncFinish, which will do a blocking read and will definitely succeed
  135. // Block until we are finished
  136. g_pFileSystem->AsyncFinish( m_hAsyncControl, true );
  137. }
  138. g_pFileSystem->AsyncRelease( m_hAsyncControl );
  139. m_hAsyncControl = NULL;
  140. }
  141. }
  142. if ( IsX360() )
  143. {
  144. if ( m_hAsyncControl )
  145. {
  146. if ( !m_bLoaded && !m_bMissing )
  147. {
  148. // force an abort
  149. int errStatus = g_pFileSystem->AsyncAbort( m_hAsyncControl );
  150. if ( errStatus != FSASYNC_ERR_UNKNOWNID )
  151. {
  152. // must wait for abort to finish before deallocating data
  153. g_pFileSystem->AsyncFinish( m_hAsyncControl, true );
  154. }
  155. }
  156. g_pFileSystem->AsyncRelease( m_hAsyncControl );
  157. m_hAsyncControl = NULL;
  158. }
  159. if ( m_hBuffer != INVALID_BUFFER_HANDLE )
  160. {
  161. // hint the manager that this tracked buffer is invalid
  162. wavedatacache->MarkBufferDiscarded( m_hBuffer );
  163. }
  164. }
  165. // delete buffers
  166. if ( IsPC() || !IsX360() )
  167. {
  168. g_pFileSystem->FreeOptimalReadBuffer( m_pAlloc );
  169. }
  170. else
  171. {
  172. delete [] m_pAlloc;
  173. }
  174. delete this;
  175. }
  176. //-----------------------------------------------------------------------------
  177. // Purpose:
  178. // Output : char const
  179. //-----------------------------------------------------------------------------
  180. char const *CAsyncWaveData::GetFileName()
  181. {
  182. static char sz[MAX_PATH];
  183. if ( m_hFileNameHandle )
  184. {
  185. if ( g_pFileSystem->String( m_hFileNameHandle, sz, sizeof( sz ) ) )
  186. {
  187. return sz;
  188. }
  189. }
  190. Assert( 0 );
  191. return "";
  192. }
  193. //-----------------------------------------------------------------------------
  194. // Purpose:
  195. // Output : CAsyncWaveData
  196. //-----------------------------------------------------------------------------
  197. CAsyncWaveData *CAsyncWaveData::GetData()
  198. {
  199. return this;
  200. }
  201. //-----------------------------------------------------------------------------
  202. // Purpose:
  203. // Output : unsigned int
  204. //-----------------------------------------------------------------------------
  205. unsigned int CAsyncWaveData::Size()
  206. {
  207. int size = sizeof( *this );
  208. if ( IsPC() )
  209. {
  210. size += m_nDataSize;
  211. }
  212. if ( IsX360() )
  213. {
  214. // the data size can shrink during streaming near end of file
  215. // need the real contant size of this object's allocations
  216. size += m_nBufferBytes;
  217. }
  218. return size;
  219. }
  220. //-----------------------------------------------------------------------------
  221. // Purpose: Static method for CDataLRU
  222. // Input : &params -
  223. // Output : CAsyncWaveData
  224. //-----------------------------------------------------------------------------
  225. CAsyncWaveData *CAsyncWaveData::CreateResource( const asyncwaveparams_t &params )
  226. {
  227. CAsyncWaveData *pData = new CAsyncWaveData;
  228. Assert( pData );
  229. if ( pData )
  230. {
  231. if ( IsX360() )
  232. {
  233. // create buffer now for re-use during streaming process
  234. pData->m_nBufferBytes = AlignValue( params.datasize, params.alignment );
  235. pData->m_pAlloc = new byte[pData->m_nBufferBytes];
  236. pData->m_pvData = pData->m_pAlloc;
  237. }
  238. pData->StartAsyncLoading( params );
  239. }
  240. return pData;
  241. }
  242. //-----------------------------------------------------------------------------
  243. // Purpose: Static method
  244. // Input : &params -
  245. // Output : static unsigned int
  246. //-----------------------------------------------------------------------------
  247. unsigned int CAsyncWaveData::EstimatedSize( const asyncwaveparams_t &params )
  248. {
  249. int size = sizeof( CAsyncWaveData );
  250. if ( IsPC() )
  251. {
  252. size += params.datasize;
  253. }
  254. if ( IsX360() )
  255. {
  256. // the expected size of this object's allocations
  257. size += AlignValue( params.datasize, params.alignment );
  258. }
  259. return size;
  260. }
  261. //-----------------------------------------------------------------------------
  262. // Purpose: Static method, called by thread, don't call anything non-threadsafe from handler!!!
  263. // Input : asyncFilePtr -
  264. // numReadBytes -
  265. // err -
  266. //-----------------------------------------------------------------------------
  267. void CAsyncWaveData::AsyncCallback(const FileAsyncRequest_t &asyncRequest, int numReadBytes, FSAsyncStatus_t err )
  268. {
  269. CAsyncWaveData *pObject = reinterpret_cast< CAsyncWaveData * >( asyncRequest.pContext );
  270. Assert( pObject );
  271. if ( pObject )
  272. {
  273. pObject->OnAsyncCompleted( &asyncRequest, numReadBytes, err );
  274. }
  275. }
  276. //-----------------------------------------------------------------------------
  277. // Purpose: Static method, called by thread, don't call anything non-threadsafe from handler!!!
  278. //-----------------------------------------------------------------------------
  279. void CAsyncWaveData::QueuedLoaderCallback( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError )
  280. {
  281. CAsyncWaveData *pObject = reinterpret_cast< CAsyncWaveData * >( pContext );
  282. Assert( pObject );
  283. pObject->OnAsyncCompleted( NULL, nSize, loaderError == LOADERERROR_NONE ? FSASYNC_OK : FSASYNC_ERR_FILEOPEN );
  284. }
  285. //-----------------------------------------------------------------------------
  286. // Purpose: NOTE: THIS IS CALLED FROM A THREAD SO YOU CAN'T CALL INTO ANYTHING NON-THREADSAFE
  287. // such as CUtlSymbolTable/CUtlDict (many of the CUtl* are non-thread safe)!!!
  288. // Input : asyncFilePtr -
  289. // numReadBytes -
  290. // err -
  291. //-----------------------------------------------------------------------------
  292. void CAsyncWaveData::OnAsyncCompleted( const FileAsyncRequest_t *asyncFilePtr, int numReadBytes, FSAsyncStatus_t err )
  293. {
  294. if ( IsPC() )
  295. {
  296. // Take hold of pointer (we can just use delete[] across .dlls because we are using a shared memory allocator...)
  297. if ( err == FSASYNC_OK || err == FSASYNC_ERR_READING )
  298. {
  299. m_arrival = ( float )Plat_FloatTime();
  300. // Take over ptr
  301. m_pAlloc = ( byte * )asyncFilePtr->pData;
  302. if ( SndAlignReads() )
  303. {
  304. m_async.nOffset = ( m_async.nBytes - m_nDataSize );
  305. m_async.nBytes -= m_async.nOffset;
  306. m_pvData = ((byte *)m_pAlloc) + m_async.nOffset;
  307. m_nReadSize = numReadBytes - m_async.nOffset;
  308. }
  309. else
  310. {
  311. m_pvData = m_pAlloc;
  312. m_nReadSize = numReadBytes;
  313. }
  314. // Needs to be post-processed
  315. m_bPostProcessed = false;
  316. // Finished loading
  317. m_bLoaded = true;
  318. }
  319. else if ( err == FSASYNC_ERR_FILEOPEN )
  320. {
  321. // SEE NOTE IN FUNCTION COMMENT ABOVE!!!
  322. // Tracker 22905, et al.
  323. // Because this api gets called from the other thread, don't spew warning here as it can
  324. // cause a crash in searching CUtlSymbolTables since they use a global var for a LessFunc context!!!
  325. m_bMissing = true;
  326. }
  327. }
  328. if ( IsX360() )
  329. {
  330. m_arrival = (float)Plat_FloatTime();
  331. // possibly reading more than intended due to alignment restriction
  332. m_nReadSize = numReadBytes;
  333. if ( m_nReadSize > m_nDataSize )
  334. {
  335. // clamp to expected, extra data is unreliable
  336. m_nReadSize = m_nDataSize;
  337. }
  338. if ( err != FSASYNC_OK )
  339. {
  340. // track as any error
  341. m_bMissing = true;
  342. }
  343. if ( err != FSASYNC_ERR_FILEOPEN )
  344. {
  345. // some data got loaded
  346. m_bLoaded = true;
  347. }
  348. }
  349. }
  350. //-----------------------------------------------------------------------------
  351. // Purpose:
  352. // Input : *destbuffer -
  353. // destbufsize -
  354. // startoffset -
  355. // count -
  356. // Output : Returns true on success, false on failure.
  357. //-----------------------------------------------------------------------------
  358. bool CAsyncWaveData::BlockingCopyData( void *destbuffer, int destbufsize, int startoffset, int count )
  359. {
  360. if ( !m_bLoaded )
  361. {
  362. Assert( m_hAsyncControl );
  363. // Force it to finish
  364. // It could finish between the above line and here, but the AsyncFinish call will just have a bogus id, not a big deal
  365. if ( SndAsyncSpewBlocking() )
  366. {
  367. // Force it to finish
  368. float st = ( float )Plat_FloatTime();
  369. g_pFileSystem->AsyncFinish( m_hAsyncControl, true );
  370. float ed = ( float )Plat_FloatTime();
  371. Warning( "%f BCD: Async I/O Force %s (%8.2f msec / %8.2f msec total)\n", realtime, GetFileName(), 1000.0f * (float)( ed - st ), 1000.0f * (float)( m_arrival - m_start ) );
  372. }
  373. else
  374. {
  375. g_pFileSystem->AsyncFinish( m_hAsyncControl, true );
  376. }
  377. }
  378. // notify on any error
  379. if ( m_bMissing )
  380. {
  381. // Only warn once
  382. m_bMissing = false;
  383. char fn[MAX_PATH];
  384. if ( g_pFileSystem->String( m_hFileNameHandle, fn, sizeof( fn ) ) )
  385. {
  386. MaybeReportMissingWav( fn );
  387. }
  388. }
  389. if ( !m_bLoaded )
  390. {
  391. return false;
  392. }
  393. else if ( m_arrival != 0 && snd_async_spew.GetBool() )
  394. {
  395. DevMsg( "%f Async I/O Read successful %s (%8.2f msec)\n", realtime, GetFileName(), 1000.0f * (float)( m_arrival - m_start ) );
  396. m_arrival = 0;
  397. }
  398. // clamp requested to available
  399. if ( count > m_nReadSize )
  400. {
  401. count = m_nReadSize - startoffset;
  402. }
  403. if ( count < 0 )
  404. {
  405. return false;
  406. }
  407. // Copy data from stream buffer
  408. Q_memcpy( destbuffer, (char *)m_pvData + ( startoffset - m_async.nOffset ), count );
  409. g_pFileSystem->AsyncRelease( m_hAsyncControl );
  410. m_hAsyncControl = NULL;
  411. return true;
  412. }
  413. bool CAsyncWaveData::IsCurrentlyLoading()
  414. {
  415. if ( m_bLoaded )
  416. return true;
  417. FSAsyncStatus_t status = g_pFileSystem->AsyncStatus( m_hAsyncControl );
  418. if ( status == FSASYNC_STATUS_INPROGRESS || status == FSASYNC_OK )
  419. return true;
  420. return false;
  421. }
  422. //-----------------------------------------------------------------------------
  423. // Purpose:
  424. // Input : **ppData -
  425. // Output : Returns true on success, false on failure.
  426. //-----------------------------------------------------------------------------
  427. bool CAsyncWaveData::BlockingGetDataPointer( void **ppData )
  428. {
  429. Assert( ppData );
  430. if ( !m_bLoaded )
  431. {
  432. // Force it to finish
  433. // It could finish between the above line and here, but the AsyncFinish call will just have a bogus id, not a big deal
  434. if ( SndAsyncSpewBlocking() )
  435. {
  436. float st = ( float )Plat_FloatTime();
  437. g_pFileSystem->AsyncFinish( m_hAsyncControl, true );
  438. float ed = ( float )Plat_FloatTime();
  439. Warning( "%f BlockingGetDataPointer: Async I/O Force %s (%8.2f msec / %8.2f msec total )\n", realtime, GetFileName(), 1000.0f * (float)( ed - st ), 1000.0f * (float)( m_arrival - m_start ) );
  440. }
  441. else
  442. {
  443. g_pFileSystem->AsyncFinish( m_hAsyncControl, true );
  444. }
  445. }
  446. // notify on any error
  447. if ( m_bMissing )
  448. {
  449. // Only warn once
  450. m_bMissing = false;
  451. char fn[MAX_PATH];
  452. if ( g_pFileSystem->String( m_hFileNameHandle, fn, sizeof( fn ) ) )
  453. {
  454. MaybeReportMissingWav( fn );
  455. }
  456. }
  457. if ( !m_bLoaded )
  458. {
  459. return false;
  460. }
  461. else if ( m_arrival != 0 && snd_async_spew.GetBool() )
  462. {
  463. DevMsg( "%f Async I/O Read successful %s (%8.2f msec)\n", realtime, GetFileName(), 1000.0f * (float)( m_arrival - m_start ) );
  464. m_arrival = 0;
  465. }
  466. *ppData = m_pvData;
  467. g_pFileSystem->AsyncRelease( m_hAsyncControl );
  468. m_hAsyncControl = NULL;
  469. return true;
  470. }
  471. void CAsyncWaveData::SetAsyncPriority( int priority )
  472. {
  473. if ( m_async.priority != priority )
  474. {
  475. m_async.priority = priority;
  476. g_pFileSystem->AsyncSetPriority( m_hAsyncControl, m_async.priority );
  477. if ( snd_async_spew.GetBool() )
  478. {
  479. DevMsg( "%f Async I/O Bumped priority for %s (%8.2f msec)\n", realtime, GetFileName(), 1000.0f * (float)( Plat_FloatTime() - m_start ) );
  480. }
  481. }
  482. }
  483. //-----------------------------------------------------------------------------
  484. // Purpose:
  485. // Input : params -
  486. //-----------------------------------------------------------------------------
  487. void CAsyncWaveData::StartAsyncLoading( const asyncwaveparams_t& params )
  488. {
  489. Assert( IsX360() || ( IsPC() && !m_bLoaded ) );
  490. // expected to be relative to the sound\ dir
  491. m_hFileNameHandle = params.hFilename;
  492. // build the real filename
  493. char szFilename[MAX_PATH];
  494. Q_snprintf( szFilename, sizeof( szFilename ), "sound\\%s", GetFileName() );
  495. int nPriority = 1;
  496. if ( params.bPrefetch )
  497. {
  498. // lower the priority of prefetched sounds, so they don't block immediate sounds from being loaded
  499. nPriority = 0;
  500. }
  501. if ( !IsX360() )
  502. {
  503. m_async.pData = NULL;
  504. if ( SndAlignReads() )
  505. {
  506. m_async.nOffset = 0;
  507. m_async.nBytes = params.seekpos + params.datasize;
  508. }
  509. else
  510. {
  511. m_async.nOffset = params.seekpos;
  512. m_async.nBytes = params.datasize;
  513. }
  514. }
  515. else
  516. {
  517. Assert( params.datasize > 0 );
  518. // using explicit allocated buffer on xbox
  519. m_async.pData = m_pvData;
  520. m_async.nOffset = params.seekpos;
  521. m_async.nBytes = AlignValue( params.datasize, params.alignment );
  522. }
  523. m_async.pfnCallback = AsyncCallback; // optional completion callback
  524. m_async.pContext = (void *)this; // caller's unique context
  525. m_async.priority = nPriority; // inter list priority, 0=lowest
  526. m_async.flags = IsX360() ? 0 : FSASYNC_FLAGS_ALLOCNOFREE;
  527. m_async.pszPathID = "GAME";
  528. m_bLoaded = false;
  529. m_bMissing = false;
  530. m_nDataSize = params.datasize;
  531. m_start = (float)Plat_FloatTime();
  532. m_arrival = 0;
  533. m_nReadSize = 0;
  534. m_bPostProcessed = false;
  535. // The async layer creates a copy of this string, ok to send a local reference
  536. m_async.pszFilename = szFilename;
  537. char szFullName[MAX_PATH];
  538. if ( IsX360() && ( g_pFileSystem->GetDVDMode() == DVDMODE_STRICT ) )
  539. {
  540. // all audio is expected be in zips
  541. // resolve to absolute name now, where path can be filtered to just the zips (fast find, no real i/o)
  542. // otherwise the dvd will do a costly seek for each zip miss due to search path fall through
  543. PathTypeQuery_t pathType;
  544. if ( !g_pFileSystem->RelativePathToFullPath( m_async.pszFilename, m_async.pszPathID, szFullName, sizeof( szFullName ), FILTER_CULLNONPACK, &pathType ) )
  545. {
  546. // not found, do callback now to handle error
  547. m_async.pfnCallback( m_async, 0, FSASYNC_ERR_FILEOPEN );
  548. return;
  549. }
  550. m_async.pszFilename = szFullName;
  551. }
  552. if ( IsX360() && params.bCanBeQueued )
  553. {
  554. // queued loader takes over
  555. LoaderJob_t loaderJob;
  556. loaderJob.m_pFilename = m_async.pszFilename;
  557. loaderJob.m_pPathID = m_async.pszPathID;
  558. loaderJob.m_pCallback = QueuedLoaderCallback;
  559. loaderJob.m_pContext = (void *)this;
  560. loaderJob.m_Priority = LOADERPRIORITY_DURINGPRELOAD;
  561. loaderJob.m_pTargetData = m_async.pData;
  562. loaderJob.m_nBytesToRead = m_async.nBytes;
  563. loaderJob.m_nStartOffset = m_async.nOffset;
  564. g_pQueuedLoader->AddJob( &loaderJob );
  565. return;
  566. }
  567. MEM_ALLOC_CREDIT();
  568. // Commence async I/O
  569. Assert( !m_hAsyncControl );
  570. g_pFileSystem->AsyncRead( m_async, &m_hAsyncControl );
  571. }
  572. //-----------------------------------------------------------------------------
  573. // Purpose:
  574. // Output : Returns true on success, false on failure.
  575. //-----------------------------------------------------------------------------
  576. bool CAsyncWaveData::GetPostProcessed()
  577. {
  578. return m_bPostProcessed;
  579. }
  580. //-----------------------------------------------------------------------------
  581. // Purpose:
  582. // Input : proc -
  583. //-----------------------------------------------------------------------------
  584. void CAsyncWaveData::SetPostProcessed( bool proc )
  585. {
  586. m_bPostProcessed = proc;
  587. }
  588. //-----------------------------------------------------------------------------
  589. // Purpose: Implements a cache of .wav / .mp3 data based on filename
  590. //-----------------------------------------------------------------------------
  591. class CAsyncWavDataCache : public IAsyncWavDataCache,
  592. public CManagedDataCacheClient<CAsyncWaveData, asyncwaveparams_t>
  593. {
  594. public:
  595. CAsyncWavDataCache();
  596. ~CAsyncWavDataCache() {}
  597. virtual bool Init( unsigned int memSize );
  598. virtual void Shutdown();
  599. // implementation that treats file as monolithic
  600. virtual memhandle_t AsyncLoadCache( char const *filename, int datasize, int startpos, bool bIsPrefetch = false );
  601. virtual void PrefetchCache( char const *filename, int datasize, int startpos );
  602. virtual bool CopyDataIntoMemory( char const *filename, int datasize, int startpos, void *buffer, int bufsize, int copystartpos, int bytestocopy, bool *pbPostProcessed );
  603. virtual bool CopyDataIntoMemory( memhandle_t& handle, char const *filename, int datasize, int startpos, void *buffer, int bufsize, int copystartpos, int bytestocopy, bool *pbPostProcessed );
  604. virtual void SetPostProcessed( memhandle_t handle, bool proc );
  605. virtual void Unload( memhandle_t handle );
  606. virtual bool GetDataPointer( memhandle_t& handle, char const *filename, int datasize, int startpos, void **pData, int copystartpos, bool *pbPostProcessed );
  607. virtual bool IsDataLoadCompleted( memhandle_t handle, bool *pIsValid );
  608. virtual void RestartDataLoad( memhandle_t* handle, char const *filename, int datasize, int startpos );
  609. virtual bool IsDataLoadInProgress( memhandle_t handle );
  610. // Xbox: alternate multi-buffer streaming implementation
  611. virtual StreamHandle_t OpenStreamedLoad( char const *pFileName, int dataSize, int dataStart, int startPos, int loopPos, int bufferSize, int numBuffers, streamFlags_t flags );
  612. virtual void CloseStreamedLoad( StreamHandle_t hStream );
  613. virtual int CopyStreamedDataIntoMemory( StreamHandle_t hStream, void *pBuffer, int bufferSize, int copyStartPos, int bytesToCopy );
  614. virtual bool IsStreamedDataReady( StreamHandle_t hStream );
  615. virtual void MarkBufferDiscarded( BufferHandle_t hBuffer );
  616. virtual void *GetStreamedDataPointer( StreamHandle_t hStream, bool bSync );
  617. virtual void Flush();
  618. virtual void OnMixBegin();
  619. virtual void OnMixEnd();
  620. void QueueUnlock( const memhandle_t &handle );
  621. void SpewMemoryUsage( int level );
  622. // Cache helpers
  623. bool GetItemName( DataCacheClientID_t clientId, const void *pItem, char *pDest, unsigned nMaxLen );
  624. private:
  625. void Clear();
  626. struct CacheEntry_t
  627. {
  628. CacheEntry_t() :
  629. name( 0 ),
  630. handle( 0 )
  631. {
  632. }
  633. FileNameHandle_t name;
  634. memhandle_t handle;
  635. };
  636. // tags the signature of a buffer inside a rb tree for faster than linear find
  637. struct BufferEntry_t
  638. {
  639. FileNameHandle_t m_hName;
  640. memhandle_t m_hWaveData;
  641. int m_StartPos;
  642. bool m_bCanBeShared;
  643. };
  644. static bool BufferHandleLessFunc( const BufferEntry_t& lhs, const BufferEntry_t& rhs )
  645. {
  646. if ( lhs.m_hName != rhs.m_hName )
  647. {
  648. return lhs.m_hName < rhs.m_hName;
  649. }
  650. if ( lhs.m_StartPos != rhs.m_StartPos )
  651. {
  652. return lhs.m_StartPos < rhs.m_StartPos;
  653. }
  654. return lhs.m_bCanBeShared < rhs.m_bCanBeShared;
  655. }
  656. CUtlRBTree< BufferEntry_t, BufferHandle_t > m_BufferList;
  657. // encapsulates (n) buffers for a streamed wave object
  658. struct StreamedEntry_t
  659. {
  660. FileNameHandle_t m_hName;
  661. memhandle_t m_hWaveData[STREAM_BUFFER_COUNT];
  662. int m_Front; // buffer index, forever incrementing
  663. int m_NextStartPos; // predicted offset if mixing linearly
  664. int m_DataSize; // length of the data set in bytes
  665. int m_DataStart; // file offset where data set starts
  666. int m_LoopStart; // offset in data set where loop starts
  667. int m_BufferSize; // size of the buffer in bytes
  668. int m_numBuffers; // number of buffers (1 or 2) to march through
  669. int m_SectorSize; // size of sector on stream device
  670. bool m_bSinglePlay; // hint to keep same buffers
  671. };
  672. CUtlLinkedList< StreamedEntry_t, StreamHandle_t > m_StreamedHandles;
  673. static bool CacheHandleLessFunc( const CacheEntry_t& lhs, const CacheEntry_t& rhs )
  674. {
  675. return lhs.name < rhs.name;
  676. }
  677. CUtlRBTree< CacheEntry_t, int > m_CacheHandles;
  678. memhandle_t FindOrCreateBuffer( asyncwaveparams_t &params, bool bFind );
  679. bool m_bInitialized;
  680. bool m_bQueueCacheUnlocks;
  681. CUtlVector<memhandle_t> m_unlockQueue;
  682. };
  683. //-----------------------------------------------------------------------------
  684. // Purpose:
  685. //-----------------------------------------------------------------------------
  686. CAsyncWavDataCache::CAsyncWavDataCache() :
  687. m_CacheHandles( 0, 0, CacheHandleLessFunc ),
  688. m_BufferList( 0, 0, BufferHandleLessFunc ),
  689. m_bInitialized( false ),
  690. m_bQueueCacheUnlocks( false )
  691. {
  692. }
  693. //-----------------------------------------------------------------------------
  694. // Purpose:
  695. // Output : Returns true on success, false on failure.
  696. //-----------------------------------------------------------------------------
  697. bool CAsyncWavDataCache::Init( unsigned int memSize )
  698. {
  699. if ( m_bInitialized )
  700. return true;
  701. if ( IsX360() )
  702. {
  703. const char *pGame = engineClient->GetGameDirectory();
  704. if ( !Q_stricmp( Q_UnqualifiedFileName( pGame ), "tf" ) )
  705. {
  706. memSize = TF_XBOX_WAV_MEMORY_CACHE;
  707. }
  708. else
  709. {
  710. memSize = DEFAULT_XBOX_WAV_MEMORY_CACHE;
  711. }
  712. }
  713. else
  714. {
  715. if ( memSize < DEFAULT_WAV_MEMORY_CACHE )
  716. {
  717. memSize = DEFAULT_WAV_MEMORY_CACHE;
  718. }
  719. }
  720. #if FORCE_SMALL_MEMORY_CACHE_SIZE
  721. memSize = FORCE_SMALL_MEMORY_CACHE_SIZE;
  722. Msg( "WARNING CAsyncWavDataCache::Init() forcing small memory cache size: %u\n", memSize );
  723. #endif
  724. CCacheClientBaseClass::Init( g_pDataCache, "WaveData", memSize );
  725. m_bInitialized = true;
  726. return true;
  727. }
  728. //-----------------------------------------------------------------------------
  729. // Purpose:
  730. //-----------------------------------------------------------------------------
  731. void CAsyncWavDataCache::Shutdown()
  732. {
  733. if ( !m_bInitialized )
  734. {
  735. return;
  736. }
  737. Clear();
  738. CCacheClientBaseClass::Shutdown();
  739. m_bInitialized = false;
  740. }
  741. //-----------------------------------------------------------------------------
  742. // Purpose: Creates initial cache object if it doesn't already exist, starts async loading the actual data
  743. // in any case.
  744. // Input : *filename -
  745. // datasize -
  746. // startpos -
  747. // Output : memhandle_t
  748. //-----------------------------------------------------------------------------
  749. memhandle_t CAsyncWavDataCache::AsyncLoadCache( char const *filename, int datasize, int startpos, bool bIsPrefetch )
  750. {
  751. VPROF( "CAsyncWavDataCache::AsyncLoadCache" );
  752. FileNameHandle_t fnh = g_pFileSystem->FindOrAddFileName( filename );
  753. CacheEntry_t search;
  754. search.name = fnh;
  755. search.handle = 0;
  756. // find or create the handle
  757. int idx = m_CacheHandles.Find( search );
  758. if ( idx == m_CacheHandles.InvalidIndex() )
  759. {
  760. idx = m_CacheHandles.Insert( search );
  761. Assert( idx != m_CacheHandles.InvalidIndex() );
  762. }
  763. CacheEntry_t &entry = m_CacheHandles[idx];
  764. // Try and pull it into cache
  765. CAsyncWaveData *data = CacheGet( entry.handle );
  766. if ( !data )
  767. {
  768. // Try and reload it
  769. asyncwaveparams_t params;
  770. params.hFilename = fnh;
  771. params.datasize = datasize;
  772. params.seekpos = startpos;
  773. params.bPrefetch = bIsPrefetch;
  774. entry.handle = CacheCreate( params );
  775. }
  776. return entry.handle;
  777. }
  778. //-----------------------------------------------------------------------------
  779. // Purpose: Reclaim a buffer. A reclaimed resident buffer is ready for play.
  780. //-----------------------------------------------------------------------------
  781. memhandle_t CAsyncWavDataCache::FindOrCreateBuffer( asyncwaveparams_t &params, bool bFind )
  782. {
  783. CAsyncWaveData *pWaveData;
  784. BufferEntry_t search;
  785. BufferHandle_t hBuffer;
  786. search.m_hName = params.hFilename;
  787. search.m_StartPos = params.seekpos;
  788. search.m_bCanBeShared = bFind;
  789. search.m_hWaveData = 0;
  790. if ( bFind )
  791. {
  792. // look for an existing buffer that matches exactly (same file, offset, and share)
  793. int iBuffer = m_BufferList.Find( search );
  794. if ( iBuffer != m_BufferList.InvalidIndex() )
  795. {
  796. // found
  797. search.m_hWaveData = m_BufferList[iBuffer].m_hWaveData;
  798. if ( snd_async_stream_spew.GetInt() >= 2 )
  799. {
  800. char tempBuff[MAX_PATH];
  801. g_pFileSystem->String( params.hFilename, tempBuff, sizeof( tempBuff ) );
  802. Msg( "Found Buffer: %s, offset: %d\n", tempBuff, params.seekpos );
  803. }
  804. }
  805. }
  806. // each resource buffer stays locked (valid) while in use
  807. // a buffering stream is not subject to lru and can rely on it's buffers
  808. // a buffering stream may obsolete it's buffers by reducing the lock count, allowing for lru
  809. pWaveData = CacheLock( search.m_hWaveData );
  810. if ( !pWaveData )
  811. {
  812. // not in cache, create and lock
  813. // not found, create buffer and fill with data
  814. search.m_hWaveData = CacheCreate( params, DCAF_LOCK );
  815. // add the buffer to our managed list
  816. hBuffer = m_BufferList.Insert( search );
  817. Assert( hBuffer != m_BufferList.InvalidIndex() );
  818. // store the handle into our managed list
  819. // used during a lru discard as a means to keep the list in-sync
  820. pWaveData = CacheGet( search.m_hWaveData );
  821. pWaveData->m_hBuffer = hBuffer;
  822. }
  823. else
  824. {
  825. // still in cache
  826. // same as requesting it and having it arrive instantly
  827. pWaveData->m_start = pWaveData->m_arrival = (float)Plat_FloatTime();
  828. }
  829. return search.m_hWaveData;
  830. }
  831. //-----------------------------------------------------------------------------
  832. // Purpose: Load data asynchronously via multi-buffers, returns specialized handle
  833. //-----------------------------------------------------------------------------
  834. StreamHandle_t CAsyncWavDataCache::OpenStreamedLoad( char const *pFileName, int dataSize, int dataStart, int startPos, int loopPos, int bufferSize, int numBuffers, streamFlags_t flags )
  835. {
  836. VPROF( "CAsyncWavDataCache::OpenStreamedLoad" );
  837. StreamedEntry_t streamedEntry;
  838. StreamHandle_t hStream;
  839. asyncwaveparams_t params;
  840. int i;
  841. Assert( numBuffers > 0 && numBuffers <= STREAM_BUFFER_COUNT );
  842. // queued load mandates one buffer
  843. Assert( !( flags & STREAMED_QUEUEDLOAD ) || numBuffers == 1 );
  844. streamedEntry.m_hName = g_pFileSystem->FindOrAddFileName( pFileName );
  845. streamedEntry.m_Front = 0;
  846. streamedEntry.m_DataSize = dataSize;
  847. streamedEntry.m_DataStart = dataStart;
  848. streamedEntry.m_NextStartPos = startPos + numBuffers * bufferSize;
  849. streamedEntry.m_LoopStart = loopPos;
  850. streamedEntry.m_BufferSize = bufferSize;
  851. streamedEntry.m_numBuffers = numBuffers;
  852. streamedEntry.m_bSinglePlay = ( flags & STREAMED_SINGLEPLAY ) != 0;
  853. streamedEntry.m_SectorSize = ( IsX360() && ( flags & STREAMED_FROMDVD ) ) ? XBOX_DVD_SECTORSIZE : 1;
  854. // single play streams expect to uniquely own and thus recycle their buffers though the data
  855. // single play streams are guaranteed that their buffers are private and cannot be shared
  856. // a non-single play stream wants persisting buffers and attempts to reclaim a matching buffer
  857. bool bFindBuffer = ( streamedEntry.m_bSinglePlay == false );
  858. // initial load populates buffers
  859. // mixing starts after front buffer viable
  860. // buffer rotation occurs after front buffer consumed
  861. // there should be no blocking
  862. params.hFilename = streamedEntry.m_hName;
  863. params.datasize = bufferSize;
  864. params.alignment = streamedEntry.m_SectorSize;
  865. params.bCanBeQueued = ( flags & STREAMED_QUEUEDLOAD ) != 0;
  866. for ( i=0; i<numBuffers; ++i )
  867. {
  868. params.seekpos = dataStart + startPos + i * bufferSize;
  869. streamedEntry.m_hWaveData[i] = FindOrCreateBuffer( params, bFindBuffer );
  870. }
  871. // get a unique handle for each stream request
  872. hStream = m_StreamedHandles.AddToTail( streamedEntry );
  873. Assert( hStream != m_StreamedHandles.InvalidIndex() );
  874. return hStream;
  875. }
  876. //-----------------------------------------------------------------------------
  877. // Purpose: Cleanup a streamed load's resources.
  878. //-----------------------------------------------------------------------------
  879. void CAsyncWavDataCache::CloseStreamedLoad( StreamHandle_t hStream )
  880. {
  881. VPROF( "CAsyncWavDataCache::CloseStreamedLoad" );
  882. if ( hStream == INVALID_STREAM_HANDLE )
  883. {
  884. return;
  885. }
  886. int lockCount;
  887. StreamedEntry_t &streamedEntry = m_StreamedHandles[hStream];
  888. for ( int i=0; i<streamedEntry.m_numBuffers; ++i )
  889. {
  890. // multiple streams could be using the same buffer, keeping the lock count nonzero
  891. lockCount = GetCacheSection()->GetLockCount( streamedEntry.m_hWaveData[i] );
  892. Assert( lockCount >= 1 );
  893. if ( lockCount > 0 )
  894. {
  895. lockCount = CacheUnlock( streamedEntry.m_hWaveData[i] );
  896. }
  897. if ( streamedEntry.m_bSinglePlay )
  898. {
  899. // a buffering single play stream has no reason to reuse its own buffers and destroys them
  900. Assert( lockCount == 0 );
  901. CacheRemove( streamedEntry.m_hWaveData[i] );
  902. }
  903. }
  904. m_StreamedHandles.Remove( hStream );
  905. }
  906. //-----------------------------------------------------------------------------
  907. // Purpose:
  908. // Input : *filename -
  909. // datasize -
  910. // startpos -
  911. //-----------------------------------------------------------------------------
  912. void CAsyncWavDataCache::PrefetchCache( char const *filename, int datasize, int startpos )
  913. {
  914. // Just do an async load, but don't get cache handle
  915. AsyncLoadCache( filename, datasize, startpos, true );
  916. }
  917. //-----------------------------------------------------------------------------
  918. // Purpose:
  919. // Input : *filename -
  920. // datasize -
  921. // startpos -
  922. // *buffer -
  923. // bufsize -
  924. // copystartpos -
  925. // bytestocopy -
  926. // Output : Returns true on success, false on failure.
  927. //-----------------------------------------------------------------------------
  928. bool CAsyncWavDataCache::CopyDataIntoMemory( char const *filename, int datasize, int startpos, void *buffer, int bufsize, int copystartpos, int bytestocopy, bool *pbPostProcessed )
  929. {
  930. VPROF( "CAsyncWavDataCache::CopyDataIntoMemory" );
  931. bool bret = false;
  932. // Add to caching system
  933. AsyncLoadCache( filename, datasize, startpos );
  934. FileNameHandle_t fnh = g_pFileSystem->FindOrAddFileName( filename );
  935. CacheEntry_t search;
  936. search.name = fnh;
  937. search.handle = 0;
  938. // Now look it up, it should be in the system
  939. int idx = m_CacheHandles.Find( search );
  940. if ( idx == m_CacheHandles.InvalidIndex() )
  941. {
  942. Assert( 0 );
  943. return bret;
  944. }
  945. // Now see if the handle has been paged out...
  946. return CopyDataIntoMemory( m_CacheHandles[ idx ].handle, filename, datasize, startpos, buffer, bufsize, copystartpos, bytestocopy, pbPostProcessed );
  947. }
  948. //-----------------------------------------------------------------------------
  949. // Purpose:
  950. // Input : handle -
  951. // *filename -
  952. // datasize -
  953. // startpos -
  954. // *buffer -
  955. // bufsize -
  956. // copystartpos -
  957. // bytestocopy -
  958. // Output : Returns true on success, false on failure.
  959. //-----------------------------------------------------------------------------
  960. bool CAsyncWavDataCache::CopyDataIntoMemory( memhandle_t& handle, char const *filename, int datasize, int startpos, void *buffer, int bufsize, int copystartpos, int bytestocopy, bool *pbPostProcessed )
  961. {
  962. VPROF( "CAsyncWavDataCache::CopyDataIntoMemory" );
  963. *pbPostProcessed = false;
  964. bool bret = false;
  965. CAsyncWaveData *data = CacheLock( handle );
  966. if ( !data )
  967. {
  968. FileNameHandle_t fnh = g_pFileSystem->FindOrAddFileName( filename );
  969. CacheEntry_t search;
  970. search.name = fnh;
  971. search.handle = 0;
  972. // Now look it up, it should be in the system
  973. int idx = m_CacheHandles.Find( search );
  974. if ( idx == m_CacheHandles.InvalidIndex() )
  975. {
  976. Assert( 0 );
  977. return false;
  978. }
  979. // Try and reload it
  980. asyncwaveparams_t params;
  981. params.hFilename = fnh;
  982. params.datasize = datasize;
  983. params.seekpos = startpos;
  984. handle = m_CacheHandles[ idx ].handle = CacheCreate( params );
  985. data = CacheLock( handle );
  986. if ( !data )
  987. {
  988. return bret;
  989. }
  990. }
  991. // Cache entry exists, but if filesize == 0 then the file itself wasn't on disk...
  992. if ( data->m_nDataSize != 0 )
  993. {
  994. bret = data->BlockingCopyData( buffer, bufsize, copystartpos, bytestocopy );
  995. }
  996. *pbPostProcessed = data->GetPostProcessed();
  997. // Release lock
  998. CacheUnlock( handle );
  999. return bret;
  1000. }
  1001. //-----------------------------------------------------------------------------
  1002. // Purpose: Copy from streaming buffers into target memory, never blocks.
  1003. //-----------------------------------------------------------------------------
  1004. int CAsyncWavDataCache::CopyStreamedDataIntoMemory( int hStream, void *pBuffer, int bufferSize, int copyStartPos, int bytesToCopy )
  1005. {
  1006. VPROF( "CAsyncWavDataCache::CopyStreamedDataIntoMemory" );
  1007. int actualCopied;
  1008. int count;
  1009. int i;
  1010. int which;
  1011. CAsyncWaveData *pWaveData[STREAM_BUFFER_COUNT];
  1012. CAsyncWaveData *pFront;
  1013. asyncwaveparams_t params;
  1014. int nextStartPos;
  1015. int bufferPos;
  1016. bool bEndOfFile;
  1017. int index;
  1018. bool bWaiting;
  1019. bool bCompleted;
  1020. StreamedEntry_t &streamedEntry = m_StreamedHandles[hStream];
  1021. if ( copyStartPos >= streamedEntry.m_DataStart + streamedEntry.m_DataSize )
  1022. {
  1023. // at or past end of file
  1024. return 0;
  1025. }
  1026. for ( i=0; i<streamedEntry.m_numBuffers; ++i )
  1027. {
  1028. pWaveData[i] = CacheGetNoTouch( streamedEntry.m_hWaveData[i] );
  1029. Assert( pWaveData[i] );
  1030. }
  1031. // drive the buffering
  1032. index = streamedEntry.m_Front;
  1033. bEndOfFile = 0;
  1034. actualCopied = 0;
  1035. bWaiting = false;
  1036. while ( 1 )
  1037. {
  1038. // try to satisfy from the front
  1039. pFront = pWaveData[index % streamedEntry.m_numBuffers];
  1040. bufferPos = copyStartPos - pFront->m_async.nOffset;
  1041. // cache atomic async completion signal off to avoid coherency issues
  1042. bCompleted = pFront->m_bLoaded || pFront->m_bMissing;
  1043. if ( snd_async_stream_spew.GetInt() >= 1 )
  1044. {
  1045. // interval is the audio block clock rate, the block must be available within this interval
  1046. // a faster audio rate or smaller block size implies a smaller interval
  1047. // latency is the actual block delivery time
  1048. // latency must not exceed the delivery interval or stariving occurs and audio pops
  1049. float nowTime = Plat_FloatTime();
  1050. int interval = (int)(1000.0f*(nowTime-pFront->m_start));
  1051. int latency;
  1052. if ( bCompleted && pFront->m_bLoaded )
  1053. {
  1054. latency = (int)(1000.0f*(pFront->m_arrival-pFront->m_start));
  1055. }
  1056. else
  1057. {
  1058. // buffer has not arrived yet
  1059. latency = -1;
  1060. }
  1061. DevMsg( "Stream:%2d interval:%5dms latency:%5dms offset:%d length:%d (%s)\n", hStream, interval, latency, pFront->m_async.nOffset, pFront->m_nReadSize, pFront->GetFileName() );
  1062. }
  1063. if ( bCompleted && pFront->m_hAsyncControl && ( pFront->m_bLoaded || pFront->m_bMissing) )
  1064. {
  1065. g_pFileSystem->AsyncRelease( pFront->m_hAsyncControl );
  1066. pFront->m_hAsyncControl = NULL;
  1067. }
  1068. if ( bCompleted && pFront->m_bLoaded )
  1069. {
  1070. if ( bufferPos >= 0 && bufferPos < pFront->m_nReadSize )
  1071. {
  1072. count = bytesToCopy;
  1073. if ( bufferPos + bytesToCopy > pFront->m_nReadSize )
  1074. {
  1075. // clamp requested to actual available
  1076. count = pFront->m_nReadSize - bufferPos;
  1077. }
  1078. if ( bufferPos + count > bufferSize )
  1079. {
  1080. // clamp requested to caller's buffer dimension
  1081. count = bufferSize - bufferPos;
  1082. }
  1083. Q_memcpy( pBuffer, (char *)pFront->m_pvData + bufferPos, count );
  1084. // advance past consumed bytes
  1085. actualCopied += count;
  1086. copyStartPos += count;
  1087. bufferPos += count;
  1088. }
  1089. }
  1090. else if ( bCompleted && pFront->m_bMissing )
  1091. {
  1092. // notify on any error
  1093. MaybeReportMissingWav( pFront->GetFileName() );
  1094. break;
  1095. }
  1096. else
  1097. {
  1098. // data not available
  1099. bWaiting = true;
  1100. break;
  1101. }
  1102. // cycle past obsolete or consumed buffers
  1103. if ( bufferPos < 0 || bufferPos >= pFront->m_nReadSize )
  1104. {
  1105. // move to next buffer
  1106. index++;
  1107. if ( index - streamedEntry.m_Front >= streamedEntry.m_numBuffers )
  1108. {
  1109. // out of buffers
  1110. break;
  1111. }
  1112. }
  1113. if ( actualCopied == bytesToCopy )
  1114. {
  1115. // satisfied request
  1116. break;
  1117. }
  1118. }
  1119. if ( streamedEntry.m_numBuffers > 1 )
  1120. {
  1121. // restart consumed buffers
  1122. while ( streamedEntry.m_Front < index )
  1123. {
  1124. if ( !actualCopied && !bWaiting )
  1125. {
  1126. // couldn't return any data because the buffers aren't in the right location
  1127. // oh no! caller must be skipping
  1128. // due to latency the next buffer position has to start one full buffer ahead of the caller's desired read location
  1129. // hopefully only 1 buffer will stutter
  1130. nextStartPos = copyStartPos - streamedEntry.m_DataStart + streamedEntry.m_BufferSize;
  1131. // advance past, ready for next possible iteration
  1132. copyStartPos += streamedEntry.m_BufferSize;
  1133. }
  1134. else
  1135. {
  1136. // get the next forecasted read location
  1137. nextStartPos = streamedEntry.m_NextStartPos;
  1138. }
  1139. if ( nextStartPos >= streamedEntry.m_DataSize )
  1140. {
  1141. // next buffer is at or past end of file
  1142. if ( streamedEntry.m_LoopStart >= 0 )
  1143. {
  1144. // wrap back around to loop position
  1145. nextStartPos = streamedEntry.m_LoopStart;
  1146. }
  1147. else
  1148. {
  1149. // advance past consumed buffer
  1150. streamedEntry.m_Front++;
  1151. // start no further buffers
  1152. break;
  1153. }
  1154. }
  1155. // still valid data left to read
  1156. // snap the buffer position to required alignment
  1157. nextStartPos = streamedEntry.m_SectorSize * (nextStartPos/streamedEntry.m_SectorSize);
  1158. // start loading back buffer at future location
  1159. params.hFilename = streamedEntry.m_hName;
  1160. params.seekpos = streamedEntry.m_DataStart + nextStartPos;
  1161. params.datasize = streamedEntry.m_DataSize - nextStartPos;
  1162. params.alignment = streamedEntry.m_SectorSize;
  1163. if ( params.datasize > streamedEntry.m_BufferSize )
  1164. {
  1165. // clamp to buffer size
  1166. params.datasize = streamedEntry.m_BufferSize;
  1167. }
  1168. // save next start position
  1169. streamedEntry.m_NextStartPos = nextStartPos + params.datasize;
  1170. which = streamedEntry.m_Front % streamedEntry.m_numBuffers;
  1171. if ( streamedEntry.m_bSinglePlay )
  1172. {
  1173. // a single play wave has no reason to persist its buffers into the lru
  1174. // reuse buffer and restart until finished
  1175. pWaveData[which]->StartAsyncLoading( params );
  1176. }
  1177. else
  1178. {
  1179. // release obsolete buffer to lru management
  1180. CacheUnlock( streamedEntry.m_hWaveData[which] );
  1181. // reclaim or create/load the desired buffer
  1182. streamedEntry.m_hWaveData[which] = FindOrCreateBuffer( params, true );
  1183. }
  1184. streamedEntry.m_Front++;
  1185. }
  1186. if ( bWaiting )
  1187. {
  1188. // oh no! data needed is not yet available in front buffer
  1189. // caller requesting data faster than can be provided or caller skipped
  1190. // can only return what has been copied thus far (could be 0)
  1191. return actualCopied;
  1192. }
  1193. }
  1194. return actualCopied;
  1195. }
  1196. //-----------------------------------------------------------------------------
  1197. // Purpose: Get the front buffer, optionally block.
  1198. // Intended for user of a single buffer stream.
  1199. //-----------------------------------------------------------------------------
  1200. void *CAsyncWavDataCache::GetStreamedDataPointer( StreamHandle_t hStream, bool bSync )
  1201. {
  1202. void *pData;
  1203. CAsyncWaveData *pFront;
  1204. int index;
  1205. StreamedEntry_t &streamedEntry = m_StreamedHandles[hStream];
  1206. index = streamedEntry.m_Front % streamedEntry.m_numBuffers;
  1207. pFront = CacheGetNoTouch( streamedEntry.m_hWaveData[index] );
  1208. Assert( pFront );
  1209. if ( !pFront )
  1210. {
  1211. // shouldn't happen
  1212. return NULL;
  1213. }
  1214. if ( !pFront->m_bMissing && pFront->m_bLoaded )
  1215. {
  1216. return pFront->m_pvData;
  1217. }
  1218. if ( bSync && pFront->BlockingGetDataPointer( &pData ) )
  1219. {
  1220. return pData;
  1221. }
  1222. return NULL;
  1223. }
  1224. //-----------------------------------------------------------------------------
  1225. // Purpose: The front buffer must be valid
  1226. //-----------------------------------------------------------------------------
  1227. bool CAsyncWavDataCache::IsStreamedDataReady( int hStream )
  1228. {
  1229. VPROF( "CAsyncWavDataCache::IsStreamedDataReady" );
  1230. if ( hStream == INVALID_STREAM_HANDLE )
  1231. {
  1232. return false;
  1233. }
  1234. StreamedEntry_t &streamedEntry = m_StreamedHandles[hStream];
  1235. if ( streamedEntry.m_Front )
  1236. {
  1237. // already streaming, the buffers better be arriving as expected
  1238. return true;
  1239. }
  1240. // only the first front buffer must be present
  1241. CAsyncWaveData *pFront = CacheGetNoTouch( streamedEntry.m_hWaveData[0] );
  1242. Assert( pFront );
  1243. if ( !pFront )
  1244. {
  1245. // shouldn't happen
  1246. // let the caller think data is ready, so stream can shutdown
  1247. return true;
  1248. }
  1249. // regardless of any errors
  1250. // errors handled during data fetch
  1251. return pFront->m_bLoaded || pFront->m_bMissing;
  1252. }
  1253. //-----------------------------------------------------------------------------
  1254. // Purpose: Dequeue the buffer entry (backdoor for list management)
  1255. //-----------------------------------------------------------------------------
  1256. void CAsyncWavDataCache::MarkBufferDiscarded( BufferHandle_t hBuffer )
  1257. {
  1258. m_BufferList.RemoveAt( hBuffer );
  1259. }
  1260. //-----------------------------------------------------------------------------
  1261. // Purpose:
  1262. // Input : handle -
  1263. // proc -
  1264. //-----------------------------------------------------------------------------
  1265. void CAsyncWavDataCache::SetPostProcessed( memhandle_t handle, bool proc )
  1266. {
  1267. CAsyncWaveData *data = CacheGet( handle );
  1268. if ( data )
  1269. {
  1270. data->SetPostProcessed( proc );
  1271. }
  1272. }
  1273. //-----------------------------------------------------------------------------
  1274. // Purpose:
  1275. // Input : handle -
  1276. //-----------------------------------------------------------------------------
  1277. void CAsyncWavDataCache::Unload( memhandle_t handle )
  1278. {
  1279. // Don't actually unload, just mark it as stale
  1280. if ( GetCacheSection() )
  1281. {
  1282. GetCacheSection()->Age( handle );
  1283. }
  1284. }
  1285. //-----------------------------------------------------------------------------
  1286. // Purpose:
  1287. // Input : handle -
  1288. // *filename -
  1289. // datasize -
  1290. // startpos -
  1291. // **pData -
  1292. // copystartpos -
  1293. // *pbPostProcessed -
  1294. // Output : Returns true on success, false on failure.
  1295. //-----------------------------------------------------------------------------
  1296. bool CAsyncWavDataCache::GetDataPointer( memhandle_t& handle, char const *filename, int datasize, int startpos, void **pData, int copystartpos, bool *pbPostProcessed )
  1297. {
  1298. VPROF( "CAsyncWavDataCache::GetDataPointer" );
  1299. Assert( pbPostProcessed );
  1300. Assert( pData );
  1301. *pbPostProcessed = false;
  1302. bool bret = false;
  1303. *pData = NULL;
  1304. CAsyncWaveData *data = CacheLock( handle );
  1305. if ( !data )
  1306. {
  1307. FileNameHandle_t fnh = g_pFileSystem->FindOrAddFileName( filename );
  1308. CacheEntry_t search;
  1309. search.name = fnh;
  1310. search.handle = 0;
  1311. int idx = m_CacheHandles.Find( search );
  1312. if ( idx == m_CacheHandles.InvalidIndex() )
  1313. {
  1314. Assert( 0 );
  1315. return bret;
  1316. }
  1317. // Try and reload it
  1318. asyncwaveparams_t params;
  1319. params.hFilename = fnh;
  1320. params.datasize = datasize;
  1321. params.seekpos = startpos;
  1322. handle = m_CacheHandles[ idx ].handle = CacheCreate( params );
  1323. data = CacheLock( handle );
  1324. if ( !data )
  1325. {
  1326. return bret;
  1327. }
  1328. }
  1329. // Cache entry exists, but if filesize == 0 then the file itself wasn't on disk...
  1330. if ( data->m_nDataSize != 0 )
  1331. {
  1332. if ( datasize != data->m_nDataSize )
  1333. {
  1334. // We've had issues where we are called with datasize larger than what we read on disk.
  1335. // Ie: datasize is 277,180, data->m_nDataSize is 263,168
  1336. // This can happen due to a corrupted audio cache, but it's more likely that somehow
  1337. // we wound up reading the cache data from one language and the file from another.
  1338. DevMsg( "Cached datasize != sound datasize %d - %d.\n", datasize, data->m_nDataSize );
  1339. #ifdef STAGING_ONLY
  1340. // Adding a STAGING_ONLY debugger break to try and help track this down. Hopefully we'll
  1341. // get this crash internally with full debug information instead of just minidump files.
  1342. DebuggerBreak();
  1343. #endif
  1344. }
  1345. else if ( copystartpos < data->m_nDataSize )
  1346. {
  1347. if ( data->BlockingGetDataPointer( pData ) )
  1348. {
  1349. *pData = (char *)*pData + copystartpos;
  1350. bret = true;
  1351. }
  1352. }
  1353. }
  1354. *pbPostProcessed = data->GetPostProcessed();
  1355. // Release lock at the end of mixing
  1356. QueueUnlock( handle );
  1357. return bret;
  1358. }
  1359. //-----------------------------------------------------------------------------
  1360. // Purpose:
  1361. // Input : handle -
  1362. // *filename -
  1363. // datasize -
  1364. // startpos -
  1365. // Output : Returns true on success, false on failure.
  1366. //-----------------------------------------------------------------------------
  1367. bool CAsyncWavDataCache::IsDataLoadCompleted( memhandle_t handle, bool *pIsValid )
  1368. {
  1369. VPROF( "CAsyncWavDataCache::IsDataLoadCompleted" );
  1370. CAsyncWaveData *data = CacheGet( handle );
  1371. if ( !data )
  1372. {
  1373. *pIsValid = false;
  1374. return false;
  1375. }
  1376. *pIsValid = true;
  1377. // bump the priority
  1378. data->SetAsyncPriority( 1 );
  1379. return data->m_bLoaded;
  1380. }
  1381. void CAsyncWavDataCache::RestartDataLoad( memhandle_t *pHandle, const char *pFilename, int dataSize, int startpos )
  1382. {
  1383. CAsyncWaveData *data = CacheGet( *pHandle );
  1384. if ( !data )
  1385. {
  1386. *pHandle = AsyncLoadCache( pFilename, dataSize, startpos );
  1387. }
  1388. }
  1389. bool CAsyncWavDataCache::IsDataLoadInProgress( memhandle_t handle )
  1390. {
  1391. CAsyncWaveData *data = CacheGet( handle );
  1392. if ( data )
  1393. {
  1394. return data->IsCurrentlyLoading();
  1395. }
  1396. return false;
  1397. }
  1398. //-----------------------------------------------------------------------------
  1399. // Purpose:
  1400. //-----------------------------------------------------------------------------
  1401. void CAsyncWavDataCache::Flush()
  1402. {
  1403. GetCacheSection()->Flush();
  1404. SpewMemoryUsage( 0 );
  1405. }
  1406. void CAsyncWavDataCache::QueueUnlock( const memhandle_t &handle )
  1407. {
  1408. // not queuing right now, just unlock
  1409. if ( !m_bQueueCacheUnlocks )
  1410. {
  1411. CacheUnlock( handle );
  1412. return;
  1413. }
  1414. // queue to unlock at the end of mixing
  1415. m_unlockQueue.AddToTail( handle );
  1416. }
  1417. void CAsyncWavDataCache::OnMixBegin()
  1418. {
  1419. Assert( !m_bQueueCacheUnlocks );
  1420. m_bQueueCacheUnlocks = true;
  1421. Assert( m_unlockQueue.Count() == 0 );
  1422. }
  1423. void CAsyncWavDataCache::OnMixEnd()
  1424. {
  1425. m_bQueueCacheUnlocks = false;
  1426. // flush the unlock queue
  1427. for ( int i = 0; i < m_unlockQueue.Count(); i++ )
  1428. {
  1429. CacheUnlock( m_unlockQueue[i] );
  1430. }
  1431. m_unlockQueue.RemoveAll();
  1432. }
  1433. //-----------------------------------------------------------------------------
  1434. // Purpose:
  1435. //-----------------------------------------------------------------------------
  1436. bool CAsyncWavDataCache::GetItemName( DataCacheClientID_t clientId, const void *pItem, char *pDest, unsigned nMaxLen )
  1437. {
  1438. CAsyncWaveData *pWaveData = (CAsyncWaveData *)pItem;
  1439. Q_strncpy( pDest, pWaveData->GetFileName(), nMaxLen );
  1440. return true;
  1441. }
  1442. //-----------------------------------------------------------------------------
  1443. // Purpose: Spew a cache summary to the console
  1444. //-----------------------------------------------------------------------------
  1445. void CAsyncWavDataCache::SpewMemoryUsage( int level )
  1446. {
  1447. DataCacheStatus_t status;
  1448. DataCacheLimits_t limits;
  1449. GetCacheSection()->GetStatus( &status, &limits );
  1450. int bytesUsed = status.nBytes;
  1451. int bytesTotal = limits.nMaxBytes;
  1452. if ( IsPC() )
  1453. {
  1454. float percent = 100.0f * (float)bytesUsed / (float)bytesTotal;
  1455. Msg( "CAsyncWavDataCache: %i .wavs total %s, %.2f %% of capacity\n", m_CacheHandles.Count(), Q_pretifymem( bytesUsed, 2 ), percent );
  1456. if ( level >= 1 )
  1457. {
  1458. for ( int i = m_CacheHandles.FirstInorder(); m_CacheHandles.IsValidIndex(i); i = m_CacheHandles.NextInorder(i) )
  1459. {
  1460. char name[MAX_PATH];
  1461. if ( !g_pFileSystem->String( m_CacheHandles[ i ].name, name, sizeof( name ) ) )
  1462. {
  1463. Assert( 0 );
  1464. continue;
  1465. }
  1466. memhandle_t &handle = m_CacheHandles[ i ].handle;
  1467. CAsyncWaveData *data = CacheGetNoTouch( handle );
  1468. if ( data )
  1469. {
  1470. Msg( "\t%16.16s : %s\n", Q_pretifymem(data->Size()),name);
  1471. }
  1472. else
  1473. {
  1474. Msg( "\t%16.16s : %s\n", "not resident",name);
  1475. }
  1476. }
  1477. Msg( "CAsyncWavDataCache: %i .wavs total %s, %.2f %% of capacity\n", m_CacheHandles.Count(), Q_pretifymem( bytesUsed, 2 ), percent );
  1478. }
  1479. }
  1480. if ( IsX360() )
  1481. {
  1482. CAsyncWaveData *pData;
  1483. BufferEntry_t *pBuffer;
  1484. BufferHandle_t h;
  1485. float percent;
  1486. int lockCount;
  1487. if ( bytesTotal <= 0 )
  1488. {
  1489. // unbounded, indeterminate
  1490. percent = 0;
  1491. bytesTotal = 0;
  1492. }
  1493. else
  1494. {
  1495. percent = 100.0f*(float)bytesUsed/(float)bytesTotal;
  1496. }
  1497. if ( level >= 1 )
  1498. {
  1499. // detail buffers
  1500. ConMsg( "Streaming Buffer List:\n" );
  1501. for ( h = m_BufferList.FirstInorder(); h != m_BufferList.InvalidIndex(); h = m_BufferList.NextInorder( h ) )
  1502. {
  1503. pBuffer = &m_BufferList[h];
  1504. pData = CacheGetNoTouch( pBuffer->m_hWaveData );
  1505. lockCount = GetCacheSection()->GetLockCount( pBuffer->m_hWaveData );
  1506. CacheLockMutex();
  1507. if ( pData )
  1508. {
  1509. ConMsg( "Start:%7d Length:%7d Lock:%3d %s\n", pData->m_async.nOffset, pData->m_nDataSize, lockCount, pData->GetFileName() );
  1510. }
  1511. CacheUnlockMutex();
  1512. }
  1513. }
  1514. ConMsg( "CAsyncWavDataCache: %.2f MB used of %.2f MB, %.2f%% of capacity", (float)bytesUsed/(1024.0f*1024.0f), (float)bytesTotal/(1024.0f*1024.0f), percent );
  1515. }
  1516. }
  1517. //-----------------------------------------------------------------------------
  1518. // Purpose:
  1519. //-----------------------------------------------------------------------------
  1520. void CAsyncWavDataCache::Clear()
  1521. {
  1522. for ( int i = m_CacheHandles.FirstInorder(); m_CacheHandles.IsValidIndex(i); i = m_CacheHandles.NextInorder(i) )
  1523. {
  1524. CacheEntry_t& dat = m_CacheHandles[i];
  1525. CacheRemove( dat.handle );
  1526. }
  1527. m_CacheHandles.RemoveAll();
  1528. FOR_EACH_LL( m_StreamedHandles, i )
  1529. {
  1530. StreamedEntry_t &dat = m_StreamedHandles[i];
  1531. for ( int j=0; j<dat.m_numBuffers; ++j )
  1532. {
  1533. GetCacheSection()->BreakLock( dat.m_hWaveData[j] );
  1534. CacheRemove( dat.m_hWaveData[j] );
  1535. }
  1536. }
  1537. m_StreamedHandles.RemoveAll();
  1538. m_BufferList.RemoveAll();
  1539. }
  1540. static CAsyncWavDataCache g_AsyncWaveDataCache;
  1541. IAsyncWavDataCache *wavedatacache = &g_AsyncWaveDataCache;
  1542. CON_COMMAND( snd_async_flush, "Flush all unlocked async audio data" )
  1543. {
  1544. g_AsyncWaveDataCache.Flush();
  1545. }
  1546. CON_COMMAND( snd_async_showmem, "Show async memory stats" )
  1547. {
  1548. g_AsyncWaveDataCache.SpewMemoryUsage( 1 );
  1549. }
  1550. //-----------------------------------------------------------------------------
  1551. // Purpose:
  1552. // Input : *pFileName -
  1553. // dataOffset -
  1554. // dataSize -
  1555. //-----------------------------------------------------------------------------
  1556. void PrefetchDataStream( const char *pFileName, int dataOffset, int dataSize )
  1557. {
  1558. if ( IsX360() )
  1559. {
  1560. // Xbox streaming buffer implementation does not support this "hinting"
  1561. return;
  1562. }
  1563. wavedatacache->PrefetchCache( pFileName, dataSize, dataOffset );
  1564. }
  1565. //-----------------------------------------------------------------------------
  1566. // Purpose: This is an instance of a stream.
  1567. // This contains the file handle and streaming buffer
  1568. // The mixer doesn't know the file is streaming. The IWaveData
  1569. // abstracts the data access. The mixer abstracts data encoding/format
  1570. //-----------------------------------------------------------------------------
  1571. class CWaveDataStreamAsync : public IWaveData
  1572. {
  1573. public:
  1574. CWaveDataStreamAsync( CAudioSource &source, IWaveStreamSource *pStreamSource, const char *pFileName, int fileStart, int fileSize, CSfxTable *sfx, int startOffset );
  1575. ~CWaveDataStreamAsync( void );
  1576. // return the source pointer (mixer needs this to determine some things like sampling rate)
  1577. CAudioSource &Source( void ) { return m_source; }
  1578. // Read data from the source - this is the primary function of a IWaveData subclass
  1579. // Get the data from the buffer (or reload from disk)
  1580. virtual int ReadSourceData( void **pData, int sampleIndex, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] );
  1581. bool IsValid() { return m_bValid; }
  1582. virtual bool IsReadyToMix();
  1583. private:
  1584. CWaveDataStreamAsync( const CWaveDataStreamAsync & );
  1585. //-----------------------------------------------------------------------------
  1586. // Purpose:
  1587. // Output : byte
  1588. //-----------------------------------------------------------------------------
  1589. inline byte *GetCachedDataPointer()
  1590. {
  1591. VPROF( "CWaveDataStreamAsync::GetCachedDataPointer" );
  1592. CAudioSourceCachedInfo *info = m_AudioCacheHandle.Get( CAudioSource::AUDIO_SOURCE_WAV, m_pSfx->IsPrecachedSound(), m_pSfx, &m_nCachedDataSize );
  1593. if ( !info )
  1594. {
  1595. Assert( !"CAudioSourceWave::GetCachedDataPointer info == NULL" );
  1596. return NULL;
  1597. }
  1598. return (byte *)info->CachedData();
  1599. }
  1600. char const *GetFileName();
  1601. CAudioSource &m_source; // wave source
  1602. IWaveStreamSource *m_pStreamSource; // streaming
  1603. int m_sampleSize; // size of a sample in bytes
  1604. int m_waveSize; // total number of samples in the file
  1605. int m_bufferSize; // size of buffer in samples
  1606. char *m_buffer;
  1607. int m_sampleIndex;
  1608. int m_bufferCount;
  1609. int m_dataStart;
  1610. int m_dataSize;
  1611. memhandle_t m_hCache;
  1612. StreamHandle_t m_hStream;
  1613. FileNameHandle_t m_hFileName;
  1614. bool m_bValid;
  1615. CAudioSourceCachedInfoHandle_t m_AudioCacheHandle;
  1616. int m_nCachedDataSize;
  1617. CSfxTable *m_pSfx;
  1618. };
  1619. CWaveDataStreamAsync::CWaveDataStreamAsync
  1620. (
  1621. CAudioSource &source,
  1622. IWaveStreamSource *pStreamSource,
  1623. const char *pFileName,
  1624. int fileStart,
  1625. int fileSize,
  1626. CSfxTable *sfx,
  1627. int startOffset
  1628. ) :
  1629. m_source( source ),
  1630. m_dataStart( fileStart ),
  1631. m_dataSize( fileSize ),
  1632. m_pStreamSource( pStreamSource ),
  1633. m_bValid( false ),
  1634. m_hCache( 0 ),
  1635. m_hStream( INVALID_STREAM_HANDLE ),
  1636. m_hFileName( 0 ),
  1637. m_pSfx( sfx )
  1638. {
  1639. m_hFileName = g_pFileSystem->FindOrAddFileName( pFileName );
  1640. // nothing in the buffer yet
  1641. m_sampleIndex = 0;
  1642. m_bufferCount = 0;
  1643. if ( IsPC() )
  1644. {
  1645. m_buffer = new char[SINGLE_BUFFER_SIZE];
  1646. Q_memset( m_buffer, 0, SINGLE_BUFFER_SIZE );
  1647. }
  1648. m_nCachedDataSize = 0;
  1649. if ( m_dataSize <= 0 )
  1650. {
  1651. DevMsg(1, "Can't find streaming wav file: sound\\%s\n", GetFileName() );
  1652. return;
  1653. }
  1654. if ( IsPC() )
  1655. {
  1656. m_hCache = wavedatacache->AsyncLoadCache( GetFileName(), m_dataSize, m_dataStart );
  1657. // size of a sample
  1658. m_sampleSize = source.SampleSize();
  1659. // size in samples of the buffer
  1660. m_bufferSize = SINGLE_BUFFER_SIZE / m_sampleSize;
  1661. // size in samples (not bytes) of the wave itself
  1662. m_waveSize = fileSize / m_sampleSize;
  1663. m_AudioCacheHandle.Get( CAudioSource::AUDIO_SOURCE_WAV, m_pSfx->IsPrecachedSound(), m_pSfx, &m_nCachedDataSize );
  1664. }
  1665. if ( IsX360() )
  1666. {
  1667. // size of a sample
  1668. m_sampleSize = source.SampleSize();
  1669. // size in samples (not bytes) of the wave itself
  1670. m_waveSize = fileSize / m_sampleSize;
  1671. streamFlags_t flags = STREAMED_FROMDVD;
  1672. if ( !Q_strnicmp( pFileName, "music", 5 ) && ( pFileName[5] == '\\' || pFileName[5] == '/') )
  1673. {
  1674. // music discards and cycles its buffers
  1675. flags |= STREAMED_SINGLEPLAY;
  1676. }
  1677. else if ( !Q_strnicmp( pFileName, "vo", 2 ) && ( pFileName[2] == '\\' || pFileName[2] == '/' ) && !source.IsSentenceWord() )
  1678. {
  1679. // vo discards and cycles its buffers, except for sentence sources, which do recur
  1680. flags |= STREAMED_SINGLEPLAY;
  1681. }
  1682. int bufferSize;
  1683. if ( source.Format() == WAVE_FORMAT_XMA )
  1684. {
  1685. // each xma block has its own compression rate
  1686. // the buffer must be large enough to cover worst case delivery i/o latency
  1687. // the xma mixer expects quantum xma blocks
  1688. COMPILE_TIME_ASSERT( ( STREAM_BUFFER_DATASIZE % XMA_BLOCK_SIZE ) == 0 );
  1689. bufferSize = STREAM_BUFFER_DATASIZE;
  1690. }
  1691. else
  1692. {
  1693. // calculate a worst case buffer size based on rate
  1694. bufferSize = STREAM_BUFFER_TIME*source.SampleRate()*m_sampleSize;
  1695. if ( source.Format() == WAVE_FORMAT_ADPCM )
  1696. {
  1697. // consider adpcm as 4 bit samples
  1698. bufferSize /= 2;
  1699. }
  1700. if ( source.IsLooped() )
  1701. {
  1702. // lighten the streaming load for looping samples
  1703. // doubling the buffer halves the buffer search/load requests
  1704. bufferSize *= 2;
  1705. }
  1706. }
  1707. // streaming buffers obey alignments
  1708. bufferSize = AlignValue( bufferSize, XBOX_DVD_SECTORSIZE );
  1709. // use double buffering
  1710. int numBuffers = 2;
  1711. if ( m_dataSize <= STREAM_BUFFER_DATASIZE || m_dataSize <= numBuffers*bufferSize )
  1712. {
  1713. // no gain for buffering a small file or multiple buffering
  1714. // match the expected transfer with a single buffer
  1715. bufferSize = m_dataSize;
  1716. numBuffers = 1;
  1717. }
  1718. // size in samples of the transfer buffer
  1719. m_bufferSize = bufferSize / m_sampleSize;
  1720. // allocate a transfer buffer
  1721. // matches the size of the streaming buffer exactly
  1722. // ensures that buffers can be filled and then consumed/requeued at the same time
  1723. m_buffer = new char[bufferSize];
  1724. int loopStart;
  1725. if ( source.IsLooped() )
  1726. {
  1727. int loopBlock;
  1728. loopStart = m_pStreamSource->GetLoopingInfo( &loopBlock, NULL, NULL ) * m_sampleSize;
  1729. if ( source.Format() == WAVE_FORMAT_XMA )
  1730. {
  1731. // xma works in blocks, mixer handles inter-block accurate loop positioning
  1732. // block streaming will cycle from the block where the loop occurs
  1733. loopStart = loopBlock * XMA_BLOCK_SIZE;
  1734. }
  1735. }
  1736. else
  1737. {
  1738. // sample not looped
  1739. loopStart = -1;
  1740. }
  1741. // load the file piecewise through a buffering implementation
  1742. m_hStream = wavedatacache->OpenStreamedLoad( pFileName, m_dataSize, m_dataStart, startOffset, loopStart, bufferSize, numBuffers, flags );
  1743. }
  1744. m_bValid = true;
  1745. }
  1746. //-----------------------------------------------------------------------------
  1747. // Purpose:
  1748. //-----------------------------------------------------------------------------
  1749. CWaveDataStreamAsync::~CWaveDataStreamAsync( void )
  1750. {
  1751. if ( IsPC() && m_source.IsPlayOnce() && m_source.CanDelete() )
  1752. {
  1753. m_source.SetPlayOnce( false ); // in case it gets used again
  1754. wavedatacache->Unload( m_hCache );
  1755. }
  1756. if ( IsX360() )
  1757. {
  1758. wavedatacache->CloseStreamedLoad( m_hStream );
  1759. }
  1760. delete [] m_buffer;
  1761. }
  1762. //-----------------------------------------------------------------------------
  1763. // Purpose:
  1764. // Output : char const
  1765. //-----------------------------------------------------------------------------
  1766. char const *CWaveDataStreamAsync::GetFileName()
  1767. {
  1768. static char fn[MAX_PATH];
  1769. if ( m_hFileName )
  1770. {
  1771. if ( g_pFileSystem->String( m_hFileName, fn, sizeof( fn ) ) )
  1772. {
  1773. return fn;
  1774. }
  1775. }
  1776. Assert( 0 );
  1777. return "";
  1778. }
  1779. //-----------------------------------------------------------------------------
  1780. // Purpose:
  1781. // Output : Returns true on success, false on failure.
  1782. //-----------------------------------------------------------------------------
  1783. bool CWaveDataStreamAsync::IsReadyToMix()
  1784. {
  1785. if ( IsPC() )
  1786. {
  1787. // If not async loaded, start mixing right away
  1788. if ( !m_source.IsAsyncLoad() && !snd_async_fullyasync.GetBool() )
  1789. {
  1790. return true;
  1791. }
  1792. bool bCacheValid;
  1793. bool bLoaded = wavedatacache->IsDataLoadCompleted( m_hCache, &bCacheValid );
  1794. if ( !bCacheValid )
  1795. {
  1796. wavedatacache->RestartDataLoad( &m_hCache, GetFileName(), m_dataSize, m_dataStart );
  1797. }
  1798. return bLoaded;
  1799. }
  1800. if ( IsX360() )
  1801. {
  1802. return wavedatacache->IsStreamedDataReady( m_hStream );
  1803. }
  1804. return false;
  1805. }
  1806. //-----------------------------------------------------------------------------
  1807. // Purpose: Read data from the source - this is the primary function of a IWaveData subclass
  1808. // Get the data from the buffer (or reload from disk)
  1809. // Input : **pData -
  1810. // sampleIndex -
  1811. // sampleCount -
  1812. // copyBuf[AUDIOSOURCE_COPYBUF_SIZE] -
  1813. // Output : int
  1814. //-----------------------------------------------------------------------------
  1815. int CWaveDataStreamAsync::ReadSourceData( void **pData, int sampleIndex, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] )
  1816. {
  1817. // Current file position
  1818. int seekpos = m_dataStart + m_sampleIndex * m_sampleSize;
  1819. // wrap position if looping
  1820. if ( m_source.IsLooped() )
  1821. {
  1822. sampleIndex = m_pStreamSource->UpdateLoopingSamplePosition( sampleIndex );
  1823. if ( sampleIndex < m_sampleIndex )
  1824. {
  1825. // looped back, buffer has no samples yet
  1826. m_sampleIndex = sampleIndex;
  1827. m_bufferCount = 0;
  1828. // update file position
  1829. seekpos = m_dataStart + sampleIndex * m_sampleSize;
  1830. }
  1831. }
  1832. // UNDONE: This is an error!!
  1833. // The mixer playing back the stream tried to go backwards!?!?!
  1834. // BUGBUG: Just play the beginning of the buffer until we get to a valid linear position
  1835. if ( sampleIndex < m_sampleIndex )
  1836. sampleIndex = m_sampleIndex;
  1837. // calc sample position relative to the current buffer
  1838. // m_sampleIndex is the sample position of the first byte of the buffer
  1839. sampleIndex -= m_sampleIndex;
  1840. // out of range? refresh buffer
  1841. if ( sampleIndex >= m_bufferCount )
  1842. {
  1843. // advance one buffer (the file is positioned here)
  1844. m_sampleIndex += m_bufferCount;
  1845. // next sample to load
  1846. sampleIndex -= m_bufferCount;
  1847. // if the remainder is greated than one buffer size, seek over it. Otherwise, read the next chunk
  1848. // and leave the remainder as an offset.
  1849. // number of buffers to "skip" (as in the case where we are starting a streaming sound not at the beginning)
  1850. int skips = sampleIndex / m_bufferSize;
  1851. // If we are skipping over a buffer, do it with a seek instead of a read.
  1852. if ( skips )
  1853. {
  1854. // skip directly to next position
  1855. m_sampleIndex += sampleIndex;
  1856. sampleIndex = 0;
  1857. }
  1858. // move the file to the new position
  1859. seekpos = m_dataStart + (m_sampleIndex * m_sampleSize);
  1860. // This is the maximum number of samples we could read from the file
  1861. m_bufferCount = m_waveSize - m_sampleIndex;
  1862. // past the end of the file? stop the wave.
  1863. if ( m_bufferCount <= 0 )
  1864. return 0;
  1865. // clamp available samples to buffer size
  1866. if ( m_bufferCount > m_bufferSize )
  1867. m_bufferCount = m_bufferSize;
  1868. if ( IsPC() )
  1869. {
  1870. // See if we can load in the intial data right out of the cached data lump instead.
  1871. int cacheddatastartpos = ( seekpos - m_dataStart );
  1872. // FastGet doesn't call into IsPrecachedSound if the handle appears valid...
  1873. CAudioSourceCachedInfo *info = m_AudioCacheHandle.FastGet();
  1874. if ( !info )
  1875. {
  1876. // Full recache
  1877. info = m_AudioCacheHandle.Get( CAudioSource::AUDIO_SOURCE_WAV, m_pSfx->IsPrecachedSound(), m_pSfx, &m_nCachedDataSize );
  1878. }
  1879. bool startupCacheUsed = false;
  1880. if ( info &&
  1881. ( m_nCachedDataSize > 0 ) &&
  1882. ( cacheddatastartpos < m_nCachedDataSize ) )
  1883. {
  1884. // Get a ptr to the cached data
  1885. const byte *cacheddata = info->CachedData();
  1886. if ( cacheddata )
  1887. {
  1888. // See how many samples of cached data are available (cacheddatastartpos is zero on the first read)
  1889. int availSamples = ( m_nCachedDataSize - cacheddatastartpos ) / m_sampleSize;
  1890. // Clamp to size of our internal buffer
  1891. if ( availSamples > m_bufferSize )
  1892. {
  1893. availSamples = m_bufferSize;
  1894. }
  1895. // Mark how many we are returning
  1896. m_bufferCount = availSamples;
  1897. // Copy raw sample data directly out of cache
  1898. Q_memcpy( m_buffer, ( char * )cacheddata + cacheddatastartpos, availSamples * m_sampleSize );
  1899. startupCacheUsed = true;
  1900. }
  1901. }
  1902. // Not in startup cache, grab data from async cache loader (will block if data hasn't arrived yet)
  1903. if ( !startupCacheUsed )
  1904. {
  1905. bool postprocessed = false;
  1906. // read in the max bufferable, available samples
  1907. if ( !wavedatacache->CopyDataIntoMemory(
  1908. m_hCache,
  1909. GetFileName(),
  1910. m_dataSize,
  1911. m_dataStart,
  1912. m_buffer,
  1913. sizeof( m_buffer ),
  1914. seekpos,
  1915. m_bufferCount * m_sampleSize,
  1916. &postprocessed ) )
  1917. {
  1918. return 0;
  1919. }
  1920. // do any conversion the source needs (mixer will decode/decompress)
  1921. if ( !postprocessed )
  1922. {
  1923. // Note that we don't set the postprocessed flag on the underlying data, since for streaming we're copying the
  1924. // original data into this buffer instead.
  1925. m_pStreamSource->UpdateSamples( m_buffer, m_bufferCount );
  1926. }
  1927. }
  1928. }
  1929. if ( IsX360() )
  1930. {
  1931. if ( m_hStream != INVALID_STREAM_HANDLE )
  1932. {
  1933. // request available data, may get less
  1934. // drives the buffering
  1935. m_bufferCount = wavedatacache->CopyStreamedDataIntoMemory(
  1936. m_hStream,
  1937. m_buffer,
  1938. m_bufferSize * m_sampleSize,
  1939. seekpos,
  1940. m_bufferCount * m_sampleSize );
  1941. // convert to number of samples in the buffer
  1942. m_bufferCount /= m_sampleSize;
  1943. }
  1944. else
  1945. {
  1946. return 0;
  1947. }
  1948. // do any conversion now the source needs (mixer will decode/decompress) on this buffer
  1949. m_pStreamSource->UpdateSamples( m_buffer, m_bufferCount );
  1950. }
  1951. }
  1952. // If we have some samples in the buffer that are within range of the request
  1953. // Use unsigned comparisons so that if sampleIndex is somehow negative that
  1954. // will be treated as out of range.
  1955. if ( (unsigned)sampleIndex < (unsigned)m_bufferCount )
  1956. {
  1957. // Get the desired starting sample
  1958. *pData = (void *)&m_buffer[sampleIndex * m_sampleSize];
  1959. // max available
  1960. int available = m_bufferCount - sampleIndex;
  1961. // clamp available to max requested
  1962. if ( available > sampleCount )
  1963. available = sampleCount;
  1964. return available;
  1965. }
  1966. return 0;
  1967. }
  1968. //-----------------------------------------------------------------------------
  1969. // Purpose: Iterator for wave data (this is to abstract streaming/buffering)
  1970. //-----------------------------------------------------------------------------
  1971. class CWaveDataMemoryAsync : public IWaveData
  1972. {
  1973. public:
  1974. CWaveDataMemoryAsync( CAudioSource &source );
  1975. ~CWaveDataMemoryAsync( void ) {}
  1976. CAudioSource &Source( void ) { return m_source; }
  1977. virtual int ReadSourceData( void **pData, int sampleIndex, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] );
  1978. virtual bool IsReadyToMix();
  1979. private:
  1980. CAudioSource &m_source; // pointer to source
  1981. };
  1982. //-----------------------------------------------------------------------------
  1983. // Purpose:
  1984. // Input : &source -
  1985. //-----------------------------------------------------------------------------
  1986. CWaveDataMemoryAsync::CWaveDataMemoryAsync( CAudioSource &source ) :
  1987. m_source(source)
  1988. {
  1989. }
  1990. //-----------------------------------------------------------------------------
  1991. // Purpose:
  1992. // Input : **pData -
  1993. // sampleIndex -
  1994. // sampleCount -
  1995. // copyBuf[AUDIOSOURCE_COPYBUF_SIZE] -
  1996. // Output : int
  1997. //-----------------------------------------------------------------------------
  1998. int CWaveDataMemoryAsync::ReadSourceData( void **pData, int sampleIndex, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] )
  1999. {
  2000. return m_source.GetOutputData( pData, sampleIndex, sampleCount, copyBuf );
  2001. }
  2002. //-----------------------------------------------------------------------------
  2003. // Purpose:
  2004. // Output : Returns true on success, false on failure.
  2005. //-----------------------------------------------------------------------------
  2006. bool CWaveDataMemoryAsync::IsReadyToMix()
  2007. {
  2008. if ( !m_source.IsAsyncLoad() && !snd_async_fullyasync.GetBool() )
  2009. {
  2010. // Wait until we're pending at least
  2011. if ( m_source.GetCacheStatus() == CAudioSource::AUDIO_NOT_LOADED )
  2012. {
  2013. return false;
  2014. }
  2015. return true;
  2016. }
  2017. if ( m_source.IsCached() )
  2018. {
  2019. return true;
  2020. }
  2021. if ( IsPC() )
  2022. {
  2023. // Msg( "Waiting for data '%s'\n", m_source.GetFileName() );
  2024. m_source.CacheLoad();
  2025. }
  2026. if ( IsX360() )
  2027. {
  2028. // expected to be resident and valid, otherwise being called prior to load
  2029. Assert( 0 );
  2030. }
  2031. return false;
  2032. }
  2033. //-----------------------------------------------------------------------------
  2034. // Purpose:
  2035. // Input : &source -
  2036. // *pStreamSource -
  2037. // &io -
  2038. // *pFileName -
  2039. // dataOffset -
  2040. // dataSize -
  2041. // Output : IWaveData
  2042. //-----------------------------------------------------------------------------
  2043. IWaveData *CreateWaveDataStream( CAudioSource &source, IWaveStreamSource *pStreamSource, const char *pFileName, int dataStart, int dataSize, CSfxTable *pSfx, int startOffset )
  2044. {
  2045. CWaveDataStreamAsync *pStream = new CWaveDataStreamAsync( source, pStreamSource, pFileName, dataStart, dataSize, pSfx, startOffset );
  2046. if ( !pStream || !pStream->IsValid() )
  2047. {
  2048. delete pStream;
  2049. pStream = NULL;
  2050. }
  2051. return pStream;
  2052. }
  2053. //-----------------------------------------------------------------------------
  2054. // Purpose:
  2055. // Input : &source -
  2056. // Output : IWaveData
  2057. //-----------------------------------------------------------------------------
  2058. IWaveData *CreateWaveDataMemory( CAudioSource &source )
  2059. {
  2060. CWaveDataMemoryAsync *mem = new CWaveDataMemoryAsync( source );
  2061. return mem;
  2062. }