Counter Strike : Global Offensive Source Code
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.

3487 lines
90 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "audio_pch.h"
  9. #include "snd_mp3_source.h"
  10. #include "utlsymbol.h"
  11. #include "checksum_crc.h"
  12. #include "../../host.h"
  13. #include "xwvfile.h"
  14. #include "filesystem/IQueuedLoader.h"
  15. #include "tier1/lzmaDecoder.h"
  16. #include "tier1/fmtstr.h"
  17. #include "characterset.h"
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include "tier0/memdbgon.h"
  20. // This determines how much data to pre-cache (will invalidate per-map caches if changed).
  21. #define SOUND_DIRECTORY_LENGTH 6 // i.e., Q_strlen( "sound/" )
  22. #define MASTER_CACHE_NAME "_master"
  23. #define SOUND_PREFETCH_FILE "scripts/sound_prefetch.txt"
  24. extern ConVar snd_async_spew_blocking;
  25. extern double realtime;
  26. ConVar snd_async_minsize("snd_async_minsize", "262144");
  27. static ConVar snd_prefetch_common( "snd_prefetch_common", "1", FCVAR_RELEASE, "Prefetch common sounds from directories specified in " SOUND_PREFETCH_FILE );
  28. ConVar force_audio_english( "force_audio_english", "0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_GAMECONSOLE, "Keeps track of whether we're forcing english in a localized language." );
  29. // #define DEBUG_CHUNKS
  30. //-----------------------------------------------------------------------------
  31. // Purpose: Report chunk error
  32. // Input : id - chunk FOURCC
  33. //-----------------------------------------------------------------------------
  34. void ChunkError( unsigned int id )
  35. {
  36. #if defined( DEBUG_CHUNKS ) && defined( _DEBUG )
  37. if ( id == WAVE_LIST || id == WAVE_FACT )
  38. {
  39. // unused chunks, not an error
  40. return;
  41. }
  42. char tmp[256];
  43. char idname[5];
  44. idname[4] = 0;
  45. memcpy( idname, &id, 4 );
  46. Q_snprintf( tmp, sizeof( tmp ), "Unhandled chunk %s\n", idname );
  47. Plat_DebugString( tmp );
  48. #endif
  49. }
  50. //-----------------------------------------------------------------------------
  51. // Purpose: Determine a true sample count for an ADPCM blob
  52. //-----------------------------------------------------------------------------
  53. int ADPCMSampleCount( ADPCMWAVEFORMAT *pFormat, int length )
  54. {
  55. // determine a true sample count
  56. int nChannels = LittleWord( pFormat->wfx.nChannels );
  57. int wSamplesPerBlock = LittleWord( pFormat->wSamplesPerBlock );
  58. int blockSize = (( wSamplesPerBlock - 2) * nChannels ) / 2;
  59. blockSize += 7 * nChannels;
  60. int blockCount = length / blockSize;
  61. int blockRem = length % blockSize;
  62. // total samples in complete blocks
  63. int sampleCount = blockCount * wSamplesPerBlock;
  64. // add remaining in a short block
  65. if ( blockRem )
  66. {
  67. sampleCount += wSamplesPerBlock - (((blockSize - blockRem) * 2) / nChannels );
  68. }
  69. return sampleCount;
  70. }
  71. //-----------------------------------------------------------------------------
  72. // Purpose: Init to empty wave
  73. //-----------------------------------------------------------------------------
  74. CAudioSourceWave::CAudioSourceWave( CSfxTable *pSfx )
  75. {
  76. m_format = 0;
  77. m_pHeader = NULL;
  78. m_nHeaderSize = 0;
  79. // no looping
  80. m_loopStart = -1;
  81. m_sampleSize = 1;
  82. m_sampleCount = 0;
  83. m_bits = 0;
  84. m_channels = 0;
  85. m_dataStart = 0;
  86. m_dataSize = 0;
  87. m_rate = 0;
  88. m_refCount = 0;
  89. m_pSfx = pSfx;
  90. #ifdef _DEBUG
  91. if ( m_pSfx )
  92. {
  93. char buf[MAX_PATH];
  94. m_pDebugName = strdup( m_pSfx->getname(buf, sizeof(buf)) );
  95. }
  96. #endif
  97. m_bNoSentence = false;
  98. m_pTempSentence = NULL;
  99. m_nCachedDataSize = 0;
  100. m_bIsPlayOnce = false;
  101. m_bIsSentenceWord = false;
  102. m_numDecodedSamples = 0;
  103. // TERROR: limit data read while rebuilding cache
  104. m_bIsRebuildingCache = false;
  105. }
  106. CAudioSourceWave::CAudioSourceWave( CSfxTable *pSfx, CAudioSourceCachedInfo *info )
  107. {
  108. m_pSfx = pSfx;
  109. #ifdef _DEBUG
  110. if ( m_pSfx )
  111. {
  112. char buf[MAX_PATH];
  113. m_pDebugName = strdup( m_pSfx->getname(buf, sizeof(buf)) );
  114. }
  115. #endif
  116. m_refCount = 0;
  117. m_pHeader = NULL;
  118. m_nHeaderSize = 0;
  119. if ( info->HeaderData() )
  120. {
  121. m_pHeader = new char[ info->HeaderSize() ];
  122. Assert( m_pHeader );
  123. Q_memcpy( m_pHeader, info->HeaderData(), info->HeaderSize() );
  124. m_nHeaderSize = info->HeaderSize();
  125. }
  126. m_bits = info->Bits();
  127. m_channels = info->Channels();
  128. m_sampleSize = info->SampleSize();
  129. m_format = info->Format();
  130. m_dataStart = info->DataStart();
  131. m_dataSize = info->DataSize();
  132. m_rate = info->SampleRate();
  133. m_loopStart = info->LoopStart();
  134. m_sampleCount = info->SampleCount();
  135. m_numDecodedSamples = m_sampleCount;
  136. if ( m_format == WAVE_FORMAT_ADPCM && m_pHeader )
  137. {
  138. m_numDecodedSamples = ADPCMSampleCount( (ADPCMWAVEFORMAT *)m_pHeader, m_sampleCount );
  139. }
  140. m_bNoSentence = false;
  141. m_pTempSentence = NULL;
  142. m_nCachedDataSize = 0;
  143. m_bIsPlayOnce = false;
  144. m_bIsSentenceWord = false;
  145. // TERROR: limit data read while rebuilding cache
  146. m_bIsRebuildingCache = false;
  147. }
  148. CAudioSourceWave::~CAudioSourceWave( void )
  149. {
  150. #if _DEBUG
  151. if ( !CanDelete() )
  152. Assert(0);
  153. #endif
  154. // for non-standard waves, we store a copy of the header in RAM
  155. delete[] m_pHeader;
  156. delete m_pTempSentence;
  157. }
  158. int CAudioSourceWave::GetType( void )
  159. {
  160. return AUDIO_SOURCE_WAV;
  161. }
  162. void CAudioSourceWave::GetCacheData( CAudioSourceCachedInfo *info )
  163. {
  164. Assert( info->Type() == CAudioSource::AUDIO_SOURCE_WAV );
  165. int nFileSize;
  166. if (GetStartupData( nFileSize ) == false)
  167. {
  168. m_dataStart = 0;
  169. m_dataSize = nFileSize;
  170. // START OF HACK - HACK - HACK! Necessary for Portal 2 so it fits in one single DVD
  171. // If that's not a WAV file, assume that's an MP3
  172. // Unfortunately shortcomings of the sound cache force us to do this here
  173. info->SetBits( 8 );
  174. info->SetChannels( 1 ); // Make this more accurate at some point
  175. info->SetSampleRate( 44100 ); // Make this more accurate at some point
  176. info->SetLoopStart( -1 ); // No loop
  177. info->SetSampleCount( m_dataSize );
  178. info->SetDataSize( m_dataSize );
  179. info->SetDataStart( 0 );
  180. info->SetType( CAudioSource::AUDIO_SOURCE_MP3 );
  181. // END OF HACK - HACK - HACK!
  182. return;
  183. }
  184. info->SetBits( m_bits );
  185. info->SetChannels( m_channels );
  186. info->SetSampleSize( m_sampleSize );
  187. info->SetFormat( m_format );
  188. info->SetDataStart( m_dataStart ); // offset of wave data chunk
  189. info->SetDataSize( m_dataSize ); // size of wave data chunk
  190. info->SetSampleRate( m_rate );
  191. info->SetLoopStart( m_loopStart );
  192. info->SetSampleCount( m_sampleCount );
  193. if ( m_pTempSentence )
  194. {
  195. CSentence *scopy = new CSentence;
  196. *scopy = *m_pTempSentence;
  197. info->SetSentence( scopy );
  198. // Wipe it down to basically nothing
  199. delete m_pTempSentence;
  200. m_pTempSentence = NULL;
  201. }
  202. if ( m_pHeader && m_nHeaderSize > 0 )
  203. {
  204. byte *data = new byte[ m_nHeaderSize ];
  205. Q_memcpy( data, m_pHeader, m_nHeaderSize );
  206. info->SetHeaderSize( m_nHeaderSize );
  207. info->SetHeaderData( data );
  208. }
  209. }
  210. //-----------------------------------------------------------------------------
  211. // Purpose:
  212. // Output : char const
  213. //-----------------------------------------------------------------------------
  214. char const *CAudioSourceWave::GetFileName( char *pOutBuf, size_t bufLen )
  215. {
  216. return m_pSfx ? m_pSfx->GetFileName( pOutBuf, bufLen ) : "NULL m_pSfx";
  217. }
  218. //-----------------------------------------------------------------------------
  219. // Purpose:
  220. // Output : Returns true on success, false on failure.
  221. //-----------------------------------------------------------------------------
  222. bool CAudioSourceWave::IsAsyncLoad()
  223. {
  224. VPROF("CAudioSourceWave::IsAsyncLoad");
  225. if ( ( IsPC() || !IsGameConsole() ) && !m_AudioCacheHandle.IsValid() )
  226. {
  227. m_AudioCacheHandle.Get( GetType(), m_pSfx->IsPrecachedSound(), m_pSfx, &m_nCachedDataSize );
  228. }
  229. // If there's a bit of "cached data" then we don't have to lazy/async load (we still async load the remaining data,
  230. // but we run from the cache initially)
  231. if ( m_dataSize > snd_async_minsize.GetInt() )
  232. return true;
  233. return ( m_nCachedDataSize > 0 ) ? false : true;
  234. }
  235. //-----------------------------------------------------------------------------
  236. // Purpose:
  237. //-----------------------------------------------------------------------------
  238. void CAudioSourceWave::CheckAudioSourceCache()
  239. {
  240. if ( IsGameConsole() )
  241. {
  242. // 360 does not use audio cache files
  243. return;
  244. }
  245. Assert( m_pSfx );
  246. if ( !m_pSfx->IsPrecachedSound() )
  247. {
  248. return;
  249. }
  250. // This will "re-cache" this if it's not in this level's cache already
  251. m_AudioCacheHandle.Get( GetType(), true, m_pSfx, &m_nCachedDataSize );
  252. }
  253. //-----------------------------------------------------------------------------
  254. // Purpose: Init the wave data.
  255. // Input : *pHeaderBuffer - the RIFF fmt chunk
  256. // headerSize - size of that chunk
  257. //-----------------------------------------------------------------------------
  258. void CAudioSourceWave::Init( const char *pHeaderBuffer, int headerSize )
  259. {
  260. const WAVEFORMATEX *pHeader = (const WAVEFORMATEX *)pHeaderBuffer;
  261. // copy the relevant header data
  262. m_format = LittleWord( pHeader->wFormatTag );
  263. m_bits = LittleWord( pHeader->wBitsPerSample );
  264. m_rate = LittleDWord( pHeader->nSamplesPerSec );
  265. m_channels = LittleWord( pHeader->nChannels );
  266. m_sampleSize = (m_bits * m_channels)/8;
  267. // this can never be zero -- other functions divide by this.
  268. // this should never happen, but avoid crashing
  269. if ( m_sampleSize <= 0 )
  270. {
  271. m_sampleSize = 1;
  272. }
  273. if ( m_format == WAVE_FORMAT_ADPCM )
  274. {
  275. // For non-standard waves (like ADPCM) store the header, it has the decoding coefficients
  276. m_pHeader = new char[headerSize];
  277. memcpy( m_pHeader, pHeader, headerSize );
  278. m_nHeaderSize = headerSize;
  279. // treat ADPCM sources as a file of bytes. They are decoded by the mixer
  280. m_sampleSize = 1;
  281. }
  282. }
  283. int CAudioSourceWave::SampleRate( void )
  284. {
  285. return m_rate;
  286. }
  287. //-----------------------------------------------------------------------------
  288. // Purpose: Size of each sample
  289. // Output :
  290. //-----------------------------------------------------------------------------
  291. int CAudioSourceWave::SampleSize( void )
  292. {
  293. return m_sampleSize;
  294. }
  295. //-----------------------------------------------------------------------------
  296. // Purpose: Total number of samples in this source
  297. // Output : int
  298. //-----------------------------------------------------------------------------
  299. int CAudioSourceWave::SampleCount( void )
  300. {
  301. // caller wants real samples
  302. return m_numDecodedSamples;
  303. }
  304. int CAudioSourceWave::Format( void )
  305. {
  306. return m_format;
  307. }
  308. int CAudioSourceWave::DataSize( void )
  309. {
  310. return m_dataSize;
  311. }
  312. bool CAudioSourceWave::IsVoiceSource()
  313. {
  314. if ( GetSentence() )
  315. {
  316. if ( GetSentence()->GetVoiceDuck() )
  317. return true;
  318. }
  319. return false;
  320. }
  321. //-----------------------------------------------------------------------------
  322. // Purpose: Do any sample conversion
  323. // For 8 bit PCM, convert to signed because the mixing routine assumes this
  324. // Input : *pData - pointer to sample data
  325. // sampleCount - number of samples
  326. //-----------------------------------------------------------------------------
  327. void CAudioSourceWave::ConvertSamples( char *pData, int sampleCount )
  328. {
  329. if ( m_format == WAVE_FORMAT_PCM )
  330. {
  331. if ( m_bits == 8 )
  332. {
  333. for ( int i = 0; i < sampleCount*m_channels; i++ )
  334. {
  335. *pData = (unsigned char)((int)((unsigned)*pData) - 128);
  336. pData++;
  337. }
  338. }
  339. }
  340. }
  341. //-----------------------------------------------------------------------------
  342. // Purpose: Parse base chunks
  343. // Input : &walk - riff file to parse
  344. // : chunkName - name of the chunk to parse
  345. //-----------------------------------------------------------------------------
  346. // UNDONE: Move parsing loop here and drop each chunk into a virtual function
  347. // instead of this being virtual.
  348. void CAudioSourceWave::ParseChunk( IterateRIFF &walk, int chunkName )
  349. {
  350. switch( chunkName )
  351. {
  352. case WAVE_CUE:
  353. ParseCueChunk( walk );
  354. break;
  355. case WAVE_SAMPLER:
  356. ParseSamplerChunk( walk );
  357. break;
  358. case WAVE_VALVEDATA:
  359. ParseSentence( walk );
  360. break;
  361. default:
  362. // unknown and don't care
  363. ChunkError( walk.ChunkName() );
  364. break;
  365. }
  366. }
  367. bool CAudioSourceWave::IsLooped( void )
  368. {
  369. return (m_loopStart >= 0) ? true : false;
  370. }
  371. bool CAudioSourceWave::IsStereoWav( void )
  372. {
  373. return (m_channels == 2) ? true : false;
  374. }
  375. bool CAudioSourceWave::IsStreaming( void )
  376. {
  377. return false;
  378. }
  379. int CAudioSourceWave::GetCacheStatus( void )
  380. {
  381. return AUDIO_IS_LOADED;
  382. }
  383. void CAudioSourceWave::CacheLoad( void )
  384. {
  385. }
  386. void CAudioSourceWave::CacheUnload( void )
  387. {
  388. }
  389. int CAudioSourceWave::ZeroCrossingBefore( int sample )
  390. {
  391. return sample;
  392. }
  393. int CAudioSourceWave::ZeroCrossingAfter( int sample )
  394. {
  395. return sample;
  396. }
  397. //-----------------------------------------------------------------------------
  398. // Purpose:
  399. // Input : &walk -
  400. //-----------------------------------------------------------------------------
  401. void CAudioSourceWave::ParseSentence( IterateRIFF &walk )
  402. {
  403. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  404. buf.EnsureCapacity( walk.ChunkSize() );
  405. walk.ChunkRead( buf.Base() );
  406. buf.SeekPut( CUtlBuffer::SEEK_HEAD, walk.ChunkSize() );
  407. m_pTempSentence = new CSentence();
  408. Assert( m_pTempSentence );
  409. m_pTempSentence->InitFromDataChunk( buf.Base(), buf.TellPut() );
  410. // Throws all phonemes into one word, discards sentence memory, etc.
  411. m_pTempSentence->MakeRuntimeOnly();
  412. }
  413. //-----------------------------------------------------------------------------
  414. // Purpose:
  415. // Output : CSentence
  416. //-----------------------------------------------------------------------------
  417. CSentence *CAudioSourceWave::GetSentence( void )
  418. {
  419. if ( IsGameConsole() )
  420. {
  421. return m_pTempSentence;
  422. }
  423. // Already checked and this wav doesn't have sentence data...
  424. if ( m_bNoSentence == true )
  425. {
  426. return NULL;
  427. }
  428. // Look up sentence from cache
  429. CAudioSourceCachedInfo *info = m_AudioCacheHandle.FastGet();
  430. if ( !info )
  431. {
  432. info = m_AudioCacheHandle.Get( CAudioSource::AUDIO_SOURCE_WAV, m_pSfx->IsPrecachedSound(), m_pSfx, &m_nCachedDataSize );
  433. }
  434. Assert( info );
  435. if ( !info )
  436. {
  437. m_bNoSentence = true;
  438. return NULL;
  439. }
  440. CSentence *sentence = info->Sentence();
  441. if ( !sentence )
  442. {
  443. m_bNoSentence = true;
  444. return NULL;
  445. }
  446. if ( sentence->m_bIsValid )
  447. {
  448. return sentence;
  449. }
  450. m_bNoSentence = true;
  451. return NULL;
  452. }
  453. const char *CAudioSourceWave::GetName( char *pBuf, size_t bufLen )
  454. {
  455. return m_pSfx ? m_pSfx->getname(pBuf, bufLen) : NULL;
  456. }
  457. int CAudioSourceWave::GetQuality()
  458. {
  459. return ( m_format == WAVE_FORMAT_XMA ? m_quality : 0 );
  460. }
  461. PathTypeFilter_t GetAudioPathFilter()
  462. {
  463. if ( XBX_IsAudioLocalized() && force_audio_english.GetBool() )
  464. {
  465. // skip the localized search paths and fall through to the primary zips
  466. return FILTER_CULLLOCALIZED;
  467. }
  468. // No audio exists outside of zips, all the audio is inside the zips
  469. return FILTER_CULLNONPACK;
  470. }
  471. //-----------------------------------------------------------------------------
  472. // Load a native xaudio or legacy wav
  473. //-----------------------------------------------------------------------------
  474. bool CAudioSourceWave::GetXboxAudioStartupData()
  475. {
  476. CUtlBuffer buf;
  477. char fileName[MAX_PATH];
  478. char tempFileName[MAX_PATH];
  479. MEM_ALLOC_CREDIT();
  480. // try native optimal xma wav file first
  481. Q_StripExtension( m_pSfx->GetFileName(fileName, sizeof(fileName)), tempFileName, sizeof( tempFileName ) );
  482. Q_snprintf( fileName, sizeof( fileName ), "sound\\%s" PLATFORM_EXT ".wav", tempFileName );
  483. V_FixDoubleSlashes( fileName );
  484. PathTypeQuery_t pathType;
  485. char szFullName[MAX_PATH];
  486. if ( !g_pFullFileSystem->RelativePathToFullPath( fileName, "GAME", szFullName, sizeof( szFullName ), GetAudioPathFilter(), &pathType ) )
  487. {
  488. // not found, not supported
  489. return false;
  490. }
  491. if ( !g_pFullFileSystem->ReadFile( szFullName, "GAME", buf, sizeof( xwvHeader_t ) ) )
  492. {
  493. // not found, not supported
  494. return false;
  495. }
  496. else
  497. {
  498. xwvHeader_t* pHeader = (xwvHeader_t *)buf.Base();
  499. if ( pHeader->id != XWV_ID || pHeader->version != XWV_VERSION )
  500. {
  501. return false;
  502. }
  503. switch ( pHeader->format )
  504. {
  505. case XWV_FORMAT_XMA:
  506. m_format = WAVE_FORMAT_XMA;
  507. break;
  508. case XWV_FORMAT_PCM:
  509. m_format = WAVE_FORMAT_PCM;
  510. break;
  511. case XWV_FORMAT_MP3:
  512. m_format = WAVE_FORMAT_MP3;
  513. break;
  514. case XWV_FORMAT_TEMP:
  515. m_format = WAVE_FORMAT_TEMP;
  516. break;
  517. default:
  518. // unknown
  519. return false;
  520. }
  521. m_rate = pHeader->GetSampleRate();
  522. m_channels = pHeader->channels;
  523. m_dataStart = pHeader->dataOffset;
  524. m_dataSize = pHeader->dataSize;
  525. m_quality = pHeader->quality;
  526. m_loopStart = pHeader->loopStart;
  527. m_loopBlock = pHeader->loopBlock;
  528. m_numLeadingSamples = pHeader->numLeadingSamples;
  529. m_numTrailingSamples = pHeader->numTrailingSamples;
  530. switch (m_format)
  531. {
  532. case WAVE_FORMAT_MP3:
  533. case WAVE_FORMAT_TEMP:
  534. // MP3 and temp format do not store numDecodedSamples correctly (in bytes instead of samples).
  535. // We would need to change makegamedata accordingly (corresponding code is commented).
  536. // However I [oliviern] did not want to change the data that late in Portal 2. So patch in the code instead.
  537. pHeader->numDecodedSamples /= m_channels * sizeof( short );
  538. // Pass through...
  539. case WAVE_FORMAT_XMA:
  540. // xma and MP3 are compressed blocks, trick to fool system to treat data as bytes, not samples
  541. // unfortunate, but callers must know xma context and provide offsets in samples or bytes
  542. m_bits = 16;
  543. m_sampleSize = 1;
  544. m_sampleCount = m_dataSize;
  545. break;
  546. default:
  547. m_bits = 16;
  548. m_sampleSize = sizeof( short ) * m_channels;
  549. m_sampleCount = m_dataSize / m_sampleSize;
  550. break;
  551. }
  552. // keep true decoded samples because cannot be easily determined
  553. m_numDecodedSamples = pHeader->numDecodedSamples;
  554. m_bNoSentence = true;
  555. CUtlBuffer fileBuffer;
  556. if ( pHeader->staticDataSize )
  557. {
  558. // get optional data
  559. if ( !g_pFullFileSystem->ReadFile( szFullName, "GAME", fileBuffer, pHeader->staticDataSize, sizeof( xwvHeader_t ) ) )
  560. {
  561. return false;
  562. }
  563. unsigned char *pData = (unsigned char *)fileBuffer.Base() + sizeof( xwvHeader_t );
  564. if ( pHeader->GetSeekTableSize() )
  565. {
  566. // store off the seek table
  567. m_nHeaderSize = pHeader->GetSeekTableSize();
  568. m_pHeader = new char[m_nHeaderSize];
  569. V_memcpy( m_pHeader, pData, m_nHeaderSize );
  570. // advance past optional seek table
  571. pData += m_nHeaderSize;
  572. }
  573. if ( pHeader->vdatSize )
  574. {
  575. m_pTempSentence = new CSentence();
  576. Assert( m_pTempSentence );
  577. m_bNoSentence = false;
  578. // vdat is precompiled into minimal binary format and possibly compressed
  579. CLZMA lzma;
  580. if ( lzma.IsCompressed( pData ) )
  581. {
  582. // uncompress binary vdat and restore
  583. CUtlBuffer targetBuffer;
  584. int originalSize = lzma.GetActualSize( pData );
  585. targetBuffer.EnsureCapacity( originalSize );
  586. lzma.Uncompress( pData, (unsigned char *)targetBuffer.Base() );
  587. targetBuffer.SeekPut( CUtlBuffer::SEEK_HEAD, originalSize );
  588. m_pTempSentence->CacheRestoreFromBuffer( targetBuffer );
  589. }
  590. else
  591. {
  592. m_pTempSentence->CacheRestoreFromBuffer( fileBuffer );
  593. }
  594. }
  595. }
  596. }
  597. return true;
  598. }
  599. //-----------------------------------------------------------------------------
  600. // Purpose: Bastardized construction routine. This is just to avoid complex
  601. // constructor functions so code can be shared more easily by sub-classes
  602. // Input : *pFormatBuffer - RIFF header
  603. // formatSize - header size
  604. // &walk - RIFF file
  605. //-----------------------------------------------------------------------------
  606. void CAudioSourceWave::Setup( const char *pFormatBuffer, int formatSize, IterateRIFF &walk )
  607. {
  608. Init( pFormatBuffer, formatSize );
  609. while ( walk.ChunkAvailable() )
  610. {
  611. ParseChunk( walk, walk.ChunkName() );
  612. walk.ChunkNext();
  613. }
  614. }
  615. bool CAudioSourceWave::GetStartupData( int &nfileSize )
  616. {
  617. char formatBuffer[1024];
  618. char nameBuf[MAX_PATH];
  619. const char *pName = m_pSfx->GetFileName(nameBuf, sizeof(nameBuf));
  620. InFileRIFF riff( pName, *g_pSndIO );
  621. nfileSize = riff.GetFileSize();
  622. if ( riff.RIFFName() != RIFF_WAVE )
  623. {
  624. return false;
  625. }
  626. // set up the iterator for the whole file (root RIFF is a chunk)
  627. IterateRIFF walk( riff, riff.RIFFSize() );
  628. int format = 0;
  629. int formatSize = 0;
  630. // This chunk must be first as it contains the wave's format
  631. // break out when we've parsed it
  632. while ( walk.ChunkAvailable() && format == 0 )
  633. {
  634. switch( walk.ChunkName() )
  635. {
  636. case WAVE_FMT:
  637. {
  638. if ( walk.ChunkSize() <= sizeof( formatBuffer ) )
  639. {
  640. walk.ChunkRead( formatBuffer );
  641. formatSize = walk.ChunkSize();
  642. format = ((WAVEFORMATEX *)formatBuffer)->wFormatTag;
  643. if( ((WAVEFORMATEX *)formatBuffer)->wBitsPerSample > 16)
  644. {
  645. Warning("Unsupported %d-bit wave file %s\n", (int)((WAVEFORMATEX *)formatBuffer)->wBitsPerSample, pName);
  646. }
  647. }
  648. }
  649. break;
  650. default:
  651. {
  652. ChunkError( walk.ChunkName() );
  653. }
  654. break;
  655. }
  656. walk.ChunkNext();
  657. }
  658. // Not really a WAVE file or no format chunk, bail
  659. if ( !format )
  660. {
  661. return false;
  662. }
  663. Setup( formatBuffer, formatSize, walk );
  664. if ( !m_dataStart || !m_dataSize )
  665. {
  666. // failed during setup
  667. return false;
  668. }
  669. return true;
  670. }
  671. //-----------------------------------------------------------------------------
  672. // Purpose: parses loop information from a cue chunk
  673. // Input : &walk - RIFF iterator
  674. // Output : int loop start position
  675. //-----------------------------------------------------------------------------
  676. void CAudioSourceWave::ParseCueChunk( IterateRIFF &walk )
  677. {
  678. // Cue chunk as specified by RIFF format
  679. // see $/research/jay/sound/riffnew.htm
  680. struct
  681. {
  682. unsigned int dwName;
  683. unsigned int dwPosition;
  684. unsigned int fccChunk;
  685. unsigned int dwChunkStart;
  686. unsigned int dwBlockStart;
  687. unsigned int dwSampleOffset;
  688. } cue_chunk;
  689. int cueCount;
  690. // assume that the cue chunk stored in the wave is the start of the loop
  691. // assume only one cue chunk, UNDONE: Test this assumption here?
  692. cueCount = walk.ChunkReadInt();
  693. if ( cueCount > 0 )
  694. {
  695. walk.ChunkReadPartial( &cue_chunk, sizeof(cue_chunk) );
  696. m_loopStart = LittleLong( cue_chunk.dwSampleOffset );
  697. }
  698. }
  699. //-----------------------------------------------------------------------------
  700. // Purpose: parses loop information from a 'smpl' chunk
  701. // Input : &walk - RIFF iterator
  702. // Output : int loop start position
  703. //-----------------------------------------------------------------------------
  704. void CAudioSourceWave::ParseSamplerChunk( IterateRIFF &walk )
  705. {
  706. // Sampler chunk for MIDI instruments
  707. // Parse loop info from this chunk too
  708. struct SampleLoop
  709. {
  710. unsigned int dwIdentifier;
  711. unsigned int dwType;
  712. unsigned int dwStart;
  713. unsigned int dwEnd;
  714. unsigned int dwFraction;
  715. unsigned int dwPlayCount;
  716. };
  717. struct
  718. {
  719. unsigned int dwManufacturer;
  720. unsigned int dwProduct;
  721. unsigned int dwSamplePeriod;
  722. unsigned int dwMIDIUnityNote;
  723. unsigned int dwMIDIPitchFraction;
  724. unsigned int dwSMPTEFormat;
  725. unsigned int dwSMPTEOffset;
  726. unsigned int cSampleLoops;
  727. unsigned int cbSamplerData;
  728. struct SampleLoop Loops[1];
  729. } samplerChunk;
  730. // assume that the loop end is the sample end
  731. // assume that only the first loop is relevant
  732. walk.ChunkReadPartial( &samplerChunk, sizeof(samplerChunk) );
  733. if ( LittleLong( samplerChunk.cSampleLoops ) > 0 )
  734. {
  735. // only support normal forward loops
  736. if ( LittleLong( samplerChunk.Loops[0].dwType ) == 0 )
  737. {
  738. m_loopStart = LittleLong( samplerChunk.Loops[0].dwStart );
  739. }
  740. #ifdef _DEBUG
  741. else
  742. {
  743. char nameBuf[MAX_PATH];
  744. Msg("Unknown sampler chunk type %d on %s\n", LittleLong( samplerChunk.Loops[0].dwType ), m_pSfx->GetFileName(nameBuf,sizeof(nameBuf)) );
  745. }
  746. #endif
  747. }
  748. // else discard - this is some other non-loop sampler data we don't support
  749. }
  750. //-----------------------------------------------------------------------------
  751. // Purpose: get the wave header
  752. //-----------------------------------------------------------------------------
  753. void *CAudioSourceWave::GetHeader( void )
  754. {
  755. return m_pHeader;
  756. }
  757. //-----------------------------------------------------------------------------
  758. // Gets the looping information. Some parameters are interpreted based on format
  759. //-----------------------------------------------------------------------------
  760. int CAudioSourceWave::GetLoopingInfo( int *pLoopBlock, int *pNumLeadingSamples, int *pNumTrailingSamples )
  761. {
  762. if ( pLoopBlock )
  763. {
  764. // for xma, the block that contains the loop point
  765. *pLoopBlock = m_loopBlock;
  766. }
  767. if ( pNumLeadingSamples )
  768. {
  769. // for xma, the number of leading samples at the loop block to discard
  770. *pNumLeadingSamples = m_numLeadingSamples;
  771. }
  772. if ( pNumTrailingSamples )
  773. {
  774. // for xma, the number of trailing samples at the final block to discard
  775. *pNumTrailingSamples = m_numTrailingSamples;
  776. }
  777. // the loop point in samples
  778. return m_loopStart;
  779. }
  780. //-----------------------------------------------------------------------------
  781. // Purpose: wrap the position wrt looping
  782. // Input : samplePosition - absolute position
  783. // Output : int - looped position
  784. //-----------------------------------------------------------------------------
  785. int64 CAudioSourceWave::ConvertLoopedPosition( int64 samplePosition )
  786. {
  787. switch ( m_format )
  788. {
  789. case WAVE_FORMAT_XMA:
  790. case WAVE_FORMAT_MP3:
  791. case WAVE_FORMAT_TEMP:
  792. // xma and mp3 mixer interprets loops and *always* sends a corrected position
  793. return samplePosition;
  794. }
  795. // if the wave is looping and we're past the end of the sample
  796. // convert to a position within the loop
  797. // At the end of the loop, we return a short buffer, and subsequent call
  798. // will loop back and get the rest of the buffer
  799. if ( m_loopStart >= 0 && samplePosition >= m_sampleCount )
  800. {
  801. // size of loop
  802. int loopSize = m_sampleCount - m_loopStart;
  803. // subtract off starting bit of the wave
  804. samplePosition -= m_loopStart;
  805. if ( loopSize )
  806. {
  807. // "real" position in memory (mod off extra loops)
  808. samplePosition = m_loopStart + (samplePosition % loopSize);
  809. }
  810. // ERROR? if no loopSize
  811. }
  812. return samplePosition;
  813. }
  814. //-----------------------------------------------------------------------------
  815. // Purpose: remove the reference for the mixer getting deleted
  816. // Input : *pMixer -
  817. //-----------------------------------------------------------------------------
  818. void CAudioSourceWave::ReferenceRemove( CAudioMixer *pMixer )
  819. {
  820. m_refCount--;
  821. if ( m_refCount == 0 && ( ( IsPC() && IsPlayOnce() ) || ( IsGameConsole() && IsStreaming() ) ) )
  822. {
  823. SetPlayOnce( false ); // in case it gets used again
  824. CacheUnload();
  825. }
  826. }
  827. //-----------------------------------------------------------------------------
  828. // Purpose: Add a mixer reference
  829. // Input : *pMixer -
  830. //-----------------------------------------------------------------------------
  831. void CAudioSourceWave::ReferenceAdd( CAudioMixer *pMixer )
  832. {
  833. m_refCount++;
  834. }
  835. //-----------------------------------------------------------------------------
  836. // Purpose: return true if no mixers reference this source
  837. //-----------------------------------------------------------------------------
  838. bool CAudioSourceWave::CanDelete( void )
  839. {
  840. if ( m_refCount > 0 )
  841. return false;
  842. return true;
  843. }
  844. // CAudioSourceMemWave is a bunch of wave data that is all in memory.
  845. // To use it:
  846. // - derive from CAudioSourceMemWave
  847. // - call CAudioSourceWave::Init with a WAVEFORMATEX
  848. // - set m_sampleCount.
  849. // - implement GetDataPointer
  850. class CAudioSourceMemWave : public CAudioSourceWave
  851. {
  852. public:
  853. CAudioSourceMemWave();
  854. CAudioSourceMemWave( CSfxTable *pSfx );
  855. CAudioSourceMemWave( CSfxTable *pSfx, CAudioSourceCachedInfo *info );
  856. virtual ~CAudioSourceMemWave();
  857. // These are all implemented by CAudioSourceMemWave.
  858. virtual CAudioMixer* CreateMixer( int initialStreamPosition, int skipInitialSamples, bool bUpdateDelayForChoreo, SoundError &soundError, hrtf_info_t* pHRTFVec );
  859. virtual int GetOutputData( void **pData, int64 samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] );
  860. virtual int ZeroCrossingBefore( int sample );
  861. virtual int ZeroCrossingAfter( int sample );
  862. virtual int GetCacheStatus( void );
  863. virtual void CacheLoad( void );
  864. virtual void CacheUnload( void );
  865. // by definition, should already be in memory
  866. virtual void Prefetch() {}
  867. virtual void ParseChunk( IterateRIFF &walk, int chunkName );
  868. void ParseDataChunk( IterateRIFF &walk );
  869. protected:
  870. // Whoeover derives must implement this.
  871. virtual char *GetDataPointer( void );
  872. WaveCacheHandle_t m_hCache;
  873. StreamHandle_t m_hStream;
  874. private:
  875. CAudioSourceMemWave( const CAudioSourceMemWave & ); // not implemented, not accessible
  876. };
  877. CAudioSourceMemWave::CAudioSourceMemWave() :
  878. CAudioSourceWave( NULL )
  879. {
  880. m_hCache = 0;
  881. m_hStream = INVALID_STREAM_HANDLE;
  882. }
  883. CAudioSourceMemWave::CAudioSourceMemWave( CSfxTable *pSfx ) :
  884. CAudioSourceWave( pSfx )
  885. {
  886. m_hCache = 0;
  887. m_hStream = INVALID_STREAM_HANDLE;
  888. if ( IsGameConsole() )
  889. {
  890. bool bValid = GetXboxAudioStartupData();
  891. if ( !bValid )
  892. {
  893. // failed, substitute placeholder
  894. pSfx->m_bUseErrorFilename = true;
  895. bValid = GetXboxAudioStartupData();
  896. if ( bValid )
  897. {
  898. char nameBuf1[MAX_PATH];
  899. char nameBuf2[MAX_PATH];
  900. DevWarning( "Failed to load sound \"%s\", substituting \"%s\"\n", pSfx->getname(nameBuf1,sizeof(nameBuf1)), pSfx->GetFileName(nameBuf2,sizeof(nameBuf2)) );
  901. }
  902. }
  903. if ( bValid )
  904. {
  905. // a 360 memory wave is a critical resource kept locked in memory, load its data now
  906. CacheLoad();
  907. }
  908. }
  909. }
  910. CAudioSourceMemWave::CAudioSourceMemWave( CSfxTable *pSfx, CAudioSourceCachedInfo *info ) :
  911. CAudioSourceWave( pSfx, info )
  912. {
  913. m_hCache = 0;
  914. m_hStream = INVALID_STREAM_HANDLE;
  915. }
  916. CAudioSourceMemWave::~CAudioSourceMemWave()
  917. {
  918. }
  919. //-----------------------------------------------------------------------------
  920. // Purpose: Creates a mixer and initializes it with an appropriate mixer
  921. //-----------------------------------------------------------------------------
  922. CAudioMixer *CAudioSourceMemWave::CreateMixer( int initialStreamPosition, int skipInitialSamples, bool bUpdateDelayForChoreo, SoundError &soundError, hrtf_info_t* pHRTFVector )
  923. {
  924. if (pHRTFVector && m_bits != 16)
  925. {
  926. char filename[256];
  927. this->m_pSfx->GetFileName(filename, sizeof(filename));
  928. DevMsg("Sound %s configured to use HRTF but is not a 16-bit sound\n", filename);
  929. pHRTFVector = nullptr;
  930. }
  931. CAudioMixer *pMixer = CreateWaveMixer( CreateWaveDataHRTF(CreateWaveDataMemory(*this), pHRTFVector), m_format, pHRTFVector ? 2 : m_channels, m_bits, initialStreamPosition, skipInitialSamples, bUpdateDelayForChoreo );
  932. if ( pMixer )
  933. {
  934. ReferenceAdd( pMixer );
  935. soundError = SE_OK;
  936. }
  937. else
  938. {
  939. soundError = SE_CANT_CREATE_MIXER;
  940. }
  941. return pMixer;
  942. }
  943. //-----------------------------------------------------------------------------
  944. // Purpose:
  945. // Input : **pData - output pointer to samples
  946. // samplePosition - position (in samples not bytes)
  947. // sampleCount - number of samples (not bytes)
  948. // Output : int - number of samples available
  949. //-----------------------------------------------------------------------------
  950. int CAudioSourceMemWave::GetOutputData( void **pData, int64 samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] )
  951. {
  952. // handle position looping
  953. samplePosition = ConvertLoopedPosition( samplePosition );
  954. // how many samples are available (linearly not counting looping)
  955. int totalSampleCount = m_sampleCount - samplePosition;
  956. // may be asking for a sample out of range, clip at zero
  957. if ( totalSampleCount < 0 )
  958. {
  959. totalSampleCount = 0;
  960. }
  961. // clip max output samples to max available
  962. if ( sampleCount > totalSampleCount )
  963. {
  964. sampleCount = totalSampleCount;
  965. }
  966. // byte offset in sample database
  967. samplePosition *= m_sampleSize;
  968. // if we are returning some samples, store the pointer
  969. if ( sampleCount )
  970. {
  971. // Starting past end of "preloaded" data, just use regular cache
  972. if ( samplePosition >= m_nCachedDataSize )
  973. {
  974. *pData = GetDataPointer();
  975. }
  976. else
  977. {
  978. if ( IsPC() || !IsGameConsole() )
  979. {
  980. // Start async loader if we haven't already done so
  981. CacheLoad();
  982. // Return less data if we are about to run out of uncached data
  983. if ( samplePosition + ( sampleCount * m_sampleSize ) >= m_nCachedDataSize )
  984. {
  985. sampleCount = ( m_nCachedDataSize - samplePosition ) / m_sampleSize;
  986. }
  987. // Point at preloaded/cached data from .cache file for now
  988. *pData = GetCachedDataPointer();
  989. }
  990. else
  991. {
  992. // for 360, memory wave data should have already been loaded and locked in cache
  993. Assert( 0 );
  994. }
  995. }
  996. if ( *pData )
  997. {
  998. *pData = (char *)*pData + samplePosition;
  999. }
  1000. else
  1001. {
  1002. // End of data or some other problem
  1003. sampleCount = 0;
  1004. }
  1005. }
  1006. return sampleCount;
  1007. }
  1008. // Hardcoded macros to test for zero crossing
  1009. #define ZERO_X_8(b) ((b)<8 && (b)>-8)
  1010. #define ZERO_X_16(b) ((b)<2048 && (b)>-2048)
  1011. //-----------------------------------------------------------------------------
  1012. // Purpose: Search backward for a zero crossing starting at sample
  1013. // Input : sample - starting point
  1014. // Output : position of zero crossing
  1015. //-----------------------------------------------------------------------------
  1016. int CAudioSourceMemWave::ZeroCrossingBefore( int sample )
  1017. {
  1018. char *pWaveData = GetDataPointer();
  1019. if ( m_format == WAVE_FORMAT_PCM )
  1020. {
  1021. if ( m_bits == 8 )
  1022. {
  1023. char *pData = pWaveData + sample * m_sampleSize;
  1024. bool zero = false;
  1025. if ( m_channels == 1 )
  1026. {
  1027. while ( sample > 0 && !zero )
  1028. {
  1029. if ( ZERO_X_8(*pData) )
  1030. zero = true;
  1031. else
  1032. {
  1033. sample--;
  1034. pData--;
  1035. }
  1036. }
  1037. }
  1038. else
  1039. {
  1040. while ( sample > 0 && !zero )
  1041. {
  1042. if ( ZERO_X_8(*pData) && ZERO_X_8(pData[1]) )
  1043. zero = true;
  1044. else
  1045. {
  1046. sample--;
  1047. pData--;
  1048. }
  1049. }
  1050. }
  1051. }
  1052. else
  1053. {
  1054. short *pData = (short *)(pWaveData + sample * m_sampleSize);
  1055. bool zero = false;
  1056. if ( m_channels == 1 )
  1057. {
  1058. while ( sample > 0 && !zero )
  1059. {
  1060. if ( ZERO_X_16(*pData) )
  1061. zero = true;
  1062. else
  1063. {
  1064. pData--;
  1065. sample--;
  1066. }
  1067. }
  1068. }
  1069. else
  1070. {
  1071. while ( sample > 0 && !zero )
  1072. {
  1073. if ( ZERO_X_16(*pData) && ZERO_X_16(pData[1]) )
  1074. zero = true;
  1075. else
  1076. {
  1077. sample--;
  1078. pData--;
  1079. }
  1080. }
  1081. }
  1082. }
  1083. }
  1084. return sample;
  1085. }
  1086. //-----------------------------------------------------------------------------
  1087. // Purpose: Search forward for a zero crossing
  1088. // Input : sample - starting point
  1089. // Output : position of found zero crossing
  1090. //-----------------------------------------------------------------------------
  1091. int CAudioSourceMemWave::ZeroCrossingAfter( int sample )
  1092. {
  1093. char *pWaveData = GetDataPointer();
  1094. Assert( pWaveData );
  1095. if ( !pWaveData )
  1096. return sample;
  1097. if ( m_format == WAVE_FORMAT_PCM )
  1098. {
  1099. if ( m_bits == 8 )
  1100. {
  1101. char *pData = pWaveData + sample * m_sampleSize;
  1102. bool zero = false;
  1103. if ( m_channels == 1 )
  1104. {
  1105. while ( sample < SampleCount() && !zero )
  1106. {
  1107. if ( ZERO_X_8(*pData) )
  1108. zero = true;
  1109. else
  1110. {
  1111. sample++;
  1112. pData++;
  1113. }
  1114. }
  1115. }
  1116. else
  1117. {
  1118. while ( sample < SampleCount() && !zero )
  1119. {
  1120. if ( ZERO_X_8(*pData) && ZERO_X_8(pData[1]) )
  1121. zero = true;
  1122. else
  1123. {
  1124. sample++;
  1125. pData++;
  1126. }
  1127. }
  1128. }
  1129. }
  1130. else
  1131. {
  1132. short *pData = (short *)(pWaveData + sample * m_sampleSize);
  1133. bool zero = false;
  1134. if ( m_channels == 1 )
  1135. {
  1136. while ( sample > 0 && !zero )
  1137. {
  1138. if ( ZERO_X_16(*pData) )
  1139. zero = true;
  1140. else
  1141. {
  1142. pData++;
  1143. sample++;
  1144. }
  1145. }
  1146. }
  1147. else
  1148. {
  1149. while ( sample > 0 && !zero )
  1150. {
  1151. if ( ZERO_X_16(*pData) && ZERO_X_16(pData[1]) )
  1152. zero = true;
  1153. else
  1154. {
  1155. sample++;
  1156. pData++;
  1157. }
  1158. }
  1159. }
  1160. }
  1161. }
  1162. return sample;
  1163. }
  1164. //-----------------------------------------------------------------------------
  1165. // Purpose: parse chunks with unique processing to in-memory waves
  1166. // Input : &walk - RIFF file
  1167. //-----------------------------------------------------------------------------
  1168. void CAudioSourceMemWave::ParseChunk( IterateRIFF &walk, int chunkName )
  1169. {
  1170. switch( chunkName )
  1171. {
  1172. // this is the audio data
  1173. case WAVE_DATA:
  1174. ParseDataChunk( walk );
  1175. return;
  1176. }
  1177. CAudioSourceWave::ParseChunk( walk, chunkName );
  1178. }
  1179. //-----------------------------------------------------------------------------
  1180. // Purpose: reads the actual sample data and parses it
  1181. // Input : &walk - RIFF file
  1182. //-----------------------------------------------------------------------------
  1183. void CAudioSourceMemWave::ParseDataChunk( IterateRIFF &walk )
  1184. {
  1185. m_dataStart = walk.ChunkFilePosition() + 8;
  1186. m_dataSize = walk.ChunkSize();
  1187. // 360 streaming model loads data later, but still needs critical member setup
  1188. char *pData = NULL;
  1189. if ( IsPC() || !IsGameConsole() )
  1190. {
  1191. pData = GetDataPointer();
  1192. if ( !pData )
  1193. {
  1194. char nameBuf[MAX_PATH];
  1195. Error( "CAudioSourceMemWave (%s): GetDataPointer() failed.", m_pSfx ? m_pSfx->GetFileName(nameBuf, sizeof(nameBuf)) : "m_pSfx = NULL" );
  1196. }
  1197. // load them into memory (bad!!, this is a duplicate read of the data chunk)
  1198. walk.ChunkRead( pData );
  1199. }
  1200. if ( m_format == WAVE_FORMAT_PCM )
  1201. {
  1202. // number of samples loaded
  1203. m_sampleCount = m_dataSize / m_sampleSize;
  1204. m_numDecodedSamples = m_sampleCount;
  1205. }
  1206. else if ( m_format == WAVE_FORMAT_ADPCM )
  1207. {
  1208. // The ADPCM mixers treat the wave source as a flat file of bytes.
  1209. // Since each "sample" is a byte (this is a flat file), the number of samples is the file size
  1210. m_sampleCount = m_dataSize;
  1211. m_sampleSize = 1;
  1212. // file says 4, output is 16
  1213. m_bits = 16;
  1214. m_numDecodedSamples = ADPCMSampleCount( (ADPCMWAVEFORMAT *)m_pHeader, m_dataSize );
  1215. }
  1216. // some samples need to be converted
  1217. if ( pData )
  1218. {
  1219. ConvertSamples( pData, m_sampleCount );
  1220. }
  1221. }
  1222. //-----------------------------------------------------------------------------
  1223. // Purpose:
  1224. // Output : Returns true on success, false on failure.
  1225. //-----------------------------------------------------------------------------
  1226. int CAudioSourceMemWave::GetCacheStatus( void )
  1227. {
  1228. VPROF("CAudioSourceMemWave::GetCacheStatus");
  1229. if ( IsPC() || !IsGameConsole() )
  1230. {
  1231. // NOTE: This will start the load if it isn't started
  1232. bool bCacheValid, bIsMissing;
  1233. bool bCompleted = wavedatacache->IsDataLoadCompleted( m_hCache, &bCacheValid, &bIsMissing );
  1234. if ( !bCacheValid )
  1235. {
  1236. char nameBuf[MAX_PATH];
  1237. wavedatacache->RestartDataLoad( &m_hCache, m_pSfx->GetFileName(nameBuf, sizeof(nameBuf)), m_dataSize, m_dataStart );
  1238. }
  1239. if ( bCompleted )
  1240. return AUDIO_IS_LOADED;
  1241. if ( bIsMissing )
  1242. return AUDIO_ERROR_LOADING;
  1243. if ( wavedatacache->IsDataLoadInProgress( m_hCache ) )
  1244. return AUDIO_LOADING;
  1245. }
  1246. else
  1247. {
  1248. return wavedatacache->IsStreamedDataReady( m_hStream ) ? AUDIO_IS_LOADED : AUDIO_NOT_LOADED;
  1249. }
  1250. return AUDIO_NOT_LOADED;
  1251. }
  1252. //-----------------------------------------------------------------------------
  1253. // Purpose:
  1254. //-----------------------------------------------------------------------------
  1255. void CAudioSourceMemWave::CacheLoad( void )
  1256. {
  1257. char nameBuf[MAX_PATH];
  1258. if ( IsPC() )
  1259. {
  1260. // Commence lazy load?
  1261. if ( m_hCache != 0 )
  1262. {
  1263. bool bCacheValid;
  1264. wavedatacache->IsDataLoadCompleted( m_hCache, &bCacheValid );
  1265. if ( !bCacheValid )
  1266. {
  1267. wavedatacache->RestartDataLoad( &m_hCache, m_pSfx->GetFileName(nameBuf, sizeof(nameBuf)), m_dataSize, m_dataStart );
  1268. }
  1269. return;
  1270. }
  1271. m_hCache = wavedatacache->AsyncLoadCache( m_pSfx->GetFileName(nameBuf,sizeof(nameBuf)), m_dataSize, m_dataStart );
  1272. }
  1273. if ( IsGameConsole() )
  1274. {
  1275. if ( m_hStream == INVALID_STREAM_HANDLE )
  1276. {
  1277. // memory wave is resident
  1278. const char *pFilename = m_pSfx->GetFileName(nameBuf, sizeof(nameBuf));
  1279. streamFlags_t streamFlags = STREAMED_FROMDVD;
  1280. char szFilename[MAX_PATH];
  1281. switch ( m_format )
  1282. {
  1283. case WAVE_FORMAT_XMA:
  1284. case WAVE_FORMAT_PCM:
  1285. case WAVE_FORMAT_MP3:
  1286. case WAVE_FORMAT_TEMP:
  1287. strcpy( szFilename, pFilename );
  1288. V_SetExtension( szFilename, PLATFORM_EXT ".wav", sizeof( szFilename ) );
  1289. pFilename = szFilename;
  1290. // memory resident xma waves use the queued loader
  1291. // restricting to XMA due to not correctly running a post ConvertSamples, which is not an issue for XMA
  1292. if ( g_pQueuedLoader->IsMapLoading() )
  1293. {
  1294. // hint the wave data cache
  1295. // these are map based static sounds pooled accordingly
  1296. streamFlags |= STREAMED_QUEUEDLOAD;
  1297. }
  1298. break;
  1299. default:
  1300. // Normal mode for the other files...
  1301. break;
  1302. }
  1303. // open stream to load as a single monolithic buffer
  1304. SoundError error;
  1305. m_hStream = wavedatacache->OpenStreamedLoad( pFilename, m_dataSize, m_dataStart, 0, -1, m_dataSize, 1, streamFlags, error );
  1306. if ( m_hStream != INVALID_STREAM_HANDLE && !( streamFlags & STREAMED_QUEUEDLOAD ) )
  1307. {
  1308. // causes a synchronous block to finish the load
  1309. // convert data once right now
  1310. char *pWaveData = (char *)wavedatacache->GetStreamedDataPointer( m_hStream, true );
  1311. if ( pWaveData )
  1312. {
  1313. ConvertSamples( pWaveData, m_dataSize/m_sampleSize );
  1314. }
  1315. }
  1316. }
  1317. }
  1318. }
  1319. //-----------------------------------------------------------------------------
  1320. // Purpose:
  1321. //-----------------------------------------------------------------------------
  1322. void CAudioSourceMemWave::CacheUnload( void )
  1323. {
  1324. if ( IsPC() || !IsGameConsole() )
  1325. {
  1326. if ( m_hCache != 0 )
  1327. {
  1328. wavedatacache->Unload( m_hCache );
  1329. }
  1330. }
  1331. else
  1332. {
  1333. if ( m_hStream != INVALID_STREAM_HANDLE )
  1334. {
  1335. wavedatacache->CloseStreamedLoad( m_hStream );
  1336. m_hStream = INVALID_STREAM_HANDLE;
  1337. }
  1338. }
  1339. }
  1340. //-----------------------------------------------------------------------------
  1341. // Purpose:
  1342. // Output : char
  1343. //-----------------------------------------------------------------------------
  1344. char *CAudioSourceMemWave::GetDataPointer( void )
  1345. {
  1346. char *pWaveData = NULL;
  1347. char nameBuf[MAX_PATH];
  1348. if ( IsPC() || !IsGameConsole() )
  1349. {
  1350. bool bSamplesConverted = false;
  1351. if ( m_hCache == 0 )
  1352. {
  1353. // not in cache, start loading
  1354. CacheLoad();
  1355. }
  1356. // mount the requested data, blocks if necessary
  1357. // TERROR: limit data read while rebuilding cache
  1358. int dataSize = m_dataSize;
  1359. if ( m_bIsRebuildingCache )
  1360. {
  1361. const char *filename = m_pSfx->GetFileName(nameBuf, sizeof(nameBuf));
  1362. if ( V_stristr( filename, "music" ) != NULL )
  1363. {
  1364. dataSize = MIN( dataSize, 32768 );
  1365. }
  1366. }
  1367. wavedatacache->GetDataPointer(
  1368. m_hCache,
  1369. m_pSfx->GetFileName(nameBuf, sizeof(nameBuf)),
  1370. dataSize,
  1371. m_dataStart,
  1372. (void **)&pWaveData,
  1373. 0,
  1374. &bSamplesConverted );
  1375. // If we have reloaded data from disk (async) and we haven't converted the samples yet, do it now
  1376. // FIXME: Is this correct for stereo wavs?
  1377. if ( pWaveData && !bSamplesConverted )
  1378. {
  1379. ConvertSamples( pWaveData, m_dataSize/m_sampleSize );
  1380. wavedatacache->SetPostProcessed( m_hCache, true );
  1381. }
  1382. }
  1383. else
  1384. {
  1385. if ( m_hStream != INVALID_STREAM_HANDLE )
  1386. {
  1387. // expected to be valid, unless failure during setup
  1388. pWaveData = (char *)wavedatacache->GetStreamedDataPointer( m_hStream, true );
  1389. }
  1390. }
  1391. return pWaveData;
  1392. }
  1393. //-----------------------------------------------------------------------------
  1394. // Purpose: Wave source for streaming wave files
  1395. // UNDONE: Handle looping
  1396. //-----------------------------------------------------------------------------
  1397. class CAudioSourceStreamWave : public CAudioSourceWave, public IWaveStreamSource
  1398. {
  1399. public:
  1400. CAudioSourceStreamWave( CSfxTable *pSfx );
  1401. CAudioSourceStreamWave( CSfxTable *pSfx, CAudioSourceCachedInfo *info );
  1402. virtual ~CAudioSourceStreamWave();
  1403. CAudioMixer *CreateMixer( int initialStreamPosition, int skipInitialSamples, bool bUpdateDelayForChoreo, SoundError &soundError, hrtf_info_t *pHRTFVec );
  1404. int GetOutputData( void **pData, int64 samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] );
  1405. void ParseChunk( IterateRIFF &walk, int chunkName );
  1406. bool IsStreaming( void ) { return true; }
  1407. virtual int GetCacheStatus( void );
  1408. // IWaveStreamSource
  1409. virtual int64 UpdateLoopingSamplePosition( int64 samplePosition )
  1410. {
  1411. return ConvertLoopedPosition( samplePosition );
  1412. }
  1413. virtual void UpdateSamples( char *pData, int sampleCount )
  1414. {
  1415. ConvertSamples( pData, sampleCount );
  1416. }
  1417. virtual int GetLoopingInfo( int *pLoopBlock, int *pNumLeadingSamples, int *pNumTrailingSamples )
  1418. {
  1419. return CAudioSourceWave::GetLoopingInfo( pLoopBlock, pNumLeadingSamples, pNumTrailingSamples );
  1420. }
  1421. virtual void Prefetch();
  1422. virtual int SampleToStreamPosition( int samplePosition );
  1423. virtual int StreamToSamplePosition( int streamPosition );
  1424. private:
  1425. CAudioSourceStreamWave( const CAudioSourceStreamWave & ); // not implemented, not accessible
  1426. #if !defined( _GAMECONSOLE )
  1427. // We need this for -tools mode to get access to the raw samples
  1428. FileHandle_t m_hWaveFileAccess;
  1429. #endif
  1430. };
  1431. //-----------------------------------------------------------------------------
  1432. // Purpose: Save a copy of the file name for instances to open later
  1433. // Input : *pFileName - filename
  1434. //-----------------------------------------------------------------------------
  1435. CAudioSourceStreamWave::CAudioSourceStreamWave( CSfxTable *pSfx ) : CAudioSourceWave( pSfx )
  1436. {
  1437. m_pSfx = pSfx;
  1438. m_dataStart = -1;
  1439. m_dataSize = 0;
  1440. m_sampleCount = 0;
  1441. if ( IsGameConsole() )
  1442. {
  1443. bool bValid = GetXboxAudioStartupData();
  1444. if ( !bValid )
  1445. {
  1446. // failed, substitute placeholder
  1447. pSfx->m_bUseErrorFilename = true;
  1448. bValid = GetXboxAudioStartupData();
  1449. if ( bValid )
  1450. {
  1451. char nameBuf1[MAX_PATH];
  1452. char nameBuf2[MAX_PATH];
  1453. DevWarning( "Failed to load sound \"%s\", substituting \"%s\"\n", pSfx->getname(nameBuf1,sizeof(nameBuf1)), pSfx->GetFileName(nameBuf2,sizeof(nameBuf2)) );
  1454. }
  1455. }
  1456. }
  1457. }
  1458. CAudioSourceStreamWave::CAudioSourceStreamWave( CSfxTable *pSfx, CAudioSourceCachedInfo *info ) :
  1459. CAudioSourceWave( pSfx, info )
  1460. #if !defined( _GAMECONSOLE )
  1461. ,m_hWaveFileAccess( FILESYSTEM_INVALID_HANDLE )
  1462. #endif
  1463. {
  1464. m_pSfx = pSfx;
  1465. m_dataStart = info->DataStart();
  1466. m_dataSize = info->DataSize();
  1467. m_sampleCount = info->SampleCount();
  1468. }
  1469. //-----------------------------------------------------------------------------
  1470. // Purpose: free the filename buffer
  1471. //-----------------------------------------------------------------------------
  1472. CAudioSourceStreamWave::~CAudioSourceStreamWave( void )
  1473. {
  1474. #if !defined( _GAMECONSOLE )
  1475. if ( m_hWaveFileAccess != FILESYSTEM_INVALID_HANDLE )
  1476. {
  1477. g_pFullFileSystem->Close( m_hWaveFileAccess );
  1478. m_hWaveFileAccess = FILESYSTEM_INVALID_HANDLE;
  1479. }
  1480. #endif
  1481. }
  1482. //-----------------------------------------------------------------------------
  1483. // Purpose: Create an instance (mixer & wavedata) of this sound
  1484. // Output : CAudioMixer * - pointer to the mixer
  1485. //-----------------------------------------------------------------------------
  1486. CAudioMixer *CAudioSourceStreamWave::CreateMixer( int initialStreamPosition, int skipInitialSamples, bool bUpdateDelayForChoreo, SoundError &soundError, hrtf_info_t* pHRTFVec )
  1487. {
  1488. char fileName[MAX_PATH];
  1489. const char *pFileName = m_pSfx->GetFileName(fileName, sizeof(fileName));
  1490. if ( IsGameConsole() )
  1491. {
  1492. switch ( m_format )
  1493. {
  1494. case WAVE_FORMAT_XMA:
  1495. #if IsX360()
  1496. // for safety, validate the initial stream position
  1497. // not trusting save/load
  1498. if ( ( initialStreamPosition % XBOX_DVD_SECTORSIZE ) ||
  1499. ( initialStreamPosition % XMA_BLOCK_SIZE ) ||
  1500. ( initialStreamPosition >= m_dataSize ) )
  1501. {
  1502. initialStreamPosition = 0;
  1503. }
  1504. #endif
  1505. // Pass through...
  1506. case WAVE_FORMAT_PCM:
  1507. case WAVE_FORMAT_MP3:
  1508. case WAVE_FORMAT_TEMP:
  1509. V_SetExtension( fileName, PLATFORM_EXT ".wav", sizeof( fileName ) );
  1510. break;
  1511. default:
  1512. // Do nothing otherwise
  1513. break;
  1514. }
  1515. }
  1516. if (pHRTFVec && m_bits != 16)
  1517. {
  1518. char filename[256];
  1519. this->m_pSfx->GetFileName(filename, sizeof(filename));
  1520. DevMsg("Sound %s configured to use HRTF but is not a 16-bit sound\n", filename);
  1521. pHRTFVec = nullptr;
  1522. }
  1523. // BUGBUG: Source constructs the IWaveData, mixer frees it, fix this?
  1524. IWaveData *pWaveData = CreateWaveDataHRTF(CreateWaveDataStream( *this, static_cast<IWaveStreamSource *>(this), pFileName, m_dataStart, m_dataSize, m_pSfx, initialStreamPosition, skipInitialSamples, soundError ), pHRTFVec);
  1525. if ( pWaveData )
  1526. {
  1527. CAudioMixer *pMixer = CreateWaveMixer( pWaveData, m_format, pHRTFVec ? 2 : m_channels, m_bits, initialStreamPosition, skipInitialSamples, bUpdateDelayForChoreo );
  1528. if ( pMixer )
  1529. {
  1530. ReferenceAdd( pMixer );
  1531. return pMixer;
  1532. }
  1533. // no mixer, delete the stream buffer/instance
  1534. delete pWaveData;
  1535. }
  1536. return NULL;
  1537. }
  1538. void CAudioSourceStreamWave::Prefetch()
  1539. {
  1540. char nameBuf[MAX_PATH];
  1541. PrefetchDataStream( m_pSfx->GetFileName(nameBuf, sizeof(nameBuf)), m_dataStart, m_dataSize );
  1542. }
  1543. //-----------------------------------------------------------------------------
  1544. //-----------------------------------------------------------------------------
  1545. int CAudioSourceStreamWave::SampleToStreamPosition( int samplePosition )
  1546. {
  1547. switch ( m_format )
  1548. {
  1549. case WAVE_FORMAT_XMA:
  1550. if ( m_nHeaderSize != 0 )
  1551. {
  1552. // Run through the seek table to find the block closest to the desired sample.
  1553. // Each seek table entry is the index (counting from the beginning of the file)
  1554. // of the first sample in the corresponding block, but there's no entry for the
  1555. // first block (since the index would always be zero).
  1556. int *pSeekTable = (int*)m_pHeader;
  1557. int packet = 0;
  1558. for ( int i = 0; i < m_nHeaderSize/(int)sizeof( int ); ++i )
  1559. {
  1560. if ( samplePosition < pSeekTable[i] )
  1561. {
  1562. packet = i;
  1563. break;
  1564. }
  1565. }
  1566. int streamPosition = ( packet == 0 ) ? 0 : ( packet - 1 ) * 2048;
  1567. return streamPosition;
  1568. }
  1569. break;
  1570. case WAVE_FORMAT_PCM:
  1571. return samplePosition * m_sampleSize;
  1572. }
  1573. // Function is not supported
  1574. if ( !IsCert() )
  1575. {
  1576. char fileName[MAX_PATH];
  1577. const char *pFileName = GetFileName( fileName, sizeof( fileName ) );
  1578. Warning( "SampleToStreamPosition( %d ) is not supported for sound '%s'.\n", samplePosition, pFileName );
  1579. }
  1580. // not in the expected format or lacking the seek table
  1581. return -1;
  1582. }
  1583. //-----------------------------------------------------------------------------
  1584. //-----------------------------------------------------------------------------
  1585. int CAudioSourceStreamWave::StreamToSamplePosition( int streamPosition )
  1586. {
  1587. switch ( m_format )
  1588. {
  1589. case WAVE_FORMAT_XMA:
  1590. if ( m_nHeaderSize != 0 )
  1591. {
  1592. int packet = streamPosition/2048;
  1593. if ( packet <= 0 )
  1594. {
  1595. return 0;
  1596. }
  1597. if ( packet > m_nHeaderSize/(int)sizeof( int ) )
  1598. {
  1599. return m_numDecodedSamples;
  1600. }
  1601. return ((int*)m_pHeader)[packet - 1];
  1602. }
  1603. break;
  1604. case WAVE_FORMAT_PCM:
  1605. return streamPosition / m_sampleSize;
  1606. }
  1607. // Function is not supported
  1608. if ( !IsCert() )
  1609. {
  1610. char fileName[MAX_PATH];
  1611. const char *pFileName = GetFileName( fileName, sizeof( fileName ) );
  1612. Warning( "StreamToSamplePosition( %d ) is not supported for sound '%s'.\n", streamPosition, pFileName );
  1613. }
  1614. // not in the expected format or lacking the seek table
  1615. return -1;
  1616. }
  1617. //-----------------------------------------------------------------------------
  1618. // Purpose: Parse a stream wave file chunk
  1619. // unlike the in-memory file, don't load the data, just get a reference to it.
  1620. // Input : &walk - RIFF file
  1621. //-----------------------------------------------------------------------------
  1622. void CAudioSourceStreamWave::ParseChunk( IterateRIFF &walk, int chunkName )
  1623. {
  1624. // NOTE: It would be nice to break out of parsing once we have the data start and
  1625. // save seeking over the whole file. But to do so, the other needed chunks must occur
  1626. // before the DATA chunk. But, that is not standard and breaks most other wav parsers.
  1627. switch( chunkName )
  1628. {
  1629. case WAVE_DATA:
  1630. // data starts at chunk + 8 (chunk name, chunk size = 2*4=8 bytes)
  1631. // don't load the data, just know where it is so each instance
  1632. // can load it later
  1633. m_dataStart = walk.ChunkFilePosition() + 8;
  1634. m_dataSize = walk.ChunkSize();
  1635. m_sampleCount = m_dataSize / m_sampleSize;
  1636. return;
  1637. }
  1638. CAudioSourceWave::ParseChunk( walk, chunkName );
  1639. }
  1640. //-----------------------------------------------------------------------------
  1641. // Purpose: This is not implemented here. This source has no data. It is the
  1642. // WaveData's responsibility to load/serve the data
  1643. //-----------------------------------------------------------------------------
  1644. int CAudioSourceStreamWave::GetOutputData( void **pData, int64 samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] )
  1645. {
  1646. #if !defined( _GAMECONSOLE )
  1647. // Only -tools mode uses this to build a "preview" of the wave form for PCM data only
  1648. if ( GetType() == WAVE_FORMAT_PCM )
  1649. {
  1650. if ( m_hWaveFileAccess == FILESYSTEM_INVALID_HANDLE )
  1651. {
  1652. char buf[ MAX_PATH ];
  1653. const char *pFilename = GetFileName( buf, sizeof( buf ) );
  1654. m_hWaveFileAccess = g_pFullFileSystem->Open( CFmtStr( "sound\\%s", pFilename ), "rb", "GAME" );
  1655. }
  1656. if ( m_hWaveFileAccess != FILESYSTEM_INVALID_HANDLE )
  1657. {
  1658. g_pFullFileSystem->Seek( m_hWaveFileAccess, m_dataStart + samplePosition * SampleSize(), FILESYSTEM_SEEK_HEAD );
  1659. if ( copyBuf != NULL )
  1660. {
  1661. g_pFullFileSystem->Read( copyBuf, sampleCount * SampleSize(), m_hWaveFileAccess );
  1662. }
  1663. *pData = copyBuf;
  1664. return sampleCount;
  1665. }
  1666. }
  1667. #endif
  1668. return 0;
  1669. }
  1670. int CAudioSourceStreamWave::GetCacheStatus( void )
  1671. {
  1672. if ( !m_dataSize || !m_dataStart )
  1673. {
  1674. // didn't get precached properly
  1675. return AUDIO_NOT_LOADED;
  1676. }
  1677. return AUDIO_IS_LOADED;
  1678. }
  1679. //-----------------------------------------------------------------------------
  1680. // Purpose: Create a wave audio source (streaming or in memory)
  1681. // Input : *pName - file name (NOTE: CAUDIOSOURCE KEEPS A POINTER TO pSfx)
  1682. // streaming - if true, don't load, stream each instance
  1683. // Output : CAudioSource * - a new source
  1684. //-----------------------------------------------------------------------------
  1685. CAudioSource *CreateWave( CSfxTable *pSfx, bool bStreaming )
  1686. {
  1687. Assert( pSfx );
  1688. #if defined( _DEBUG )
  1689. // For some reason you can't usually do pSfx->getname() in the dev studio debugger, so for convenience we'll grab the name
  1690. // here in debug builds at least...
  1691. char nameBuf[MAX_PATH];
  1692. char const *pName = pSfx->getname( nameBuf, sizeof(nameBuf) );
  1693. NOTE_UNUSED( pName );
  1694. #endif
  1695. CAudioSourceWave *pWave = NULL;
  1696. if ( IsPC() || !IsGameConsole() )
  1697. {
  1698. // Caching should always work, so if we failed to cache, it's a problem reading the file data, etc.
  1699. bool bIsMapSound = pSfx->IsPrecachedSound();
  1700. CAudioSourceCachedInfo *pInfo = audiosourcecache->GetInfo( CAudioSource::AUDIO_SOURCE_WAV, bIsMapSound, pSfx );
  1701. if ( pInfo && pInfo->Type() != CAudioSource::AUDIO_SOURCE_UNK )
  1702. {
  1703. // create the source from this file
  1704. if ( bStreaming )
  1705. {
  1706. pWave = new CAudioSourceStreamWave( pSfx, pInfo );
  1707. }
  1708. else
  1709. {
  1710. pWave = new CAudioSourceMemWave( pSfx, pInfo );
  1711. }
  1712. }
  1713. }
  1714. else
  1715. {
  1716. // 360 does not use audio cache system
  1717. // create the desired type
  1718. if ( bStreaming )
  1719. {
  1720. pWave = new CAudioSourceStreamWave( pSfx );
  1721. }
  1722. else
  1723. {
  1724. pWave = new CAudioSourceMemWave( pSfx );
  1725. }
  1726. }
  1727. if ( pWave && !pWave->Format() )
  1728. {
  1729. // lack of format indicates failure
  1730. delete pWave;
  1731. pWave = NULL;
  1732. }
  1733. return pWave;
  1734. }
  1735. //-----------------------------------------------------------------------------
  1736. // Purpose: Wrapper for CreateWave()
  1737. //-----------------------------------------------------------------------------
  1738. CAudioSource *Audio_CreateStreamedWave( CSfxTable *pSfx )
  1739. {
  1740. #if defined( MP3_SUPPORT )
  1741. char nameBuf[MAX_PATH];
  1742. if ( Audio_IsMP3( pSfx->GetFileName( nameBuf, sizeof(nameBuf) ) ) )
  1743. {
  1744. return Audio_CreateStreamedMP3( pSfx );
  1745. }
  1746. #endif
  1747. return CreateWave( pSfx, true );
  1748. }
  1749. //-----------------------------------------------------------------------------
  1750. // Purpose: Wrapper for CreateWave()
  1751. //-----------------------------------------------------------------------------
  1752. CAudioSource *Audio_CreateMemoryWave( CSfxTable *pSfx )
  1753. {
  1754. #if defined( MP3_SUPPORT )
  1755. char nameBuf[MAX_PATH];
  1756. if ( Audio_IsMP3( pSfx->GetFileName( nameBuf, sizeof(nameBuf) ) ) )
  1757. {
  1758. return Audio_CreateMemoryMP3( pSfx );
  1759. }
  1760. // In case the WAV file is actually an MP3 file.
  1761. bool bIsMapSound = pSfx->IsPrecachedSound();
  1762. // We pass AUDIO_SOURCE_WAV to not break the sound cache hack :(
  1763. CAudioSourceCachedInfo *pInfo = audiosourcecache->GetInfo( CAudioSource::AUDIO_SOURCE_WAV, bIsMapSound, pSfx );
  1764. if ( pInfo->Type() == CAudioSource::AUDIO_SOURCE_MP3 )
  1765. {
  1766. return Audio_CreateMemoryMP3( pSfx );
  1767. }
  1768. #endif
  1769. return CreateWave( pSfx, false );
  1770. }
  1771. float GetMP3Duration_Helper( char const *filename );
  1772. static float Audio_GetMP3Duration( char const *pName )
  1773. {
  1774. // Deduce from file
  1775. return GetMP3Duration_Helper( pName );
  1776. }
  1777. void MaybeReportMissingWav( char const *wav )
  1778. {
  1779. static CUtlSymbolTable wavErrors;
  1780. CUtlSymbol sym;
  1781. sym = wavErrors.Find( wav );
  1782. if ( UTL_INVAL_SYMBOL == sym )
  1783. {
  1784. // See if file exists
  1785. if ( g_pFullFileSystem->FileExists( wav ) )
  1786. {
  1787. DevWarning( "Bad Audio file '%s'\n", wav );
  1788. }
  1789. else
  1790. {
  1791. DevWarning( "Missing wav file '%s'\n", wav );
  1792. }
  1793. wavErrors.AddString( wav );
  1794. }
  1795. }
  1796. static float Audio_GetADPCMWaveDuration( char const *pName )
  1797. {
  1798. char formatBuffer[1024];
  1799. WAVEFORMATEX *pfmt = (WAVEFORMATEX *)formatBuffer;
  1800. InFileRIFF riff( pName, *g_pSndIO );
  1801. if ( riff.RIFFName() != RIFF_WAVE )
  1802. {
  1803. MaybeReportMissingWav( pName );
  1804. return 0.0f;
  1805. }
  1806. // set up the iterator for the whole file (root RIFF is a chunk)
  1807. IterateRIFF walk( riff, riff.RIFFSize() );
  1808. int format = 0;
  1809. int formatSize = 0;
  1810. int sampleCount = 0;
  1811. // This chunk must be first as it contains the wave's format
  1812. // break out when we've parsed it
  1813. while ( walk.ChunkAvailable() && ( format == 0 || sampleCount == 0 ) )
  1814. {
  1815. switch( walk.ChunkName() )
  1816. {
  1817. case WAVE_FMT:
  1818. if ( walk.ChunkSize() <= sizeof( formatBuffer ) )
  1819. {
  1820. walk.ChunkRead( formatBuffer );
  1821. formatSize = walk.ChunkSize();
  1822. format = LittleWord( pfmt->wFormatTag );
  1823. }
  1824. break;
  1825. case WAVE_DATA:
  1826. if ( format != 0 )
  1827. {
  1828. int dataSize = walk.ChunkSize();
  1829. if ( format == WAVE_FORMAT_ADPCM )
  1830. {
  1831. // Dummy size for now
  1832. sampleCount = dataSize;
  1833. }
  1834. else
  1835. {
  1836. sampleCount = dataSize / ( LittleWord( pfmt->wBitsPerSample ) >> 3 );
  1837. }
  1838. }
  1839. break;
  1840. default:
  1841. ChunkError( walk.ChunkName() );
  1842. break;
  1843. }
  1844. walk.ChunkNext();
  1845. }
  1846. float sampleRate = LittleDWord( pfmt->nSamplesPerSec );
  1847. // Determine actual duration
  1848. sampleCount = ADPCMSampleCount( (ADPCMWAVEFORMAT *)formatBuffer, sampleCount );
  1849. return (float)sampleCount / sampleRate;
  1850. }
  1851. static float Audio_GetWaveDuration( char const *pName )
  1852. {
  1853. if ( IsGameConsole() )
  1854. {
  1855. // should have precached
  1856. return 0;
  1857. }
  1858. CAudioSourceCachedInfo *pInfo = audiosourcecache->GetInfoByName( CFmtStr( "sound/%s", PSkipSoundChars( pName ) ) );
  1859. if ( !pInfo )
  1860. {
  1861. return 0.0f;
  1862. }
  1863. int nFormat = pInfo->Format();
  1864. int nSampleCount = pInfo->SampleCount();
  1865. int nSampleRate = pInfo->SampleRate();
  1866. // Not really a WAVE file or no format chunk, bail
  1867. if ( !nFormat || !nSampleCount )
  1868. return 0.0f;
  1869. if ( nFormat == WAVE_FORMAT_ADPCM )
  1870. {
  1871. return Audio_GetADPCMWaveDuration( pName );
  1872. }
  1873. else
  1874. {
  1875. return (float)nSampleCount / nSampleRate;
  1876. }
  1877. }
  1878. //-----------------------------------------------------------------------------
  1879. // Purpose: Fast method for determining duration of .wav/.mp3, exposed to server as well
  1880. // Input : *pName -
  1881. // Output : float
  1882. //-----------------------------------------------------------------------------
  1883. float AudioSource_GetSoundDuration( char const *pName )
  1884. {
  1885. #if defined( MP3_SUPPORT )
  1886. if ( Audio_IsMP3( pName ) )
  1887. {
  1888. return Audio_GetMP3Duration( pName );
  1889. }
  1890. #endif
  1891. if ( IsGameConsole() )
  1892. {
  1893. CSfxTable *pSound = S_PrecacheSound( pName );
  1894. if ( pSound )
  1895. {
  1896. return AudioSource_GetSoundDuration( pSound );
  1897. }
  1898. }
  1899. return Audio_GetWaveDuration( pName );
  1900. }
  1901. float AudioSource_GetSoundDuration( CSfxTable *pSfx )
  1902. {
  1903. if ( pSfx && pSfx->pSource )
  1904. {
  1905. return (float)pSfx->pSource->SampleCount() / (float)pSfx->pSource->SampleRate();
  1906. }
  1907. return 0;
  1908. }
  1909. CAudioSourceCachedInfo::CAudioSourceCachedInfo() :
  1910. infolong( 0 ),
  1911. flagsbyte( 0 ),
  1912. m_dataStart( 0 ),
  1913. m_dataSize( 0 ),
  1914. m_loopStart( 0 ),
  1915. m_sampleCount( 0 ),
  1916. m_usCachedDataSize( 0 ),
  1917. m_pCachedData( 0 ),
  1918. m_usHeaderSize( 0 ),
  1919. m_pHeader( 0 ),
  1920. m_pSentence( 0 )
  1921. {
  1922. }
  1923. CAudioSourceCachedInfo& CAudioSourceCachedInfo::operator =( const CAudioSourceCachedInfo& src )
  1924. {
  1925. if ( this == &src )
  1926. return *this;
  1927. infolong = src.infolong;
  1928. flagsbyte = src.flagsbyte;
  1929. SetDataStart( src.DataStart() );
  1930. SetDataSize( src.DataSize() );
  1931. SetLoopStart( src.LoopStart() );
  1932. SetSampleCount( src.SampleCount() );
  1933. CSentence *scopy = NULL;
  1934. if ( src.Sentence() )
  1935. {
  1936. scopy = new CSentence();
  1937. *scopy = *src.Sentence();
  1938. }
  1939. SetSentence( scopy );
  1940. byte *data = NULL;
  1941. Assert( src.CachedDataSize() == 0 || src.CachedData() );
  1942. m_usCachedDataSize = 0;
  1943. if ( src.CachedData() && src.CachedDataSize() > 0 )
  1944. {
  1945. SetCachedDataSize( src.CachedDataSize() );
  1946. data = new byte[ src.CachedDataSize() ];
  1947. Assert( data );
  1948. Q_memcpy( data, src.CachedData(), src.CachedDataSize() );
  1949. }
  1950. SetCachedData( data );
  1951. data = NULL;
  1952. Assert( src.HeaderSize() == 0 || src.HeaderData() );
  1953. m_usHeaderSize = 0;
  1954. if ( src.HeaderData() && src.HeaderSize() > 0 )
  1955. {
  1956. SetHeaderSize( src.HeaderSize() );
  1957. data = new byte[ src.HeaderSize() ];
  1958. Assert( data );
  1959. Q_memcpy( data, src.HeaderData(), src.HeaderSize() );
  1960. }
  1961. SetHeaderData( data );
  1962. return *this;
  1963. }
  1964. CAudioSourceCachedInfo::CAudioSourceCachedInfo( const CAudioSourceCachedInfo& src )
  1965. {
  1966. if ( this == &src )
  1967. {
  1968. Assert( 0 );
  1969. return;
  1970. }
  1971. infolong = src.infolong;
  1972. flagsbyte = src.flagsbyte;
  1973. SetDataStart( src.DataStart() );
  1974. SetDataSize( src.DataSize() );
  1975. SetLoopStart( src.LoopStart() );
  1976. SetSampleCount( src.SampleCount() );
  1977. CSentence *scopy = NULL;
  1978. if ( src.Sentence() )
  1979. {
  1980. scopy = new CSentence();
  1981. *scopy = *src.Sentence();
  1982. }
  1983. SetSentence( scopy );
  1984. byte *data = NULL;
  1985. Assert( src.CachedDataSize() == 0 || src.CachedData() );
  1986. m_usCachedDataSize = 0;
  1987. if ( src.CachedData() && src.CachedDataSize() > 0 )
  1988. {
  1989. SetCachedDataSize( src.CachedDataSize() );
  1990. data = new byte[ src.CachedDataSize() ];
  1991. Assert( data );
  1992. Q_memcpy( data, src.CachedData(), src.CachedDataSize() );
  1993. }
  1994. SetCachedData( data );
  1995. data = NULL;
  1996. Assert( src.HeaderSize() == 0 || src.HeaderData() );
  1997. m_usHeaderSize = 0;
  1998. if ( src.HeaderData() && src.HeaderSize() > 0 )
  1999. {
  2000. SetHeaderSize( src.HeaderSize() );
  2001. data = new byte[ src.HeaderSize() ];
  2002. Assert( data );
  2003. Q_memcpy( data, src.HeaderData(), src.HeaderSize() );
  2004. }
  2005. SetHeaderData( data );
  2006. }
  2007. CAudioSourceCachedInfo::~CAudioSourceCachedInfo()
  2008. {
  2009. Clear();
  2010. }
  2011. void CAudioSourceCachedInfo::Clear()
  2012. {
  2013. infolong = 0;
  2014. flagsbyte = 0;
  2015. m_dataStart = 0;
  2016. m_dataSize = 0;
  2017. m_loopStart = 0;
  2018. m_sampleCount = 0;
  2019. delete m_pSentence;
  2020. m_pSentence = NULL;
  2021. delete[] m_pCachedData;
  2022. m_pCachedData = NULL;
  2023. m_usCachedDataSize = 0;
  2024. delete[] m_pHeader;
  2025. m_pHeader = NULL;
  2026. m_usHeaderSize = 0;
  2027. }
  2028. void CAudioSourceCachedInfo::RemoveData()
  2029. {
  2030. delete[] m_pCachedData;
  2031. m_pCachedData = NULL;
  2032. m_usCachedDataSize = 0;
  2033. flags.m_bCachedData = false;
  2034. }
  2035. void CAudioSourceCachedInfo::Save( CUtlBuffer& buf )
  2036. {
  2037. buf.PutInt( infolong );
  2038. buf.PutChar( flagsbyte );
  2039. buf.PutInt( m_dataStart );
  2040. buf.PutInt( m_dataSize );
  2041. buf.PutInt( m_loopStart );
  2042. buf.PutInt( m_sampleCount );
  2043. if ( flags.m_bSentence )
  2044. {
  2045. m_pSentence->CacheSaveToBuffer( buf, CACHED_SENTENCE_VERSION );
  2046. }
  2047. Assert( m_usCachedDataSize < 65535 );
  2048. if ( flags.m_bCachedData && m_pCachedData )
  2049. {
  2050. buf.PutInt( m_usCachedDataSize );
  2051. buf.Put( m_pCachedData, m_usCachedDataSize );
  2052. }
  2053. Assert( m_usHeaderSize <= 32767 );
  2054. if ( flags.m_bHeader )
  2055. {
  2056. buf.PutShort( m_usHeaderSize );
  2057. buf.Put( m_pHeader, m_usHeaderSize );
  2058. }
  2059. }
  2060. void CAudioSourceCachedInfo::Restore( CUtlBuffer& buf )
  2061. {
  2062. // Wipe any old data!!!
  2063. Clear();
  2064. infolong = buf.GetInt();
  2065. flagsbyte = buf.GetChar();
  2066. m_dataStart = buf.GetInt();
  2067. m_dataSize = buf.GetInt();
  2068. m_loopStart = buf.GetInt();
  2069. m_sampleCount = buf.GetInt();
  2070. if ( flags.m_bSentence )
  2071. {
  2072. m_pSentence = new CSentence();
  2073. Assert( m_pSentence );
  2074. m_pSentence->CacheRestoreFromBuffer( buf );
  2075. }
  2076. if ( flags.m_bCachedData )
  2077. {
  2078. m_usCachedDataSize = buf.GetInt();
  2079. Assert( m_usCachedDataSize > 0 && m_usCachedDataSize < 65535 );
  2080. if ( m_usCachedDataSize > 0 )
  2081. {
  2082. byte *data = new byte[ m_usCachedDataSize ];
  2083. buf.Get( data, m_usCachedDataSize );
  2084. SetCachedData( data );
  2085. }
  2086. }
  2087. if ( flags.m_bHeader )
  2088. {
  2089. m_usHeaderSize = buf.GetShort();
  2090. Assert( m_usHeaderSize > 0 && m_usHeaderSize <= 32767 );
  2091. if ( m_usHeaderSize > 0 )
  2092. {
  2093. byte *data = new byte[ m_usHeaderSize ];
  2094. buf.Get( data, m_usHeaderSize );
  2095. SetHeaderData( data );
  2096. }
  2097. }
  2098. }
  2099. int CAudioSourceCachedInfo::s_CurrentType = CAudioSource::AUDIO_SOURCE_MAXTYPE;
  2100. CSfxTable *CAudioSourceCachedInfo::s_pSfx = NULL;
  2101. bool CAudioSourceCachedInfo::s_bIsPrecacheSound = false;
  2102. static bool g_bSoundRebuilt = false;
  2103. void CAudioSourceCachedInfo::Rebuild( char const *filename )
  2104. {
  2105. g_bSoundRebuilt = true;
  2106. // Wipe any old data
  2107. Clear();
  2108. Assert( s_pSfx );
  2109. Assert( s_CurrentType != CAudioSource::AUDIO_SOURCE_MAXTYPE );
  2110. #if 0
  2111. // Never cachify something which is not in the client precache list
  2112. if ( s_bIsPrecacheSound != s_pSfx->IsPrecachedSound() )
  2113. {
  2114. Msg( "Logic bug, precaching entry for '%s' which is not in precache list\n",
  2115. filename );
  2116. }
  2117. #endif
  2118. SetType( s_CurrentType );
  2119. CAudioSource *as = NULL;
  2120. // Note though these instantiate a specific AudioSource subclass, it doesn't matter, we just need one for .wav and one for .mp3
  2121. switch ( s_CurrentType )
  2122. {
  2123. default:
  2124. case CAudioSource::AUDIO_SOURCE_VOICE:
  2125. break;
  2126. case CAudioSource::AUDIO_SOURCE_WAV:
  2127. // TERROR: limit data read while rebuilding cache
  2128. {
  2129. CAudioSourceMemWave *memAs = new CAudioSourceMemWave( s_pSfx );
  2130. memAs->SetRebuildingCache();
  2131. as = memAs;
  2132. }
  2133. break;
  2134. case CAudioSource::AUDIO_SOURCE_MP3:
  2135. #if defined( MP3_SUPPORT )
  2136. as = new CAudioSourceMP3Cache( s_pSfx );
  2137. #endif
  2138. break;
  2139. }
  2140. if ( as )
  2141. {
  2142. as->GetCacheData( this );
  2143. delete as;
  2144. }
  2145. }
  2146. #define AUDIOSOURCE_CACHE_VERSION 3
  2147. class CAudioSourceCache : public IAudioSourceCache
  2148. {
  2149. public:
  2150. typedef CUtlCachedFileData< CAudioSourceCachedInfo > CacheType_t;
  2151. CAudioSourceCache()
  2152. {
  2153. m_pMasterSoundCache = NULL;
  2154. m_pBuildingCache = NULL;
  2155. m_nServerCount = -1;
  2156. }
  2157. bool Init( unsigned int memSize );
  2158. void Shutdown();
  2159. void LevelInit( char const *mapname );
  2160. void LevelShutdown();
  2161. virtual CAudioSourceCachedInfo *GetInfo( int audiosourcetype, bool soundisprecached, CSfxTable *sfx );
  2162. virtual CAudioSourceCachedInfo *GetInfoByName( const char *soundName );
  2163. virtual void RebuildCacheEntry( int audiosourcetype, bool soundisprecached, CSfxTable *sfx );
  2164. bool BuildMasterPrecachedSoundsCache();
  2165. bool UpdateMasterPrecachedSoundsCache();
  2166. void WriteManifest();
  2167. void ValidateSoundCache( char const *pchWavFile );
  2168. private:
  2169. // Purpose:
  2170. CacheType_t *LookUpCacheEntry( const char *fn, int audiosourcetype, bool soundisprecached, CSfxTable *sfx );
  2171. struct AudioSourceUsage_t
  2172. {
  2173. AudioSourceUsage_t() :
  2174. handle( 0 ),
  2175. count( 0u )
  2176. {
  2177. }
  2178. FileNameHandle_t handle;
  2179. unsigned int count;
  2180. };
  2181. static bool AudioSourceUsageLessFunc( const AudioSourceUsage_t& lhs, const AudioSourceUsage_t& rhs )
  2182. {
  2183. return lhs.handle < rhs.handle;
  2184. }
  2185. CacheType_t *AllocAudioCache( char const *cachename, bool bNeverCheckDisk );
  2186. bool LoadMasterCache( char const *pchLanguage, bool bAllowEmpty );
  2187. void RecursiveBuildSoundList( CUtlRBTree< FileNameHandle_t, int >& other, const char *pCurrentLanguage, const char *pCurrentDir, const char *pPathID );
  2188. CacheType_t *BuildCacheFromList( char const *cachename, CUtlRBTree< FileNameHandle_t, int >& list, bool showprogress = false, float flProgressStart = 0.0f, float flProgressEnd = 1.0f );
  2189. bool IsValidCache( char const *cachename );
  2190. void RemoveCache( char const *cachename );
  2191. void GetAudioCacheLanguageSuffix( CUtlString &sLanguage );
  2192. void PrefetchCommandSounds();
  2193. enum
  2194. {
  2195. MAX_LIST_SIZE = 1024
  2196. };
  2197. CUtlString m_szMODPath;
  2198. CUtlString m_szMapCacheBase;
  2199. CUtlString m_szMasterCache;
  2200. CUtlString m_szCurrentLanguage;
  2201. typedef enum
  2202. {
  2203. CACHE_MASTER,
  2204. CACHE_BUILDING
  2205. } SoundCacheType_t;
  2206. void SetCachePointer( SoundCacheType_t ptrType, CacheType_t *ptr );
  2207. // All sounds (no startup data) referenced anywhere in game
  2208. CacheType_t *m_pMasterSoundCache;
  2209. CacheType_t *m_pBuildingCache;
  2210. int m_nServerCount;
  2211. };
  2212. static CAudioSourceCache g_ASCache;
  2213. IAudioSourceCache *audiosourcecache = &g_ASCache;
  2214. unsigned int CAudioSourceCachedInfoHandle_t::s_nCurrentFlushCount = 1;
  2215. //-----------------------------------------------------------------------------
  2216. // Purpose:
  2217. //-----------------------------------------------------------------------------
  2218. void CAudioSourceCachedInfoHandle_t::InvalidateCache()
  2219. {
  2220. ++s_nCurrentFlushCount;
  2221. }
  2222. bool CAudioSourceCache::LoadMasterCache( char const *pchLanguage, bool bAllowEmpty )
  2223. {
  2224. m_szMasterCache = CFmtStr( "%s/_master%s.cache", AUDIOSOURCE_CACHE_ROOTDIR, pchLanguage );
  2225. char fullpath[ MAX_PATH ];
  2226. Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", m_szMODPath.String(), m_szMasterCache.String() );
  2227. // Just for display
  2228. Q_FixSlashes( fullpath, INCORRECT_PATH_SEPARATOR );
  2229. Q_strlower( fullpath );
  2230. DevMsg( 1, "Trying cache : '%s'\n", fullpath );
  2231. CacheType_t *cache = AllocAudioCache( m_szMasterCache.String(), true );
  2232. Assert( cache );
  2233. if ( !cache->Init() ||
  2234. ( !bAllowEmpty && cache->Count() == 0 ) )
  2235. {
  2236. Warning( "Failed to init '%s'\n", m_szMasterCache.String() );
  2237. m_szMasterCache = "";
  2238. delete cache;
  2239. return false;
  2240. }
  2241. else
  2242. {
  2243. DevMsg( 1, "Successfully loaded audio cache file\n" );
  2244. SetCachePointer( CACHE_MASTER, cache );
  2245. }
  2246. return true;
  2247. }
  2248. //-----------------------------------------------------------------------------
  2249. // Purpose:
  2250. // Output : Returns true on success, false on failure.
  2251. //-----------------------------------------------------------------------------
  2252. bool CAudioSourceCache::Init( unsigned int memSize )
  2253. {
  2254. #if defined( _DEBUG )
  2255. Msg( "CAudioSourceCache: Init\n" );
  2256. #endif
  2257. if ( !wavedatacache->Init( memSize ) )
  2258. {
  2259. Error( "Unable to init wavedatacache system\n" );
  2260. return false;
  2261. }
  2262. if ( IsGameConsole() )
  2263. {
  2264. // 360 doesn't use audio source caches
  2265. return true;
  2266. }
  2267. GetAudioCacheLanguageSuffix( m_szCurrentLanguage );
  2268. if ( m_szCurrentLanguage.Length() > 0 )
  2269. {
  2270. DevMsg( 1, "Audio Caches using '%s' as suffix\n", m_szCurrentLanguage.String() );
  2271. }
  2272. char sz[ MAX_PATH ];
  2273. Q_strncpy( sz, g_pSoundServices->GetGameDir(), sizeof( sz ) );
  2274. Q_StripTrailingSlash( sz );
  2275. // Special handling for -tempcontent so that audio caches are loaded out of the tempentent "GAME" dir rather than the main "MOD" dir.
  2276. // To do this we pass in a full path so that the CUtlCachedFileData doesn't get to specify that it only wants "MOD" search path
  2277. if ( CommandLine()->FindParm( "-tempcontent" ) )
  2278. {
  2279. Q_strncat( sz, "_tempcontent", sizeof( sz ), COPY_ALL_CHARACTERS );
  2280. }
  2281. V_StripTrailingSlash( sz );
  2282. char szDLCPath[ MAX_PATH ];
  2283. int nHighestDLC = 1;
  2284. for ( ;nHighestDLC <= 99; nHighestDLC++ )
  2285. {
  2286. V_snprintf( szDLCPath, sizeof( szDLCPath ), "%s_dlc%d", sz, nHighestDLC );
  2287. if ( !g_pFullFileSystem->IsDirectory( szDLCPath ) )
  2288. {
  2289. // does not exist, highest dlc available is previous
  2290. nHighestDLC--;
  2291. break;
  2292. }
  2293. V_snprintf( szDLCPath, sizeof( szDLCPath ), "%s_dlc%d/dlc_disabled.txt", sz, nHighestDLC );
  2294. if ( g_pFullFileSystem->FileExists( szDLCPath ) )
  2295. {
  2296. // disabled, highest dlc available is previous
  2297. nHighestDLC--;
  2298. break;
  2299. }
  2300. }
  2301. if ( nHighestDLC > 0 )
  2302. {
  2303. V_snprintf( szDLCPath, sizeof( szDLCPath ), "%s_dlc%d", sz, nHighestDLC );
  2304. }
  2305. else
  2306. {
  2307. V_strncpy( szDLCPath, sz, sizeof( szDLCPath ) );
  2308. }
  2309. Q_FixSlashes( szDLCPath );
  2310. Q_strlower( szDLCPath );
  2311. m_szMODPath = szDLCPath;
  2312. // Add trailing slash
  2313. m_szMODPath += CFmtStr( "%c", CORRECT_PATH_SEPARATOR );
  2314. g_pFullFileSystem->CreateDirHierarchy( CFmtStr( "%s%s", m_szMODPath.String(), AUDIOSOURCE_CACHE_ROOTDIR ), "GAME" );
  2315. // Assume failure
  2316. SetCachePointer( CACHE_MASTER, NULL );
  2317. bool bSuccess = LoadMasterCache( m_szCurrentLanguage, false );
  2318. if ( !bSuccess && Q_stricmp( m_szCurrentLanguage, "" ) )
  2319. {
  2320. bSuccess = LoadMasterCache( "", true );
  2321. }
  2322. if ( !bSuccess )
  2323. {
  2324. Warning( " .cache load failed, forcing rebuild [lang:%s]!\n", m_szCurrentLanguage.String() );
  2325. BuildMasterPrecachedSoundsCache();
  2326. }
  2327. // Tools mode always tries to update sound cache for now
  2328. else if ( CommandLine()->FindParm( "-tools" ) &&
  2329. !CommandLine()->FindParm( "-norebuildaudiocache" ) )
  2330. {
  2331. UpdateMasterPrecachedSoundsCache();
  2332. }
  2333. if ( snd_prefetch_common.GetBool() )
  2334. {
  2335. PrefetchCommandSounds();
  2336. }
  2337. return true;
  2338. }
  2339. //-----------------------------------------------------------------------------
  2340. // Purpose:
  2341. //-----------------------------------------------------------------------------
  2342. void CAudioSourceCache::Shutdown()
  2343. {
  2344. #if defined( _DEBUG )
  2345. Msg( "CAudioSourceCache: Shutdown\n" );
  2346. #endif
  2347. if ( !IsGameConsole() || IsPC() )
  2348. {
  2349. if ( m_pMasterSoundCache )
  2350. {
  2351. m_pMasterSoundCache->Shutdown();
  2352. delete m_pMasterSoundCache;
  2353. }
  2354. SetCachePointer( CACHE_MASTER, NULL );
  2355. }
  2356. wavedatacache->Shutdown();
  2357. }
  2358. //-----------------------------------------------------------------------------
  2359. // Purpose:
  2360. // Input : *cachename -
  2361. // Output : CacheType_t
  2362. //-----------------------------------------------------------------------------
  2363. CAudioSourceCache::CacheType_t *CAudioSourceCache::AllocAudioCache( char const *cachename, bool bNeverCheckDisk )
  2364. {
  2365. if ( IsGameConsole() )
  2366. {
  2367. return NULL;
  2368. }
  2369. CacheType_t *cache = new CacheType_t( CFmtStr( "%s%s", m_szMODPath.String(), cachename ), AUDIOSOURCE_CACHE_VERSION, NULL, UTL_CACHED_FILE_USE_FILESIZE, bNeverCheckDisk );
  2370. return cache;
  2371. }
  2372. //-----------------------------------------------------------------------------
  2373. // Purpose:
  2374. // Input : *mapname -
  2375. //-----------------------------------------------------------------------------
  2376. void CAudioSourceCache::LevelInit( char const *mapname )
  2377. {
  2378. m_szMapCacheBase = CFmtStr( "%s/%s", AUDIOSOURCE_CACHE_ROOTDIR, mapname );
  2379. }
  2380. //-----------------------------------------------------------------------------
  2381. // Purpose:
  2382. //-----------------------------------------------------------------------------
  2383. void CAudioSourceCache::LevelShutdown()
  2384. {
  2385. if ( IsGameConsole() )
  2386. {
  2387. // 360 not using
  2388. return;
  2389. }
  2390. // Get precached sound count and store manifest if running with -makereslists
  2391. if ( !CommandLine()->FindParm( "-makereslists" ) )
  2392. {
  2393. return;
  2394. }
  2395. int count = g_pSoundServices->GetPrecachedSoundCount();
  2396. if ( !count )
  2397. {
  2398. return;
  2399. }
  2400. // So that we only save this out once per level
  2401. if ( g_pSoundServices->GetServerCount() == m_nServerCount )
  2402. {
  2403. return;
  2404. }
  2405. m_nServerCount = g_pSoundServices->GetServerCount();
  2406. WriteManifest();
  2407. }
  2408. //-----------------------------------------------------------------------------
  2409. // Purpose:
  2410. //-----------------------------------------------------------------------------
  2411. void CAudioSourceCache::WriteManifest()
  2412. {
  2413. if ( IsGameConsole() )
  2414. {
  2415. // 360 not using
  2416. return;
  2417. }
  2418. int count = g_pSoundServices->GetPrecachedSoundCount();
  2419. if ( !count )
  2420. {
  2421. DevMsg( "Skipping WriteManifest, no entries in sound precache string table\n" );
  2422. return;
  2423. }
  2424. // Save manifest out to disk...
  2425. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  2426. for ( int i = 0; i < count; ++i )
  2427. {
  2428. char const *fn = g_pSoundServices->GetPrecachedSound( i );
  2429. if ( fn && fn[ 0 ] )
  2430. {
  2431. char full[ 512 ];
  2432. Q_snprintf( full, sizeof( full ), "sound\\%s", PSkipSoundChars( fn ) );
  2433. Q_strlower( full );
  2434. Q_FixSlashes( full );
  2435. // Write to file
  2436. buf.Printf( "\"%s\"\r\n", full );
  2437. }
  2438. }
  2439. g_pFullFileSystem->CreateDirHierarchy( AUDIOSOURCE_CACHE_ROOTDIR, "MOD" );
  2440. char manifest_name[ 512 ];
  2441. Q_snprintf( manifest_name, sizeof( manifest_name ), "%s.manifest", m_szMapCacheBase.String() );
  2442. if ( g_pFullFileSystem->FileExists( manifest_name, "MOD" ) &&
  2443. !g_pFullFileSystem->IsFileWritable( manifest_name, "MOD" ) )
  2444. {
  2445. g_pFullFileSystem->SetFileWritable( manifest_name, true, "MOD" );
  2446. }
  2447. // Now write to file
  2448. FileHandle_t fh;
  2449. fh = g_pFullFileSystem->Open( manifest_name, "wb" );
  2450. if ( FILESYSTEM_INVALID_HANDLE != fh )
  2451. {
  2452. g_pFullFileSystem->Write( buf.Base(), buf.TellPut(), fh );
  2453. g_pFullFileSystem->Close( fh );
  2454. DevMsg( "WriteManifest: Persisting cache manifest '%s' (%d entries)\n", manifest_name, count );
  2455. }
  2456. else
  2457. {
  2458. Warning( "WriteManifest: Unable to persist cache manifest '%s', check file permissions\n", manifest_name );
  2459. }
  2460. }
  2461. //-----------------------------------------------------------------------------
  2462. // Purpose:
  2463. //-----------------------------------------------------------------------------
  2464. CAudioSourceCache::CacheType_t *CAudioSourceCache::LookUpCacheEntry( const char *fn, int audiosourcetype, bool soundisprecached, CSfxTable *sfx )
  2465. {
  2466. if ( IsGameConsole() )
  2467. {
  2468. return NULL;
  2469. }
  2470. // Hack to remember the type of audiosource to create if we need to recreate it
  2471. CAudioSourceCachedInfo::s_CurrentType = audiosourcetype;
  2472. CAudioSourceCachedInfo::s_pSfx = sfx;
  2473. CAudioSourceCachedInfo::s_bIsPrecacheSound = false;
  2474. CacheType_t *pCache = NULL;
  2475. // If building a cache, just shortcut through to target cache
  2476. if ( m_pBuildingCache )
  2477. {
  2478. pCache = m_pBuildingCache;
  2479. }
  2480. else
  2481. {
  2482. // Grab from the full master list
  2483. pCache = m_pMasterSoundCache;
  2484. }
  2485. return pCache;
  2486. }
  2487. CAudioSourceCachedInfo *CAudioSourceCache::GetInfoByName( const char *soundName )
  2488. {
  2489. VPROF("CAudioSourceCache::GetInfoByName");
  2490. if ( IsGameConsole() )
  2491. {
  2492. // 360 not using
  2493. return NULL;
  2494. }
  2495. if ( !m_pMasterSoundCache )
  2496. {
  2497. return NULL;
  2498. }
  2499. return m_pMasterSoundCache->Get( soundName );
  2500. }
  2501. //-----------------------------------------------------------------------------
  2502. // Purpose:
  2503. //-----------------------------------------------------------------------------
  2504. CAudioSourceCachedInfo *CAudioSourceCache::GetInfo( int audiosourcetype, bool soundisprecached, CSfxTable *sfx )
  2505. {
  2506. VPROF("CAudioSourceCache::GetInfo");
  2507. if ( IsGameConsole() )
  2508. {
  2509. // 360 not using
  2510. return NULL;
  2511. }
  2512. Assert( sfx );
  2513. char fn[ 512 ];
  2514. char nameBuf[MAX_PATH];
  2515. Q_snprintf( fn, sizeof( fn ), "sound/%s", sfx->GetFileName( nameBuf, sizeof(nameBuf) ) );
  2516. CAudioSourceCachedInfo *info = NULL;
  2517. CacheType_t *pCache = LookUpCacheEntry( fn, audiosourcetype, soundisprecached, sfx );
  2518. if ( !pCache )
  2519. return NULL;
  2520. info = pCache->Get( fn );
  2521. if ( info && info->Format() == 0 )
  2522. {
  2523. if ( g_pFullFileSystem->FileExists( fn, "BSP" ) )
  2524. {
  2525. DevMsg( 1, "Forced rebuild of bsp cache sound '%s'\n", fn );
  2526. info = pCache->RebuildItem( fn );
  2527. Assert( info->Format() != 0 );
  2528. }
  2529. }
  2530. return info;
  2531. }
  2532. void CAudioSourceCache::RebuildCacheEntry( int audiosourcetype, bool soundisprecached, CSfxTable *sfx )
  2533. {
  2534. VPROF("CAudioSourceCache::GetInfo");
  2535. if ( IsGameConsole() )
  2536. {
  2537. // 360 not using
  2538. return;
  2539. }
  2540. Assert( sfx );
  2541. char fn[ 512 ];
  2542. char nameBuf[MAX_PATH];
  2543. Q_snprintf( fn, sizeof( fn ), "sound/%s", sfx->GetFileName( nameBuf, sizeof(nameBuf) ) );
  2544. CacheType_t *pCache = LookUpCacheEntry( fn, audiosourcetype, soundisprecached, sfx );
  2545. if ( !pCache )
  2546. return;
  2547. pCache->RebuildItem( fn );
  2548. }
  2549. //-----------------------------------------------------------------------------
  2550. // Purpose:
  2551. // Input : *cachename -
  2552. // Output : Returns true on success, false on failure.
  2553. //-----------------------------------------------------------------------------
  2554. bool CAudioSourceCache::IsValidCache( char const *cachename )
  2555. {
  2556. if ( IsGameConsole() )
  2557. {
  2558. return false;
  2559. }
  2560. CacheType_t *cache = AllocAudioCache( cachename, true );
  2561. // This will delete any outdated .cache files
  2562. bool valid = cache->IsUpToDate();
  2563. delete cache;
  2564. return valid;
  2565. }
  2566. //-----------------------------------------------------------------------------
  2567. // Purpose:
  2568. // Input : *cachename -
  2569. //-----------------------------------------------------------------------------
  2570. void CAudioSourceCache::RemoveCache( char const *cachename )
  2571. {
  2572. if ( IsGameConsole() )
  2573. {
  2574. return;
  2575. }
  2576. if ( g_pFullFileSystem->FileExists( cachename, "MOD" ) )
  2577. {
  2578. if ( !g_pFullFileSystem->IsFileWritable( cachename, "MOD" ) )
  2579. {
  2580. g_pFullFileSystem->SetFileWritable( cachename, true, "MOD" );
  2581. }
  2582. g_pFullFileSystem->RemoveFile( cachename, "MOD" );
  2583. }
  2584. }
  2585. void CAudioSourceCache::SetCachePointer( SoundCacheType_t ptrType, CacheType_t *ptr )
  2586. {
  2587. if ( IsGameConsole() )
  2588. {
  2589. return;
  2590. }
  2591. bool dirty = false;
  2592. switch ( ptrType )
  2593. {
  2594. default:
  2595. Error( "SetCachePointer with bogus type %i\n", (int)ptrType );
  2596. break;
  2597. case CACHE_MASTER:
  2598. if ( m_pMasterSoundCache != ptr )
  2599. {
  2600. dirty = true;
  2601. m_pMasterSoundCache = ptr;
  2602. }
  2603. break;
  2604. case CACHE_BUILDING:
  2605. if ( m_pBuildingCache != ptr )
  2606. {
  2607. dirty = true;
  2608. m_pBuildingCache = ptr;
  2609. }
  2610. break;
  2611. }
  2612. if ( dirty )
  2613. {
  2614. CAudioSourceCachedInfoHandle_t::InvalidateCache();
  2615. }
  2616. }
  2617. void CAudioSourceCache::ValidateSoundCache( char const *pchWavFile )
  2618. {
  2619. Assert( m_pMasterSoundCache );
  2620. if ( !m_pMasterSoundCache )
  2621. return;
  2622. m_pMasterSoundCache->SetNeverCheckDisk( false );
  2623. // Touch the cache
  2624. CSfxTable *pTable = S_PrecacheSound( &pchWavFile[ SOUND_DIRECTORY_LENGTH ] );
  2625. if ( pTable && pTable->pSource )
  2626. {
  2627. CAudioSourceCachedInfo::s_CurrentType = pTable->pSource->GetType();
  2628. CAudioSourceCachedInfo::s_pSfx = pTable;
  2629. CAudioSourceCachedInfo::s_bIsPrecacheSound = false;
  2630. g_bSoundRebuilt = false;
  2631. m_pMasterSoundCache->RecheckItem( pchWavFile );
  2632. if ( g_bSoundRebuilt )
  2633. {
  2634. Msg( " updated '%s'\n", (char *)&pchWavFile[ SOUND_DIRECTORY_LENGTH ] );
  2635. }
  2636. }
  2637. m_pMasterSoundCache->SetNeverCheckDisk( true );
  2638. // Persist data to HD if dirty
  2639. if ( m_pMasterSoundCache->IsDirty() )
  2640. {
  2641. m_pMasterSoundCache->Save();
  2642. }
  2643. }
  2644. bool CAudioSourceCache::UpdateMasterPrecachedSoundsCache()
  2645. {
  2646. Assert( m_pMasterSoundCache );
  2647. if ( !m_pMasterSoundCache )
  2648. return true;
  2649. float flStart = Plat_FloatTime();
  2650. Msg( "Updating sound cache [%d entries]\n", m_pMasterSoundCache->Count() );
  2651. CUtlRBTree< FileNameHandle_t, int > soundsOnDisk( 0, 0, DefLessFunc( FileNameHandle_t ) );
  2652. // Build recursive list of all wav files for all languages
  2653. RecursiveBuildSoundList( soundsOnDisk, m_szCurrentLanguage, "sound", "GAME" );
  2654. Msg( "Found %d sound files on disk\n", soundsOnDisk.Count() );
  2655. char soundname[ 512 ];
  2656. m_pMasterSoundCache->SetNeverCheckDisk( false );
  2657. m_pMasterSoundCache->ForceRecheckDiskInfo();
  2658. int nUpdated = 0;
  2659. for ( int i = soundsOnDisk.FirstInorder();
  2660. i != soundsOnDisk.InvalidIndex();
  2661. i = soundsOnDisk.NextInorder( i ) )
  2662. {
  2663. FileNameHandle_t& handle = soundsOnDisk[ i ];
  2664. soundname[ 0 ] = 0;
  2665. g_pFullFileSystem->String( handle, soundname, sizeof( soundname ) );
  2666. g_bSoundRebuilt = false;
  2667. // Touch the cache
  2668. CSfxTable *pTable = S_PrecacheSound( &soundname[ SOUND_DIRECTORY_LENGTH ] );
  2669. if ( pTable && pTable->pSource )
  2670. {
  2671. CAudioSourceCachedInfo::s_CurrentType = pTable->pSource->GetType();
  2672. CAudioSourceCachedInfo::s_pSfx = pTable;
  2673. CAudioSourceCachedInfo::s_bIsPrecacheSound = false;
  2674. m_pMasterSoundCache->Get( soundname );
  2675. }
  2676. if ( g_bSoundRebuilt )
  2677. {
  2678. ++nUpdated;
  2679. Msg( " updated '%s'\n", (char *)&soundname[ SOUND_DIRECTORY_LENGTH ] );
  2680. }
  2681. }
  2682. m_pMasterSoundCache->SetNeverCheckDisk( true );
  2683. // Persist data to HD if dirty
  2684. m_pMasterSoundCache->Save();
  2685. float flEnd = Plat_FloatTime();
  2686. Msg( "Updated %i out of %i cached files [%.3f msec]\n", nUpdated, soundsOnDisk.Count(), 1000.0f * ( flEnd - flStart ) );
  2687. CAudioSourceCachedInfoHandle_t::InvalidateCache();
  2688. return true;
  2689. }
  2690. //-----------------------------------------------------------------------------
  2691. // Purpose:
  2692. // Input : showprogress -
  2693. // Output : Returns true on success, false on failure.
  2694. //-----------------------------------------------------------------------------
  2695. bool CAudioSourceCache::BuildMasterPrecachedSoundsCache()
  2696. {
  2697. if ( IsGameConsole() )
  2698. {
  2699. // 360 not using
  2700. return false;
  2701. }
  2702. char fn[ 512 ];
  2703. Q_snprintf( fn, sizeof( fn ), "%s/%s%s.cache", AUDIOSOURCE_CACHE_ROOTDIR, MASTER_CACHE_NAME, m_szCurrentLanguage.String() );
  2704. DevMsg( 1, "Fast Build Temp Cache: '%s'\n", fn );
  2705. // Blow away the cache if rebuilding, which will force a full cache build
  2706. RemoveCache( fn );
  2707. g_pSoundServices->CacheBuildingStart();
  2708. CacheType_t *pOtherNoData = NULL;
  2709. CUtlRBTree< FileNameHandle_t, int > other( 0, 0, DefLessFunc( FileNameHandle_t ) );
  2710. // Build recursive list of all wav files for all languages
  2711. RecursiveBuildSoundList( other, m_szCurrentLanguage, "sound", "GAME" );
  2712. pOtherNoData = BuildCacheFromList( fn, other, true, 0.0f, 1.0f );
  2713. if ( pOtherNoData )
  2714. {
  2715. if ( m_pMasterSoundCache )
  2716. {
  2717. // Don't shutdown/save, since we have a new one already
  2718. delete m_pMasterSoundCache;
  2719. }
  2720. // Take over ptr
  2721. SetCachePointer( CACHE_MASTER, pOtherNoData );
  2722. }
  2723. g_pSoundServices->CacheBuildingFinish();
  2724. return true;
  2725. }
  2726. //-----------------------------------------------------------------------------
  2727. void CAudioSourceCache::RecursiveBuildSoundList( CUtlRBTree< FileNameHandle_t, int >& root, const char *pLanguage, const char *pCurrentDir, const char *pathID )
  2728. {
  2729. FileFindHandle_t fh;
  2730. FileNameHandle_t handle;
  2731. char path[ 512 ];
  2732. Q_snprintf( path, sizeof( path ), "%s/*.*", pCurrentDir );
  2733. Q_FixSlashes( path );
  2734. char const *fn = g_pFullFileSystem->FindFirstEx( path, pathID, &fh );
  2735. if ( fn )
  2736. {
  2737. do
  2738. {
  2739. if ( *fn != '.' )
  2740. {
  2741. if ( g_pFullFileSystem->FindIsDirectory( fh ) )
  2742. {
  2743. char nextdir[ 512 ];
  2744. Q_snprintf( nextdir, sizeof( nextdir ), "%s/%s", pCurrentDir, fn );
  2745. RecursiveBuildSoundList( root, pLanguage, nextdir, pathID );
  2746. }
  2747. else
  2748. {
  2749. char ext[ 10 ];
  2750. Q_ExtractFileExtension( fn, ext, sizeof( ext ) );
  2751. if ( ( !Q_stricmp( ext, "wav" ) || !Q_stricmp( ext, "mp3" ) ) && !Q_stristr( fn, ".360." ) && !Q_stristr( fn, ".ps3." ) )
  2752. {
  2753. char relative[ 512 ];
  2754. Q_snprintf( relative, sizeof( relative ), "%s/%s", pCurrentDir, fn );
  2755. Q_FixSlashes( relative );
  2756. handle = g_pFullFileSystem->FindOrAddFileName( relative );
  2757. if ( root.Find( handle ) == root.InvalidIndex() )
  2758. {
  2759. root.Insert( handle );
  2760. }
  2761. }
  2762. }
  2763. }
  2764. fn = g_pFullFileSystem->FindNext( fh );
  2765. } while ( fn );
  2766. g_pFullFileSystem->FindClose( fh );
  2767. }
  2768. }
  2769. //-----------------------------------------------------------------------------
  2770. // Purpose:
  2771. // Input : *cachename -
  2772. // FileNameHandle_t -
  2773. // soundlist -
  2774. // fulldata -
  2775. // showprogress -
  2776. // Output : CAudioSourceCache::CacheType_t
  2777. //-----------------------------------------------------------------------------
  2778. CAudioSourceCache::CacheType_t *CAudioSourceCache::BuildCacheFromList( char const *cachename, CUtlRBTree< FileNameHandle_t, int >& soundlist, bool showprogress /*= false*/, float flProgressStart /*= 0.0f*/, float flProgressEnd /*= 1.0f*/ )
  2779. {
  2780. if ( IsGameConsole() )
  2781. {
  2782. // 360 not using
  2783. return NULL;
  2784. }
  2785. float flStart = Plat_FloatTime();
  2786. CacheType_t *newCache = NULL;
  2787. newCache = AllocAudioCache( cachename, false );
  2788. Assert( newCache );
  2789. if ( newCache->Init() )
  2790. {
  2791. SetCachePointer( CACHE_BUILDING, newCache );
  2792. int visited = 0;
  2793. for ( int i = soundlist.FirstInorder(); i != soundlist.InvalidIndex(); i = soundlist.NextInorder( i ) )
  2794. {
  2795. FileNameHandle_t& handle = soundlist[ i ];
  2796. char soundname[ 512 ];
  2797. soundname[ 0 ] = 0;
  2798. if ( g_pFullFileSystem->String( handle, soundname, sizeof( soundname ) ) )
  2799. {
  2800. // Touch the cache
  2801. // Force it to go into the "other" cache but to also appear as "full data" precache
  2802. CSfxTable *pTable = S_PrecacheSound( &soundname[ SOUND_DIRECTORY_LENGTH ] );
  2803. // This will "re-cache" this if it's not in this level's cache already
  2804. if ( pTable && pTable->pSource )
  2805. {
  2806. GetInfo( pTable->pSource->GetType(), false, pTable );
  2807. }
  2808. }
  2809. else
  2810. {
  2811. Assert( !"Unable to find FileNameHandle_t in fileystem list." );
  2812. }
  2813. ++visited;
  2814. if ( !( visited % 100 ) )
  2815. {
  2816. Msg( " progress %i/%i (%i %%)\n",
  2817. visited, soundlist.Count(), (int)( 100.0f * ( float) visited / (float) soundlist.Count() ) );
  2818. }
  2819. if ( showprogress )
  2820. {
  2821. float frac = ( float )( visited - 1 )/( float )soundlist.Count();
  2822. frac = flProgressStart + frac * ( flProgressEnd - flProgressStart );
  2823. char base[ 256 ];
  2824. Q_FileBase( soundname, base, sizeof( base ) );
  2825. Q_strlower( base );
  2826. g_pSoundServices->CacheBuildingUpdateProgress( frac, base );
  2827. }
  2828. }
  2829. Msg( "Touched %i cached files\n", soundlist.Count() );
  2830. SetCachePointer( CACHE_BUILDING, NULL );
  2831. // Persist data to HD if dirty
  2832. newCache->Save();
  2833. }
  2834. else
  2835. {
  2836. delete newCache;
  2837. newCache = NULL;
  2838. }
  2839. float flEnd = Plat_FloatTime();
  2840. Msg( "Elapsed time: %.2f seconds\n",
  2841. flEnd - flStart );
  2842. return newCache;
  2843. }
  2844. //-----------------------------------------------------------------------------
  2845. void CAudioSourceCache::GetAudioCacheLanguageSuffix( CUtlString &sLanguage )
  2846. {
  2847. char const *pchLanguage = g_pSoundServices->GetUILanguage();
  2848. if ( !pchLanguage || !*pchLanguage || !Q_stricmp( pchLanguage, "english" ) )
  2849. {
  2850. sLanguage = "";
  2851. return;
  2852. }
  2853. //Check language right here to see if we need the caches for it.
  2854. char szLanguageList[ MAX_PATH ];
  2855. Q_snprintf( szLanguageList, sizeof( szLanguageList ), "%s/localization_cache_list.txt", AUDIOSOURCE_CACHE_ROOTDIR );
  2856. FileHandle_t fh = g_pFullFileSystem->Open( szLanguageList, "r" );
  2857. char szCacheLanguage[ MAX_LIST_SIZE ];
  2858. if ( fh )
  2859. {
  2860. g_pFullFileSystem->Read( szCacheLanguage, MAX_LIST_SIZE, fh);
  2861. g_pFullFileSystem->Close( fh );
  2862. if ( Q_stristr( szCacheLanguage, pchLanguage ) )
  2863. {
  2864. sLanguage = CFmtStr( "_%s", pchLanguage );
  2865. return;
  2866. }
  2867. else
  2868. {
  2869. sLanguage = "";
  2870. return;
  2871. }
  2872. }
  2873. sLanguage = CFmtStr( "_%s", pchLanguage );
  2874. return;
  2875. }
  2876. void CAudioSourceCache::PrefetchCommandSounds()
  2877. {
  2878. if ( IsGameConsole() )
  2879. return;
  2880. if ( !m_pMasterSoundCache )
  2881. return;
  2882. CUtlVector< CUtlString > vecSearch;
  2883. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  2884. if ( g_pFullFileSystem->ReadFile( SOUND_PREFETCH_FILE, "GAME", buf ) )
  2885. {
  2886. characterset_t breakSet;
  2887. CharacterSetBuild( &breakSet, "" );
  2888. // parse reslist
  2889. char substr[MAX_PATH];
  2890. for ( ;; )
  2891. {
  2892. int nTokenSize = buf.ParseToken( &breakSet, substr, sizeof( substr ), true );
  2893. if ( nTokenSize <= 0 )
  2894. {
  2895. break;
  2896. }
  2897. Q_FixSlashes( substr );
  2898. Q_strlower( substr );
  2899. vecSearch.AddToTail( CUtlString( substr ) );
  2900. Msg( "Prefetching data for subdir: %s\n", substr );
  2901. }
  2902. }
  2903. // Nothing to do
  2904. if ( !vecSearch.Count() )
  2905. return;
  2906. COM_TimestampedLog( "PrefetchSounds Start" );
  2907. float flStart = Plat_FloatTime();
  2908. uint64 uBytesPrefeteched = 0ull;
  2909. uint32 uSoundsPrefetched = 0u;
  2910. // Now walk the cache and prefetch shiz
  2911. for ( int i = 0 ; i < m_pMasterSoundCache->Count(); ++i )
  2912. {
  2913. char szFile[ MAX_PATH ];
  2914. m_pMasterSoundCache->GetElementName( i, szFile, sizeof( szFile ) );
  2915. Q_FixSlashes( szFile );
  2916. Q_strlower( szFile );
  2917. for ( int j = 0; j < vecSearch.Count(); ++j )
  2918. {
  2919. const CUtlString &str = vecSearch[ j ];
  2920. if ( Q_stristr( szFile, str ) )
  2921. {
  2922. ++uSoundsPrefetched;
  2923. const CAudioSourceCachedInfo *info = (*m_pMasterSoundCache)[ i ];
  2924. uBytesPrefeteched += info->DataSize();
  2925. S_PrefetchSound( szFile, false );
  2926. break;
  2927. }
  2928. }
  2929. }
  2930. float flEnd = Plat_FloatTime();
  2931. COM_TimestampedLog( "PrefetchSounds Finish" );
  2932. Msg( "Prefetched %u sounds, %s [%.3f msec]\n",
  2933. uSoundsPrefetched, Q_pretifymem( uBytesPrefeteched ), 1000.0f * ( flEnd - flStart ) );
  2934. }
  2935. CON_COMMAND( snd_rebuildaudiocache, "rebuild audio cache for current language\n" )
  2936. {
  2937. g_ASCache.BuildMasterPrecachedSoundsCache();
  2938. }
  2939. CON_COMMAND( snd_writemanifest, "If running a game, outputs the precache manifest for the current level\n" )
  2940. {
  2941. g_ASCache.WriteManifest();
  2942. }
  2943. CON_COMMAND( snd_updateaudiocache, "checks _master.cache based on file sizes and rebuilds any change/new entries\n" )
  2944. {
  2945. g_ASCache.UpdateMasterPrecachedSoundsCache();
  2946. }
  2947. void S_ValidateSoundCache( char const *pchWavFile )
  2948. {
  2949. g_ASCache.ValidateSoundCache( pchWavFile );
  2950. }