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

2816 lines
72 KiB

  1. //========= Copyright 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 "tier2/fileutils.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. // This determines how much data to pre-cache (will invalidate per-map caches if changed).
  20. #define SND_ASYNC_LOOKAHEAD_SECONDS ( 0.125f )
  21. extern ConVar snd_async_spew_blocking;
  22. ConVar snd_async_minsize("snd_async_minsize", "262144");
  23. // #define DEBUG_CHUNKS
  24. //-----------------------------------------------------------------------------
  25. // Purpose: Report chunk error
  26. // Input : id - chunk FOURCC
  27. //-----------------------------------------------------------------------------
  28. void ChunkError( unsigned int id )
  29. {
  30. #if defined( DEBUG_CHUNKS ) && defined( _DEBUG )
  31. if ( id == WAVE_LIST || id == WAVE_FACT )
  32. {
  33. // unused chunks, not an error
  34. return;
  35. }
  36. char tmp[256];
  37. char idname[5];
  38. idname[4] = 0;
  39. memcpy( idname, &id, 4 );
  40. Q_snprintf( tmp, sizeof( tmp ), "Unhandled chunk %s\n", idname );
  41. Plat_DebugString( tmp );
  42. #endif
  43. }
  44. //-----------------------------------------------------------------------------
  45. // Purpose: Determine a true sample count for an ADPCM blob
  46. //-----------------------------------------------------------------------------
  47. int ADPCMSampleCount( ADPCMWAVEFORMAT *pFormat, int length )
  48. {
  49. // determine a true sample count
  50. int nChannels = LittleWord( pFormat->wfx.nChannels );
  51. int wSamplesPerBlock = LittleWord( pFormat->wSamplesPerBlock );
  52. int blockSize = (( wSamplesPerBlock - 2) * nChannels ) / 2;
  53. blockSize += 7 * nChannels;
  54. int blockCount = length / blockSize;
  55. int blockRem = length % blockSize;
  56. // total samples in complete blocks
  57. int sampleCount = blockCount * wSamplesPerBlock;
  58. // add remaining in a short block
  59. if ( blockRem )
  60. {
  61. sampleCount += wSamplesPerBlock - (((blockSize - blockRem) * 2) / nChannels );
  62. }
  63. return sampleCount;
  64. }
  65. //-----------------------------------------------------------------------------
  66. // Purpose: Init to empty wave
  67. //-----------------------------------------------------------------------------
  68. CAudioSourceWave::CAudioSourceWave( CSfxTable *pSfx )
  69. {
  70. m_format = 0;
  71. m_pHeader = NULL;
  72. m_nHeaderSize = 0;
  73. // no looping
  74. m_loopStart = -1;
  75. m_sampleSize = 1;
  76. m_sampleCount = 0;
  77. m_bits = 0;
  78. m_channels = 0;
  79. m_dataStart = 0;
  80. m_dataSize = 0;
  81. m_rate = 0;
  82. m_refCount = 0;
  83. m_pSfx = pSfx;
  84. #ifdef _DEBUG
  85. if ( m_pSfx )
  86. m_pDebugName = strdup( m_pSfx->getname() );
  87. #endif
  88. m_bNoSentence = false;
  89. m_pTempSentence = NULL;
  90. m_nCachedDataSize = 0;
  91. m_bIsPlayOnce = false;
  92. m_bIsSentenceWord = false;
  93. m_numDecodedSamples = 0;
  94. }
  95. CAudioSourceWave::CAudioSourceWave( CSfxTable *pSfx, CAudioSourceCachedInfo *info )
  96. {
  97. m_pSfx = pSfx;
  98. #ifdef _DEBUG
  99. if ( m_pSfx )
  100. m_pDebugName = strdup( m_pSfx->getname() );
  101. #endif
  102. m_refCount = 0;
  103. m_pHeader = NULL;
  104. m_nHeaderSize = 0;
  105. if ( info->HeaderData() )
  106. {
  107. m_pHeader = new char[ info->HeaderSize() ];
  108. Assert( m_pHeader );
  109. Q_memcpy( m_pHeader, info->HeaderData(), info->HeaderSize() );
  110. m_nHeaderSize = info->HeaderSize();
  111. }
  112. m_bits = info->Bits();
  113. m_channels = info->Channels();
  114. m_sampleSize = info->SampleSize();
  115. m_format = info->Format();
  116. m_dataStart = info->DataStart();
  117. m_dataSize = info->DataSize();
  118. m_rate = info->SampleRate();
  119. m_loopStart = info->LoopStart();
  120. m_sampleCount = info->SampleCount();
  121. m_numDecodedSamples = m_sampleCount;
  122. if ( m_format == WAVE_FORMAT_ADPCM && m_pHeader )
  123. {
  124. m_numDecodedSamples = ADPCMSampleCount( (ADPCMWAVEFORMAT *)m_pHeader, m_sampleCount );
  125. }
  126. m_bNoSentence = false;
  127. m_pTempSentence = NULL;
  128. m_nCachedDataSize = 0;
  129. m_bIsPlayOnce = false;
  130. m_bIsSentenceWord = false;
  131. }
  132. CAudioSourceWave::~CAudioSourceWave( void )
  133. {
  134. #if _DEBUG
  135. if ( !CanDelete() )
  136. Assert(0);
  137. #endif
  138. // for non-standard waves, we store a copy of the header in RAM
  139. delete[] m_pHeader;
  140. delete m_pTempSentence;
  141. }
  142. int CAudioSourceWave::GetType( void )
  143. {
  144. return AUDIO_SOURCE_WAV;
  145. }
  146. void CAudioSourceWave::GetCacheData( CAudioSourceCachedInfo *info )
  147. {
  148. Assert( info->Type() == CAudioSource::AUDIO_SOURCE_WAV );
  149. byte tempbuf[ 32768 ];
  150. int datalen = 0;
  151. // NOTE GetStartupData has side-effects (...) hence the unconditional call
  152. if ( GetStartupData( tempbuf, sizeof( tempbuf ), datalen ) &&
  153. info->s_bIsPrecacheSound &&
  154. datalen > 0 )
  155. {
  156. byte *data = new byte[ datalen ];
  157. Q_memcpy( data, tempbuf, datalen );
  158. info->SetCachedDataSize( datalen );
  159. info->SetCachedData( data );
  160. }
  161. info->SetBits( m_bits );
  162. info->SetChannels( m_channels );
  163. info->SetSampleSize( m_sampleSize );
  164. info->SetFormat( m_format );
  165. info->SetDataStart( m_dataStart ); // offset of wave data chunk
  166. info->SetDataSize( m_dataSize ); // size of wave data chunk
  167. info->SetSampleRate( m_rate );
  168. info->SetLoopStart( m_loopStart );
  169. info->SetSampleCount( m_sampleCount );
  170. if ( m_pTempSentence )
  171. {
  172. CSentence *scopy = new CSentence;
  173. *scopy = *m_pTempSentence;
  174. info->SetSentence( scopy );
  175. // Wipe it down to basically nothing
  176. delete m_pTempSentence;
  177. m_pTempSentence = NULL;
  178. }
  179. if ( m_pHeader && m_nHeaderSize > 0 )
  180. {
  181. byte *data = new byte[ m_nHeaderSize ];
  182. Q_memcpy( data, m_pHeader, m_nHeaderSize );
  183. info->SetHeaderSize( m_nHeaderSize );
  184. info->SetHeaderData( data );
  185. }
  186. }
  187. //-----------------------------------------------------------------------------
  188. // Purpose:
  189. // Output : char const
  190. //-----------------------------------------------------------------------------
  191. char const *CAudioSourceWave::GetFileName()
  192. {
  193. return m_pSfx ? m_pSfx->GetFileName() : "NULL m_pSfx";
  194. }
  195. //-----------------------------------------------------------------------------
  196. // Purpose:
  197. // Output : Returns true on success, false on failure.
  198. //-----------------------------------------------------------------------------
  199. bool CAudioSourceWave::IsAsyncLoad()
  200. {
  201. VPROF("CAudioSourceWave::IsAsyncLoad");
  202. if ( ( IsPC() || !IsX360() ) && !m_AudioCacheHandle.IsValid() )
  203. {
  204. m_AudioCacheHandle.Get( GetType(), m_pSfx->IsPrecachedSound(), m_pSfx, &m_nCachedDataSize );
  205. }
  206. // If there's a bit of "cached data" then we don't have to lazy/async load (we still async load the remaining data,
  207. // but we run from the cache initially)
  208. if ( m_dataSize > snd_async_minsize.GetInt() )
  209. return true;
  210. return ( m_nCachedDataSize > 0 ) ? false : true;
  211. }
  212. //-----------------------------------------------------------------------------
  213. // Purpose:
  214. //-----------------------------------------------------------------------------
  215. void CAudioSourceWave::CheckAudioSourceCache()
  216. {
  217. if ( IsX360() )
  218. {
  219. // 360 does not use audio cache files
  220. return;
  221. }
  222. Assert( m_pSfx );
  223. if ( !m_pSfx || !m_pSfx->IsPrecachedSound() )
  224. {
  225. return;
  226. }
  227. // This will "re-cache" this if it's not in this level's cache already
  228. m_AudioCacheHandle.Get( GetType(), true, m_pSfx, &m_nCachedDataSize );
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Purpose: Init the wave data.
  232. // Input : *pHeaderBuffer - the RIFF fmt chunk
  233. // headerSize - size of that chunk
  234. //-----------------------------------------------------------------------------
  235. void CAudioSourceWave::Init( const char *pHeaderBuffer, int headerSize )
  236. {
  237. const WAVEFORMATEX *pHeader = (const WAVEFORMATEX *)pHeaderBuffer;
  238. // copy the relevant header data
  239. m_format = LittleWord( pHeader->wFormatTag );
  240. m_bits = LittleWord( pHeader->wBitsPerSample );
  241. m_rate = LittleDWord( pHeader->nSamplesPerSec );
  242. m_channels = LittleWord( pHeader->nChannels );
  243. m_sampleSize = (m_bits * m_channels)/8;
  244. // this can never be zero -- other functions divide by this.
  245. // this should never happen, but avoid crashing
  246. if ( m_sampleSize <= 0 )
  247. {
  248. m_sampleSize = 1;
  249. }
  250. if ( m_format == WAVE_FORMAT_ADPCM )
  251. {
  252. // For non-standard waves (like ADPCM) store the header, it has the decoding coefficients
  253. m_pHeader = new char[headerSize];
  254. memcpy( m_pHeader, pHeader, headerSize );
  255. m_nHeaderSize = headerSize;
  256. // treat ADPCM sources as a file of bytes. They are decoded by the mixer
  257. m_sampleSize = 1;
  258. }
  259. }
  260. int CAudioSourceWave::SampleRate( void )
  261. {
  262. return m_rate;
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Purpose: Size of each sample
  266. // Output :
  267. //-----------------------------------------------------------------------------
  268. int CAudioSourceWave::SampleSize( void )
  269. {
  270. return m_sampleSize;
  271. }
  272. //-----------------------------------------------------------------------------
  273. // Purpose: Total number of samples in this source
  274. // Output : int
  275. //-----------------------------------------------------------------------------
  276. int CAudioSourceWave::SampleCount( void )
  277. {
  278. // caller wants real samples
  279. return m_numDecodedSamples;
  280. }
  281. int CAudioSourceWave::Format( void )
  282. {
  283. return m_format;
  284. }
  285. int CAudioSourceWave::DataSize( void )
  286. {
  287. return m_dataSize;
  288. }
  289. bool CAudioSourceWave::IsVoiceSource()
  290. {
  291. if ( GetSentence() )
  292. {
  293. if ( GetSentence()->GetVoiceDuck() )
  294. return true;
  295. }
  296. return false;
  297. }
  298. //-----------------------------------------------------------------------------
  299. // Purpose: Do any sample conversion
  300. // For 8 bit PCM, convert to signed because the mixing routine assumes this
  301. // Input : *pData - pointer to sample data
  302. // sampleCount - number of samples
  303. //-----------------------------------------------------------------------------
  304. void CAudioSourceWave::ConvertSamples( char *pData, int sampleCount )
  305. {
  306. if ( m_format == WAVE_FORMAT_PCM )
  307. {
  308. if ( m_bits == 8 )
  309. {
  310. for ( int i = 0; i < sampleCount*m_channels; i++ )
  311. {
  312. *pData = (unsigned char)((int)((unsigned)*pData) - 128);
  313. pData++;
  314. }
  315. }
  316. }
  317. }
  318. //-----------------------------------------------------------------------------
  319. // Purpose: Parse base chunks
  320. // Input : &walk - riff file to parse
  321. // : chunkName - name of the chunk to parse
  322. //-----------------------------------------------------------------------------
  323. // UNDONE: Move parsing loop here and drop each chunk into a virtual function
  324. // instead of this being virtual.
  325. void CAudioSourceWave::ParseChunk( IterateRIFF &walk, int chunkName )
  326. {
  327. switch( chunkName )
  328. {
  329. case WAVE_CUE:
  330. ParseCueChunk( walk );
  331. break;
  332. case WAVE_SAMPLER:
  333. ParseSamplerChunk( walk );
  334. break;
  335. case WAVE_VALVEDATA:
  336. ParseSentence( walk );
  337. break;
  338. default:
  339. // unknown and don't care
  340. ChunkError( walk.ChunkName() );
  341. break;
  342. }
  343. }
  344. bool CAudioSourceWave::IsLooped( void )
  345. {
  346. return (m_loopStart >= 0) ? true : false;
  347. }
  348. bool CAudioSourceWave::IsStereoWav( void )
  349. {
  350. return (m_channels == 2) ? true : false;
  351. }
  352. bool CAudioSourceWave::IsStreaming( void )
  353. {
  354. return false;
  355. }
  356. int CAudioSourceWave::GetCacheStatus( void )
  357. {
  358. return AUDIO_IS_LOADED;
  359. }
  360. void CAudioSourceWave::CacheLoad( void )
  361. {
  362. }
  363. void CAudioSourceWave::CacheUnload( void )
  364. {
  365. }
  366. int CAudioSourceWave::ZeroCrossingBefore( int sample )
  367. {
  368. return sample;
  369. }
  370. int CAudioSourceWave::ZeroCrossingAfter( int sample )
  371. {
  372. return sample;
  373. }
  374. //-----------------------------------------------------------------------------
  375. // Purpose:
  376. // Input : &walk -
  377. //-----------------------------------------------------------------------------
  378. void CAudioSourceWave::ParseSentence( IterateRIFF &walk )
  379. {
  380. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  381. buf.EnsureCapacity( walk.ChunkSize() );
  382. walk.ChunkRead( buf.Base() );
  383. buf.SeekPut( CUtlBuffer::SEEK_HEAD, walk.ChunkSize() );
  384. m_pTempSentence = new CSentence();
  385. Assert( m_pTempSentence );
  386. m_pTempSentence->InitFromDataChunk( buf.Base(), buf.TellPut() );
  387. // Throws all phonemes into one word, discards sentence memory, etc.
  388. m_pTempSentence->MakeRuntimeOnly();
  389. }
  390. //-----------------------------------------------------------------------------
  391. // Purpose:
  392. // Output : CSentence
  393. //-----------------------------------------------------------------------------
  394. CSentence *CAudioSourceWave::GetSentence( void )
  395. {
  396. if ( IsX360() )
  397. {
  398. return m_pTempSentence;
  399. }
  400. // Already checked and this wav doesn't have sentence data...
  401. if ( m_bNoSentence == true )
  402. {
  403. return NULL;
  404. }
  405. // Look up sentence from cache
  406. CAudioSourceCachedInfo *info = m_AudioCacheHandle.FastGet();
  407. if ( !info )
  408. {
  409. info = m_AudioCacheHandle.Get( CAudioSource::AUDIO_SOURCE_WAV, m_pSfx->IsPrecachedSound(), m_pSfx, &m_nCachedDataSize );
  410. }
  411. Assert( info );
  412. if ( !info )
  413. {
  414. m_bNoSentence = true;
  415. return NULL;
  416. }
  417. CSentence *sentence = info->Sentence();
  418. if ( !sentence )
  419. {
  420. m_bNoSentence = true;
  421. return NULL;
  422. }
  423. if ( sentence->m_bIsValid )
  424. {
  425. return sentence;
  426. }
  427. m_bNoSentence = true;
  428. return NULL;
  429. }
  430. const char *CAudioSourceWave::GetName()
  431. {
  432. return m_pSfx ? m_pSfx->getname() : NULL;
  433. }
  434. //-----------------------------------------------------------------------------
  435. // Load a native xaudio or legacy wav
  436. //-----------------------------------------------------------------------------
  437. bool CAudioSourceWave::GetXboxAudioStartupData()
  438. {
  439. CUtlBuffer buf;
  440. char fileName[MAX_PATH];
  441. char tempFileName[MAX_PATH];
  442. MEM_ALLOC_CREDIT();
  443. // try native optimal xma wav file first
  444. Q_StripExtension( m_pSfx->GetFileName(), tempFileName, sizeof( tempFileName ) );
  445. Q_snprintf( fileName, sizeof( fileName ), "sound\\%s.360.wav", tempFileName );
  446. if ( !g_pFullFileSystem->ReadFile( fileName, "GAME", buf, sizeof( xwvHeader_t ) ) )
  447. {
  448. // not found, not supported
  449. return false;
  450. }
  451. else
  452. {
  453. xwvHeader_t* pHeader = (xwvHeader_t *)buf.Base();
  454. if ( pHeader->id != XWV_ID || pHeader->version != XWV_VERSION )
  455. {
  456. return false;
  457. }
  458. if ( pHeader->format == XWV_FORMAT_XMA )
  459. {
  460. m_format = WAVE_FORMAT_XMA;
  461. }
  462. else if ( pHeader->format == XWV_FORMAT_PCM )
  463. {
  464. m_format = WAVE_FORMAT_PCM;
  465. }
  466. else
  467. {
  468. // unknown
  469. return false;
  470. }
  471. m_rate = pHeader->GetSampleRate();
  472. m_channels = pHeader->channels;
  473. m_dataStart = pHeader->dataOffset;
  474. m_dataSize = pHeader->dataSize;
  475. m_loopStart = pHeader->loopStart;
  476. m_loopBlock = pHeader->loopBlock;
  477. m_numLeadingSamples = pHeader->numLeadingSamples;
  478. m_numTrailingSamples = pHeader->numTrailingSamples;
  479. if ( m_format == WAVE_FORMAT_XMA )
  480. {
  481. // xma is compressed blocks, trick to fool system to treat data as bytes, not samples
  482. // unfortunate, but callers must know xma context and provide offsets in samples or bytes
  483. m_bits = 16;
  484. m_sampleSize = 1;
  485. m_sampleCount = m_dataSize;
  486. }
  487. else
  488. {
  489. m_bits = 16;
  490. m_sampleSize = sizeof( short ) * m_channels;
  491. m_sampleCount = m_dataSize / m_sampleSize;
  492. }
  493. // keep true decoded samples because cannot be easily determined
  494. m_numDecodedSamples = pHeader->numDecodedSamples;
  495. m_bNoSentence = true;
  496. CUtlBuffer fileBuffer;
  497. if ( pHeader->staticDataSize )
  498. {
  499. // get optional data
  500. if ( !g_pFullFileSystem->ReadFile( fileName, "GAME", fileBuffer, pHeader->staticDataSize, sizeof( xwvHeader_t ) ) )
  501. {
  502. return false;
  503. }
  504. unsigned char *pData = (unsigned char *)fileBuffer.Base() + sizeof( xwvHeader_t );
  505. if ( pHeader->GetSeekTableSize() )
  506. {
  507. // store off the seek table
  508. m_nHeaderSize = pHeader->GetSeekTableSize();
  509. m_pHeader = new char[m_nHeaderSize];
  510. V_memcpy( m_pHeader, pData, m_nHeaderSize );
  511. // advance past optional seek table
  512. pData += m_nHeaderSize;
  513. }
  514. if ( pHeader->vdatSize )
  515. {
  516. m_pTempSentence = new CSentence();
  517. Assert( m_pTempSentence );
  518. m_bNoSentence = false;
  519. // vdat is precompiled into minimal binary format and possibly compressed
  520. if ( CLZMA::IsCompressed( pData ) )
  521. {
  522. // uncompress binary vdat and restore
  523. CUtlBuffer targetBuffer;
  524. int originalSize = CLZMA::GetActualSize( pData );
  525. targetBuffer.EnsureCapacity( originalSize );
  526. CLZMA::Uncompress( pData, (unsigned char *)targetBuffer.Base() );
  527. targetBuffer.SeekPut( CUtlBuffer::SEEK_HEAD, originalSize );
  528. m_pTempSentence->CacheRestoreFromBuffer( targetBuffer );
  529. }
  530. else
  531. {
  532. m_pTempSentence->CacheRestoreFromBuffer( fileBuffer );
  533. }
  534. }
  535. }
  536. }
  537. return true;
  538. }
  539. //-----------------------------------------------------------------------------
  540. // Purpose: Bastardized construction routine. This is just to avoid complex
  541. // constructor functions so code can be shared more easily by sub-classes
  542. // Input : *pFormatBuffer - RIFF header
  543. // formatSize - header size
  544. // &walk - RIFF file
  545. //-----------------------------------------------------------------------------
  546. void CAudioSourceWave::Setup( const char *pFormatBuffer, int formatSize, IterateRIFF &walk )
  547. {
  548. Init( pFormatBuffer, formatSize );
  549. while ( walk.ChunkAvailable() )
  550. {
  551. ParseChunk( walk, walk.ChunkName() );
  552. walk.ChunkNext();
  553. }
  554. }
  555. bool CAudioSourceWave::GetStartupData( void *dest, int destsize, int& bytesCopied )
  556. {
  557. bytesCopied = 0;
  558. char formatBuffer[1024];
  559. const char *pName = m_pSfx->GetFileName();
  560. InFileRIFF riff( pName, *g_pSndIO );
  561. if ( riff.RIFFName() != RIFF_WAVE )
  562. {
  563. return false;
  564. }
  565. // set up the iterator for the whole file (root RIFF is a chunk)
  566. IterateRIFF walk( riff, riff.RIFFSize() );
  567. int format = 0;
  568. int formatSize = 0;
  569. // This chunk must be first as it contains the wave's format
  570. // break out when we've parsed it
  571. while ( walk.ChunkAvailable() && format == 0 )
  572. {
  573. switch( walk.ChunkName() )
  574. {
  575. case WAVE_FMT:
  576. {
  577. if ( walk.ChunkSize() <= sizeof( formatBuffer ) )
  578. {
  579. walk.ChunkRead( formatBuffer );
  580. formatSize = walk.ChunkSize();
  581. format = ((WAVEFORMATEX *)formatBuffer)->wFormatTag;
  582. if( ((WAVEFORMATEX *)formatBuffer)->wBitsPerSample > 16)
  583. {
  584. Warning("Unsupported %d-bit wave file %s\n", (int)((WAVEFORMATEX *)formatBuffer)->wBitsPerSample, pName);
  585. }
  586. }
  587. }
  588. break;
  589. default:
  590. {
  591. ChunkError( walk.ChunkName() );
  592. }
  593. break;
  594. }
  595. walk.ChunkNext();
  596. }
  597. // Not really a WAVE file or no format chunk, bail
  598. if ( !format )
  599. {
  600. return false;
  601. }
  602. Setup( formatBuffer, formatSize, walk );
  603. if ( !m_dataStart || !m_dataSize )
  604. {
  605. // failed during setup
  606. return false;
  607. }
  608. // requesting precache snippet as leader for streaming startup latency
  609. if ( destsize )
  610. {
  611. int file = g_pSndIO->open( m_pSfx->GetFileName() );
  612. if ( !file )
  613. {
  614. return false;
  615. }
  616. int bytesNeeded = m_channels * ( m_bits >> 3 ) * m_rate * SND_ASYNC_LOOKAHEAD_SECONDS;
  617. // Round to multiple of 4
  618. bytesNeeded = ( bytesNeeded + 3 ) & ~3;
  619. bytesCopied = min( destsize, m_dataSize );
  620. bytesCopied = min( bytesNeeded, bytesCopied );
  621. g_pSndIO->seek( file, m_dataStart );
  622. g_pSndIO->read( dest, bytesCopied, file );
  623. g_pSndIO->close( file );
  624. // some samples need to be converted
  625. ConvertSamples( (char *)dest, ( bytesCopied / m_sampleSize ) );
  626. }
  627. return true;
  628. }
  629. //-----------------------------------------------------------------------------
  630. // Purpose: parses loop information from a cue chunk
  631. // Input : &walk - RIFF iterator
  632. // Output : int loop start position
  633. //-----------------------------------------------------------------------------
  634. void CAudioSourceWave::ParseCueChunk( IterateRIFF &walk )
  635. {
  636. // Cue chunk as specified by RIFF format
  637. // see $/research/jay/sound/riffnew.htm
  638. struct
  639. {
  640. unsigned int dwName;
  641. unsigned int dwPosition;
  642. unsigned int fccChunk;
  643. unsigned int dwChunkStart;
  644. unsigned int dwBlockStart;
  645. unsigned int dwSampleOffset;
  646. } cue_chunk;
  647. int cueCount;
  648. // assume that the cue chunk stored in the wave is the start of the loop
  649. // assume only one cue chunk, UNDONE: Test this assumption here?
  650. cueCount = walk.ChunkReadInt();
  651. if ( cueCount > 0 )
  652. {
  653. walk.ChunkReadPartial( &cue_chunk, sizeof(cue_chunk) );
  654. m_loopStart = LittleLong( cue_chunk.dwSampleOffset );
  655. }
  656. }
  657. //-----------------------------------------------------------------------------
  658. // Purpose: parses loop information from a 'smpl' chunk
  659. // Input : &walk - RIFF iterator
  660. // Output : int loop start position
  661. //-----------------------------------------------------------------------------
  662. void CAudioSourceWave::ParseSamplerChunk( IterateRIFF &walk )
  663. {
  664. // Sampler chunk for MIDI instruments
  665. // Parse loop info from this chunk too
  666. struct SampleLoop
  667. {
  668. unsigned int dwIdentifier;
  669. unsigned int dwType;
  670. unsigned int dwStart;
  671. unsigned int dwEnd;
  672. unsigned int dwFraction;
  673. unsigned int dwPlayCount;
  674. };
  675. struct
  676. {
  677. unsigned int dwManufacturer;
  678. unsigned int dwProduct;
  679. unsigned int dwSamplePeriod;
  680. unsigned int dwMIDIUnityNote;
  681. unsigned int dwMIDIPitchFraction;
  682. unsigned int dwSMPTEFormat;
  683. unsigned int dwSMPTEOffset;
  684. unsigned int cSampleLoops;
  685. unsigned int cbSamplerData;
  686. struct SampleLoop Loops[1];
  687. } samplerChunk;
  688. // assume that the loop end is the sample end
  689. // assume that only the first loop is relevant
  690. walk.ChunkReadPartial( &samplerChunk, sizeof(samplerChunk) );
  691. if ( LittleLong( samplerChunk.cSampleLoops ) > 0 )
  692. {
  693. // only support normal forward loops
  694. if ( LittleLong( samplerChunk.Loops[0].dwType ) == 0 )
  695. {
  696. m_loopStart = LittleLong( samplerChunk.Loops[0].dwStart );
  697. }
  698. #ifdef _DEBUG
  699. else
  700. {
  701. Msg("Unknown sampler chunk type %d on %s\n", LittleLong( samplerChunk.Loops[0].dwType ), m_pSfx->GetFileName() );
  702. }
  703. #endif
  704. }
  705. // else discard - this is some other non-loop sampler data we don't support
  706. }
  707. //-----------------------------------------------------------------------------
  708. // Purpose: get the wave header
  709. //-----------------------------------------------------------------------------
  710. void *CAudioSourceWave::GetHeader( void )
  711. {
  712. return m_pHeader;
  713. }
  714. //-----------------------------------------------------------------------------
  715. // Gets the looping information. Some parameters are interpreted based on format
  716. //-----------------------------------------------------------------------------
  717. int CAudioSourceWave::GetLoopingInfo( int *pLoopBlock, int *pNumLeadingSamples, int *pNumTrailingSamples )
  718. {
  719. if ( pLoopBlock )
  720. {
  721. // for xma, the block that contains the loop point
  722. *pLoopBlock = m_loopBlock;
  723. }
  724. if ( pNumLeadingSamples )
  725. {
  726. // for xma, the number of leading samples at the loop block to discard
  727. *pNumLeadingSamples = m_numLeadingSamples;
  728. }
  729. if ( pNumTrailingSamples )
  730. {
  731. // for xma, the number of trailing samples at the final block to discard
  732. *pNumTrailingSamples = m_numTrailingSamples;
  733. }
  734. // the loop point in samples
  735. return m_loopStart;
  736. }
  737. //-----------------------------------------------------------------------------
  738. // Purpose: wrap the position wrt looping
  739. // Input : samplePosition - absolute position
  740. // Output : int - looped position
  741. //-----------------------------------------------------------------------------
  742. int CAudioSourceWave::ConvertLoopedPosition( int samplePosition )
  743. {
  744. if ( m_format == WAVE_FORMAT_XMA )
  745. {
  746. // xma mixer interprets loops and *always* sends a corrected position
  747. return samplePosition;
  748. }
  749. // if the wave is looping and we're past the end of the sample
  750. // convert to a position within the loop
  751. // At the end of the loop, we return a short buffer, and subsequent call
  752. // will loop back and get the rest of the buffer
  753. if ( m_loopStart >= 0 && samplePosition >= m_sampleCount )
  754. {
  755. // size of loop
  756. int loopSize = m_sampleCount - m_loopStart;
  757. // subtract off starting bit of the wave
  758. samplePosition -= m_loopStart;
  759. if ( loopSize )
  760. {
  761. // "real" position in memory (mod off extra loops)
  762. samplePosition = m_loopStart + (samplePosition % loopSize);
  763. }
  764. // ERROR? if no loopSize
  765. }
  766. return samplePosition;
  767. }
  768. //-----------------------------------------------------------------------------
  769. // Purpose: remove the reference for the mixer getting deleted
  770. // Input : *pMixer -
  771. //-----------------------------------------------------------------------------
  772. void CAudioSourceWave::ReferenceRemove( CAudioMixer *pMixer )
  773. {
  774. m_refCount--;
  775. if ( m_refCount == 0 && ( ( IsPC() && IsPlayOnce() ) || ( IsX360() && IsStreaming() ) ) )
  776. {
  777. SetPlayOnce( false ); // in case it gets used again
  778. CacheUnload();
  779. }
  780. }
  781. //-----------------------------------------------------------------------------
  782. // Purpose: Add a mixer reference
  783. // Input : *pMixer -
  784. //-----------------------------------------------------------------------------
  785. void CAudioSourceWave::ReferenceAdd( CAudioMixer *pMixer )
  786. {
  787. m_refCount++;
  788. }
  789. //-----------------------------------------------------------------------------
  790. // Purpose: return true if no mixers reference this source
  791. //-----------------------------------------------------------------------------
  792. bool CAudioSourceWave::CanDelete( void )
  793. {
  794. if ( m_refCount > 0 )
  795. return false;
  796. return true;
  797. }
  798. // CAudioSourceMemWave is a bunch of wave data that is all in memory.
  799. // To use it:
  800. // - derive from CAudioSourceMemWave
  801. // - call CAudioSourceWave::Init with a WAVEFORMATEX
  802. // - set m_sampleCount.
  803. // - implement GetDataPointer
  804. class CAudioSourceMemWave : public CAudioSourceWave
  805. {
  806. public:
  807. CAudioSourceMemWave();
  808. CAudioSourceMemWave( CSfxTable *pSfx );
  809. CAudioSourceMemWave( CSfxTable *pSfx, CAudioSourceCachedInfo *info );
  810. virtual ~CAudioSourceMemWave();
  811. // These are all implemented by CAudioSourceMemWave.
  812. virtual CAudioMixer* CreateMixer( int initialStreamPosition = 0 );
  813. virtual int GetOutputData( void **pData, int samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] );
  814. virtual int ZeroCrossingBefore( int sample );
  815. virtual int ZeroCrossingAfter( int sample );
  816. virtual int GetCacheStatus( void );
  817. virtual void CacheLoad( void );
  818. virtual void CacheUnload( void );
  819. // by definition, should already be in memory
  820. virtual void Prefetch() {}
  821. virtual void ParseChunk( IterateRIFF &walk, int chunkName );
  822. void ParseDataChunk( IterateRIFF &walk );
  823. protected:
  824. // Whoeover derives must implement this.
  825. virtual char *GetDataPointer( void );
  826. memhandle_t m_hCache;
  827. StreamHandle_t m_hStream;
  828. private:
  829. CAudioSourceMemWave( const CAudioSourceMemWave & ); // not implemented, not accessible
  830. };
  831. CAudioSourceMemWave::CAudioSourceMemWave() :
  832. CAudioSourceWave( NULL )
  833. {
  834. m_hCache = 0;
  835. m_hStream = INVALID_STREAM_HANDLE;
  836. }
  837. CAudioSourceMemWave::CAudioSourceMemWave( CSfxTable *pSfx ) :
  838. CAudioSourceWave( pSfx )
  839. {
  840. m_hCache = 0;
  841. m_hStream = INVALID_STREAM_HANDLE;
  842. if ( IsX360() )
  843. {
  844. bool bValid = GetXboxAudioStartupData();
  845. if ( !bValid )
  846. {
  847. // failed, substitute placeholder
  848. pSfx->m_bUseErrorFilename = true;
  849. bValid = GetXboxAudioStartupData();
  850. if ( bValid )
  851. {
  852. DevWarning( "Failed to load sound \"%s\", substituting \"%s\"\n", pSfx->getname(), pSfx->GetFileName() );
  853. }
  854. }
  855. if ( bValid )
  856. {
  857. // a 360 memory wave is a critical resource kept locked in memory, load its data now
  858. CacheLoad();
  859. }
  860. }
  861. }
  862. CAudioSourceMemWave::CAudioSourceMemWave( CSfxTable *pSfx, CAudioSourceCachedInfo *info ) :
  863. CAudioSourceWave( pSfx, info )
  864. {
  865. m_hCache = 0;
  866. m_hStream = INVALID_STREAM_HANDLE;
  867. }
  868. CAudioSourceMemWave::~CAudioSourceMemWave()
  869. {
  870. }
  871. //-----------------------------------------------------------------------------
  872. // Purpose: Creates a mixer and initializes it with an appropriate mixer
  873. //-----------------------------------------------------------------------------
  874. CAudioMixer *CAudioSourceMemWave::CreateMixer( int initialStreamPosition )
  875. {
  876. CAudioMixer *pMixer = CreateWaveMixer( CreateWaveDataMemory(*this), m_format, m_channels, m_bits, initialStreamPosition );
  877. return pMixer;
  878. }
  879. //-----------------------------------------------------------------------------
  880. // Purpose:
  881. // Input : **pData - output pointer to samples
  882. // samplePosition - position (in samples not bytes)
  883. // sampleCount - number of samples (not bytes)
  884. // Output : int - number of samples available
  885. //-----------------------------------------------------------------------------
  886. int CAudioSourceMemWave::GetOutputData( void **pData, int samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] )
  887. {
  888. // handle position looping
  889. samplePosition = ConvertLoopedPosition( samplePosition );
  890. // how many samples are available (linearly not counting looping)
  891. int totalSampleCount = m_sampleCount - samplePosition;
  892. // may be asking for a sample out of range, clip at zero
  893. if ( totalSampleCount < 0 )
  894. {
  895. totalSampleCount = 0;
  896. }
  897. // clip max output samples to max available
  898. if ( sampleCount > totalSampleCount )
  899. {
  900. sampleCount = totalSampleCount;
  901. }
  902. // byte offset in sample database
  903. samplePosition *= m_sampleSize;
  904. // if we are returning some samples, store the pointer
  905. if ( sampleCount )
  906. {
  907. // Starting past end of "preloaded" data, just use regular cache
  908. if ( samplePosition >= m_nCachedDataSize )
  909. {
  910. *pData = GetDataPointer();
  911. }
  912. else
  913. {
  914. if ( IsPC() || !IsX360() )
  915. {
  916. // Start async loader if we haven't already done so
  917. CacheLoad();
  918. // Return less data if we are about to run out of uncached data
  919. if ( samplePosition + ( sampleCount * m_sampleSize ) >= m_nCachedDataSize )
  920. {
  921. sampleCount = ( m_nCachedDataSize - samplePosition ) / m_sampleSize;
  922. }
  923. // Point at preloaded/cached data from .cache file for now
  924. *pData = GetCachedDataPointer();
  925. }
  926. else
  927. {
  928. // for 360, memory wave data should have already been loaded and locked in cache
  929. Assert( 0 );
  930. }
  931. }
  932. if ( *pData )
  933. {
  934. *pData = (char *)*pData + samplePosition;
  935. }
  936. else
  937. {
  938. // End of data or some other problem
  939. sampleCount = 0;
  940. }
  941. }
  942. return sampleCount;
  943. }
  944. // Hardcoded macros to test for zero crossing
  945. #define ZERO_X_8(b) ((b)<2 && (b)>-2)
  946. #define ZERO_X_16(b) ((b)<512 && (b)>-512)
  947. //-----------------------------------------------------------------------------
  948. // Purpose: Search backward for a zero crossing starting at sample
  949. // Input : sample - starting point
  950. // Output : position of zero crossing
  951. //-----------------------------------------------------------------------------
  952. int CAudioSourceMemWave::ZeroCrossingBefore( int sample )
  953. {
  954. char *pWaveData = GetDataPointer();
  955. if ( m_format == WAVE_FORMAT_PCM )
  956. {
  957. if ( m_bits == 8 )
  958. {
  959. char *pData = pWaveData + sample * m_sampleSize;
  960. bool zero = false;
  961. if ( m_channels == 1 )
  962. {
  963. while ( sample > 0 && !zero )
  964. {
  965. if ( ZERO_X_8(*pData) )
  966. zero = true;
  967. else
  968. {
  969. sample--;
  970. pData--;
  971. }
  972. }
  973. }
  974. else
  975. {
  976. while ( sample > 0 && !zero )
  977. {
  978. if ( ZERO_X_8(*pData) && ZERO_X_8(pData[1]) )
  979. zero = true;
  980. else
  981. {
  982. sample--;
  983. pData--;
  984. }
  985. }
  986. }
  987. }
  988. else
  989. {
  990. short *pData = (short *)(pWaveData + sample * m_sampleSize);
  991. bool zero = false;
  992. if ( m_channels == 1 )
  993. {
  994. while ( sample > 0 && !zero )
  995. {
  996. if ( ZERO_X_16(*pData) )
  997. zero = true;
  998. else
  999. {
  1000. pData--;
  1001. sample--;
  1002. }
  1003. }
  1004. }
  1005. else
  1006. {
  1007. while ( sample > 0 && !zero )
  1008. {
  1009. if ( ZERO_X_16(*pData) && ZERO_X_16(pData[1]) )
  1010. zero = true;
  1011. else
  1012. {
  1013. sample--;
  1014. pData--;
  1015. }
  1016. }
  1017. }
  1018. }
  1019. }
  1020. return sample;
  1021. }
  1022. //-----------------------------------------------------------------------------
  1023. // Purpose: Search forward for a zero crossing
  1024. // Input : sample - starting point
  1025. // Output : position of found zero crossing
  1026. //-----------------------------------------------------------------------------
  1027. int CAudioSourceMemWave::ZeroCrossingAfter( int sample )
  1028. {
  1029. char *pWaveData = GetDataPointer();
  1030. if ( m_format == WAVE_FORMAT_PCM )
  1031. {
  1032. if ( m_bits == 8 )
  1033. {
  1034. char *pData = pWaveData + sample * m_sampleSize;
  1035. bool zero = false;
  1036. if ( m_channels == 1 )
  1037. {
  1038. while ( sample < SampleCount() && !zero )
  1039. {
  1040. if ( ZERO_X_8(*pData) )
  1041. zero = true;
  1042. else
  1043. {
  1044. sample++;
  1045. pData++;
  1046. }
  1047. }
  1048. }
  1049. else
  1050. {
  1051. while ( sample < SampleCount() && !zero )
  1052. {
  1053. if ( ZERO_X_8(*pData) && ZERO_X_8(pData[1]) )
  1054. zero = true;
  1055. else
  1056. {
  1057. sample++;
  1058. pData++;
  1059. }
  1060. }
  1061. }
  1062. }
  1063. else
  1064. {
  1065. short *pData = (short *)(pWaveData + sample * m_sampleSize);
  1066. bool zero = false;
  1067. if ( m_channels == 1 )
  1068. {
  1069. while ( sample > 0 && !zero )
  1070. {
  1071. if ( ZERO_X_16(*pData) )
  1072. zero = true;
  1073. else
  1074. {
  1075. pData++;
  1076. sample++;
  1077. }
  1078. }
  1079. }
  1080. else
  1081. {
  1082. while ( sample > 0 && !zero )
  1083. {
  1084. if ( ZERO_X_16(*pData) && ZERO_X_16(pData[1]) )
  1085. zero = true;
  1086. else
  1087. {
  1088. sample++;
  1089. pData++;
  1090. }
  1091. }
  1092. }
  1093. }
  1094. }
  1095. return sample;
  1096. }
  1097. //-----------------------------------------------------------------------------
  1098. // Purpose: parse chunks with unique processing to in-memory waves
  1099. // Input : &walk - RIFF file
  1100. //-----------------------------------------------------------------------------
  1101. void CAudioSourceMemWave::ParseChunk( IterateRIFF &walk, int chunkName )
  1102. {
  1103. switch( chunkName )
  1104. {
  1105. // this is the audio data
  1106. case WAVE_DATA:
  1107. ParseDataChunk( walk );
  1108. return;
  1109. }
  1110. CAudioSourceWave::ParseChunk( walk, chunkName );
  1111. }
  1112. //-----------------------------------------------------------------------------
  1113. // Purpose: reads the actual sample data and parses it
  1114. // Input : &walk - RIFF file
  1115. //-----------------------------------------------------------------------------
  1116. void CAudioSourceMemWave::ParseDataChunk( IterateRIFF &walk )
  1117. {
  1118. m_dataStart = walk.ChunkFilePosition() + 8;
  1119. m_dataSize = walk.ChunkSize();
  1120. // 360 streaming model loads data later, but still needs critical member setup
  1121. char *pData = NULL;
  1122. if ( IsPC() || !IsX360() )
  1123. {
  1124. pData = GetDataPointer();
  1125. if ( !pData )
  1126. {
  1127. Error( "CAudioSourceMemWave (%s): GetDataPointer() failed.", m_pSfx ? m_pSfx->GetFileName() : "m_pSfx = NULL" );
  1128. }
  1129. // load them into memory (bad!!, this is a duplicate read of the data chunk)
  1130. walk.ChunkRead( pData );
  1131. }
  1132. if ( m_format == WAVE_FORMAT_PCM )
  1133. {
  1134. // number of samples loaded
  1135. m_sampleCount = m_dataSize / m_sampleSize;
  1136. m_numDecodedSamples = m_sampleCount;
  1137. }
  1138. else if ( m_format == WAVE_FORMAT_ADPCM )
  1139. {
  1140. // The ADPCM mixers treat the wave source as a flat file of bytes.
  1141. // Since each "sample" is a byte (this is a flat file), the number of samples is the file size
  1142. m_sampleCount = m_dataSize;
  1143. m_sampleSize = 1;
  1144. // file says 4, output is 16
  1145. m_bits = 16;
  1146. m_numDecodedSamples = ADPCMSampleCount( (ADPCMWAVEFORMAT *)m_pHeader, m_dataSize );
  1147. }
  1148. // some samples need to be converted
  1149. if ( pData )
  1150. {
  1151. ConvertSamples( pData, m_sampleCount );
  1152. }
  1153. }
  1154. //-----------------------------------------------------------------------------
  1155. // Purpose:
  1156. // Output : Returns true on success, false on failure.
  1157. //-----------------------------------------------------------------------------
  1158. int CAudioSourceMemWave::GetCacheStatus( void )
  1159. {
  1160. VPROF("CAudioSourceMemWave::GetCacheStatus");
  1161. if ( IsPC() || !IsX360() )
  1162. {
  1163. // NOTE: This will start the load if it isn't started
  1164. bool bCacheValid;
  1165. bool bCompleted = wavedatacache->IsDataLoadCompleted( m_hCache, &bCacheValid );
  1166. if ( !bCacheValid )
  1167. {
  1168. wavedatacache->RestartDataLoad( &m_hCache, m_pSfx->GetFileName(), m_dataSize, m_dataStart );
  1169. }
  1170. if ( bCompleted )
  1171. return AUDIO_IS_LOADED;
  1172. if ( wavedatacache->IsDataLoadInProgress( m_hCache ) )
  1173. return AUDIO_LOADING;
  1174. }
  1175. else
  1176. {
  1177. return wavedatacache->IsStreamedDataReady( m_hStream ) ? AUDIO_IS_LOADED : AUDIO_NOT_LOADED;
  1178. }
  1179. return AUDIO_NOT_LOADED;
  1180. }
  1181. //-----------------------------------------------------------------------------
  1182. // Purpose:
  1183. //-----------------------------------------------------------------------------
  1184. void CAudioSourceMemWave::CacheLoad( void )
  1185. {
  1186. if ( IsPC() || !IsX360() )
  1187. {
  1188. // Commence lazy load?
  1189. if ( m_hCache != 0 )
  1190. {
  1191. bool bCacheValid;
  1192. wavedatacache->IsDataLoadCompleted( m_hCache, &bCacheValid );
  1193. if ( !bCacheValid )
  1194. {
  1195. wavedatacache->RestartDataLoad( &m_hCache, m_pSfx->GetFileName(), m_dataSize, m_dataStart );
  1196. }
  1197. return;
  1198. }
  1199. m_hCache = wavedatacache->AsyncLoadCache( m_pSfx->GetFileName(), m_dataSize, m_dataStart );
  1200. }
  1201. else
  1202. {
  1203. if ( m_hStream == INVALID_STREAM_HANDLE )
  1204. {
  1205. // memory wave is resident
  1206. const char *pFilename = m_pSfx->GetFileName();
  1207. streamFlags_t streamFlags = STREAMED_FROMDVD;
  1208. char szFilename[MAX_PATH];
  1209. if ( m_format == WAVE_FORMAT_XMA || m_format == WAVE_FORMAT_PCM )
  1210. {
  1211. V_strcpy_safe( szFilename, pFilename );
  1212. V_SetExtension( szFilename, ".360.wav", sizeof( szFilename ) );
  1213. pFilename = szFilename;
  1214. // memory resident xma waves use the queued loader
  1215. // restricting to XMA due to not correctly running a post ConvertSamples, which is not an issue for XMA
  1216. if ( g_pQueuedLoader->IsMapLoading() )
  1217. {
  1218. // hint the wave data cache
  1219. streamFlags |= STREAMED_QUEUEDLOAD;
  1220. }
  1221. }
  1222. // open stream to load as a single monolithic buffer
  1223. m_hStream = wavedatacache->OpenStreamedLoad( pFilename, m_dataSize, m_dataStart, 0, -1, m_dataSize, 1, streamFlags );
  1224. if ( m_hStream != INVALID_STREAM_HANDLE && !( streamFlags & STREAMED_QUEUEDLOAD ) )
  1225. {
  1226. // block and finish load, convert data once right now
  1227. char *pWaveData = (char *)wavedatacache->GetStreamedDataPointer( m_hStream, true );
  1228. if ( pWaveData )
  1229. {
  1230. ConvertSamples( pWaveData, m_dataSize/m_sampleSize );
  1231. }
  1232. }
  1233. }
  1234. }
  1235. }
  1236. //-----------------------------------------------------------------------------
  1237. // Purpose:
  1238. //-----------------------------------------------------------------------------
  1239. void CAudioSourceMemWave::CacheUnload( void )
  1240. {
  1241. if ( IsPC() || !IsX360() )
  1242. {
  1243. if ( m_hCache != 0 )
  1244. {
  1245. wavedatacache->Unload( m_hCache );
  1246. }
  1247. }
  1248. else
  1249. {
  1250. if ( m_hStream != INVALID_STREAM_HANDLE )
  1251. {
  1252. wavedatacache->CloseStreamedLoad( m_hStream );
  1253. m_hStream = INVALID_STREAM_HANDLE;
  1254. }
  1255. }
  1256. }
  1257. //-----------------------------------------------------------------------------
  1258. // Purpose:
  1259. // Output : char
  1260. //-----------------------------------------------------------------------------
  1261. char *CAudioSourceMemWave::GetDataPointer( void )
  1262. {
  1263. char *pWaveData = NULL;
  1264. if ( IsPC() || !IsX360() )
  1265. {
  1266. bool bSamplesConverted = false;
  1267. if ( m_hCache == 0 )
  1268. {
  1269. // not in cache, start loading
  1270. CacheLoad();
  1271. }
  1272. // mount the requested data, blocks if necessary
  1273. wavedatacache->GetDataPointer(
  1274. m_hCache,
  1275. m_pSfx->GetFileName(),
  1276. m_dataSize,
  1277. m_dataStart,
  1278. (void **)&pWaveData,
  1279. 0,
  1280. &bSamplesConverted );
  1281. // If we have reloaded data from disk (async) and we haven't converted the samples yet, do it now
  1282. // FIXME: Is this correct for stereo wavs?
  1283. if ( pWaveData && !bSamplesConverted )
  1284. {
  1285. ConvertSamples( pWaveData, m_dataSize/m_sampleSize );
  1286. wavedatacache->SetPostProcessed( m_hCache, true );
  1287. }
  1288. }
  1289. else
  1290. {
  1291. if ( m_hStream != INVALID_STREAM_HANDLE )
  1292. {
  1293. // expected to be valid, unless failure during setup
  1294. pWaveData = (char *)wavedatacache->GetStreamedDataPointer( m_hStream, true );
  1295. }
  1296. }
  1297. return pWaveData;
  1298. }
  1299. //-----------------------------------------------------------------------------
  1300. // Purpose: Wave source for streaming wave files
  1301. // UNDONE: Handle looping
  1302. //-----------------------------------------------------------------------------
  1303. class CAudioSourceStreamWave : public CAudioSourceWave, public IWaveStreamSource
  1304. {
  1305. public:
  1306. CAudioSourceStreamWave( CSfxTable *pSfx );
  1307. CAudioSourceStreamWave( CSfxTable *pSfx, CAudioSourceCachedInfo *info );
  1308. ~CAudioSourceStreamWave();
  1309. CAudioMixer *CreateMixer( int initialStreamPosition = 0 );
  1310. int GetOutputData( void **pData, int samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] );
  1311. void ParseChunk( IterateRIFF &walk, int chunkName );
  1312. bool IsStreaming( void ) { return true; }
  1313. virtual int GetCacheStatus( void );
  1314. // IWaveStreamSource
  1315. virtual int UpdateLoopingSamplePosition( int samplePosition )
  1316. {
  1317. return ConvertLoopedPosition( samplePosition );
  1318. }
  1319. virtual void UpdateSamples( char *pData, int sampleCount )
  1320. {
  1321. ConvertSamples( pData, sampleCount );
  1322. }
  1323. virtual int GetLoopingInfo( int *pLoopBlock, int *pNumLeadingSamples, int *pNumTrailingSamples )
  1324. {
  1325. return CAudioSourceWave::GetLoopingInfo( pLoopBlock, pNumLeadingSamples, pNumTrailingSamples );
  1326. }
  1327. virtual void Prefetch();
  1328. virtual int SampleToStreamPosition( int samplePosition );
  1329. virtual int StreamToSamplePosition( int streamPosition );
  1330. private:
  1331. CAudioSourceStreamWave( const CAudioSourceStreamWave & ); // not implemented, not accessible
  1332. };
  1333. //-----------------------------------------------------------------------------
  1334. // Purpose: Save a copy of the file name for instances to open later
  1335. // Input : *pFileName - filename
  1336. //-----------------------------------------------------------------------------
  1337. CAudioSourceStreamWave::CAudioSourceStreamWave( CSfxTable *pSfx ) : CAudioSourceWave( pSfx )
  1338. {
  1339. m_pSfx = pSfx;
  1340. m_dataStart = -1;
  1341. m_dataSize = 0;
  1342. m_sampleCount = 0;
  1343. if ( IsX360() )
  1344. {
  1345. bool bValid = GetXboxAudioStartupData();
  1346. if ( !bValid )
  1347. {
  1348. // failed, substitute placeholder
  1349. pSfx->m_bUseErrorFilename = true;
  1350. bValid = GetXboxAudioStartupData();
  1351. if ( bValid )
  1352. {
  1353. DevWarning( "Failed to load sound \"%s\", substituting \"%s\"\n", pSfx->getname(), pSfx->GetFileName() );
  1354. }
  1355. }
  1356. }
  1357. }
  1358. CAudioSourceStreamWave::CAudioSourceStreamWave( CSfxTable *pSfx, CAudioSourceCachedInfo *info ) :
  1359. CAudioSourceWave( pSfx, info )
  1360. {
  1361. m_pSfx = pSfx;
  1362. m_dataStart = info->DataStart();
  1363. m_dataSize = info->DataSize();
  1364. m_sampleCount = info->SampleCount();
  1365. }
  1366. //-----------------------------------------------------------------------------
  1367. // Purpose: free the filename buffer
  1368. //-----------------------------------------------------------------------------
  1369. CAudioSourceStreamWave::~CAudioSourceStreamWave( void )
  1370. {
  1371. }
  1372. //-----------------------------------------------------------------------------
  1373. // Purpose: Create an instance (mixer & wavedata) of this sound
  1374. // Output : CAudioMixer * - pointer to the mixer
  1375. //-----------------------------------------------------------------------------
  1376. CAudioMixer *CAudioSourceStreamWave::CreateMixer( int initialStreamPosition )
  1377. {
  1378. char fileName[MAX_PATH];
  1379. const char *pFileName = m_pSfx->GetFileName();
  1380. if ( IsX360() && ( m_format == WAVE_FORMAT_XMA || m_format == WAVE_FORMAT_PCM ) )
  1381. {
  1382. V_strcpy_safe( fileName, pFileName );
  1383. V_SetExtension( fileName, ".360.wav", sizeof( fileName ) );
  1384. pFileName = fileName;
  1385. // for safety, validate the initial stream position
  1386. // not trusting save/load
  1387. if ( m_format == WAVE_FORMAT_XMA )
  1388. {
  1389. if ( ( initialStreamPosition % XBOX_DVD_SECTORSIZE ) ||
  1390. ( initialStreamPosition % XMA_BLOCK_SIZE ) ||
  1391. ( initialStreamPosition >= m_dataSize ) )
  1392. {
  1393. initialStreamPosition = 0;
  1394. }
  1395. }
  1396. }
  1397. // BUGBUG: Source constructs the IWaveData, mixer frees it, fix this?
  1398. IWaveData *pWaveData = CreateWaveDataStream( *this, static_cast<IWaveStreamSource *>(this), pFileName, m_dataStart, m_dataSize, m_pSfx, initialStreamPosition );
  1399. if ( pWaveData )
  1400. {
  1401. CAudioMixer *pMixer = CreateWaveMixer( pWaveData, m_format, m_channels, m_bits, initialStreamPosition );
  1402. if ( pMixer )
  1403. {
  1404. return pMixer;
  1405. }
  1406. // no mixer, delete the stream buffer/instance
  1407. delete pWaveData;
  1408. }
  1409. return NULL;
  1410. }
  1411. void CAudioSourceStreamWave::Prefetch()
  1412. {
  1413. PrefetchDataStream( m_pSfx->GetFileName(), m_dataStart, m_dataSize );
  1414. }
  1415. //-----------------------------------------------------------------------------
  1416. //-----------------------------------------------------------------------------
  1417. int CAudioSourceStreamWave::SampleToStreamPosition( int samplePosition )
  1418. {
  1419. if ( IsPC() )
  1420. {
  1421. // not for PC
  1422. Assert( 0 );
  1423. return 0;
  1424. }
  1425. if ( m_format != WAVE_FORMAT_XMA || !m_nHeaderSize )
  1426. {
  1427. // not in the expected format or lacking the seek table
  1428. return 0;
  1429. }
  1430. // Run through the seek table to find the block closest to the desired sample.
  1431. // Each seek table entry is the index (counting from the beginning of the file)
  1432. // of the first sample in the corresponding block, but there's no entry for the
  1433. // first block (since the index would always be zero).
  1434. int *pSeekTable = (int*)m_pHeader;
  1435. int packet = 0;
  1436. for ( int i = 0; i < m_nHeaderSize/(int)sizeof( int ); ++i )
  1437. {
  1438. if ( samplePosition < pSeekTable[i] )
  1439. {
  1440. packet = i;
  1441. break;
  1442. }
  1443. }
  1444. int streamPosition = ( packet == 0 ) ? 0 : ( packet - 1 ) * 2048;
  1445. return streamPosition;
  1446. }
  1447. //-----------------------------------------------------------------------------
  1448. //-----------------------------------------------------------------------------
  1449. int CAudioSourceStreamWave::StreamToSamplePosition( int streamPosition )
  1450. {
  1451. if ( IsPC() )
  1452. {
  1453. // not for PC
  1454. Assert( 0 );
  1455. return 0;
  1456. }
  1457. if ( m_format != WAVE_FORMAT_XMA || !m_nHeaderSize )
  1458. {
  1459. // not in the expected format or lacking the seek table
  1460. return 0;
  1461. }
  1462. int packet = streamPosition/2048;
  1463. if ( packet <= 0 )
  1464. {
  1465. return 0;
  1466. }
  1467. if ( packet > m_nHeaderSize/(int)sizeof( int ) )
  1468. {
  1469. return m_numDecodedSamples;
  1470. }
  1471. return ((int*)m_pHeader)[packet - 1];
  1472. }
  1473. //-----------------------------------------------------------------------------
  1474. // Purpose: Parse a stream wave file chunk
  1475. // unlike the in-memory file, don't load the data, just get a reference to it.
  1476. // Input : &walk - RIFF file
  1477. //-----------------------------------------------------------------------------
  1478. void CAudioSourceStreamWave::ParseChunk( IterateRIFF &walk, int chunkName )
  1479. {
  1480. // NOTE: It would be nice to break out of parsing once we have the data start and
  1481. // save seeking over the whole file. But to do so, the other needed chunks must occur
  1482. // before the DATA chunk. But, that is not standard and breaks most other wav parsers.
  1483. switch( chunkName )
  1484. {
  1485. case WAVE_DATA:
  1486. // data starts at chunk + 8 (chunk name, chunk size = 2*4=8 bytes)
  1487. // don't load the data, just know where it is so each instance
  1488. // can load it later
  1489. m_dataStart = walk.ChunkFilePosition() + 8;
  1490. m_dataSize = walk.ChunkSize();
  1491. m_sampleCount = m_dataSize / m_sampleSize;
  1492. return;
  1493. }
  1494. CAudioSourceWave::ParseChunk( walk, chunkName );
  1495. }
  1496. //-----------------------------------------------------------------------------
  1497. // Purpose: This is not implemented here. This source has no data. It is the
  1498. // WaveData's responsibility to load/serve the data
  1499. //-----------------------------------------------------------------------------
  1500. int CAudioSourceStreamWave::GetOutputData( void **pData, int samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] )
  1501. {
  1502. return 0;
  1503. }
  1504. int CAudioSourceStreamWave::GetCacheStatus( void )
  1505. {
  1506. if ( !m_dataSize || !m_dataStart )
  1507. {
  1508. // didn't get precached properly
  1509. return AUDIO_NOT_LOADED;
  1510. }
  1511. return AUDIO_IS_LOADED;
  1512. }
  1513. //-----------------------------------------------------------------------------
  1514. // Purpose: Create a wave audio source (streaming or in memory)
  1515. // Input : *pName - file name (NOTE: CAUDIOSOURCE KEEPS A POINTER TO pSfx)
  1516. // streaming - if true, don't load, stream each instance
  1517. // Output : CAudioSource * - a new source
  1518. //-----------------------------------------------------------------------------
  1519. CAudioSource *CreateWave( CSfxTable *pSfx, bool bStreaming )
  1520. {
  1521. Assert( pSfx );
  1522. #if defined( _DEBUG )
  1523. // For some reason you can't usually do pSfx->getname() in the dev studio debugger, so for convenience we'll grab the name
  1524. // here in debug builds at least...
  1525. char const *pName = pSfx->getname();
  1526. NOTE_UNUSED( pName );
  1527. #endif
  1528. CAudioSourceWave *pWave = NULL;
  1529. if ( IsPC() || !IsX360() )
  1530. {
  1531. // Caching should always work, so if we failed to cache, it's a problem reading the file data, etc.
  1532. bool bIsMapSound = pSfx->IsPrecachedSound();
  1533. CAudioSourceCachedInfo *pInfo = audiosourcecache->GetInfo( CAudioSource::AUDIO_SOURCE_WAV, bIsMapSound, pSfx );
  1534. if ( pInfo && pInfo->Type() != CAudioSource::AUDIO_SOURCE_UNK )
  1535. {
  1536. // create the source from this file
  1537. if ( bStreaming )
  1538. {
  1539. pWave = new CAudioSourceStreamWave( pSfx, pInfo );
  1540. }
  1541. else
  1542. {
  1543. pWave = new CAudioSourceMemWave( pSfx, pInfo );
  1544. }
  1545. }
  1546. }
  1547. else
  1548. {
  1549. // 360 does not use audio cache system
  1550. // create the desired type
  1551. if ( bStreaming )
  1552. {
  1553. pWave = new CAudioSourceStreamWave( pSfx );
  1554. }
  1555. else
  1556. {
  1557. pWave = new CAudioSourceMemWave( pSfx );
  1558. }
  1559. }
  1560. if ( pWave && !pWave->Format() )
  1561. {
  1562. // lack of format indicates failure
  1563. delete pWave;
  1564. pWave = NULL;
  1565. }
  1566. return pWave;
  1567. }
  1568. //-----------------------------------------------------------------------------
  1569. // Purpose: Wrapper for CreateWave()
  1570. //-----------------------------------------------------------------------------
  1571. CAudioSource *Audio_CreateStreamedWave( CSfxTable *pSfx )
  1572. {
  1573. #if defined( MP3_SUPPORT )
  1574. if ( Audio_IsMP3( pSfx->GetFileName() ) )
  1575. {
  1576. return Audio_CreateStreamedMP3( pSfx );
  1577. }
  1578. #endif
  1579. return CreateWave( pSfx, true );
  1580. }
  1581. //-----------------------------------------------------------------------------
  1582. // Purpose: Wrapper for CreateWave()
  1583. //-----------------------------------------------------------------------------
  1584. CAudioSource *Audio_CreateMemoryWave( CSfxTable *pSfx )
  1585. {
  1586. #if defined( MP3_SUPPORT )
  1587. if ( Audio_IsMP3( pSfx->GetFileName() ) )
  1588. {
  1589. return Audio_CreateMemoryMP3( pSfx );
  1590. }
  1591. #endif
  1592. return CreateWave( pSfx, false );
  1593. }
  1594. float GetMP3Duration_Helper( char const *filename );
  1595. static float Audio_GetMP3Duration( char const *pName )
  1596. {
  1597. // Deduce from file
  1598. return GetMP3Duration_Helper( pName );
  1599. }
  1600. void MaybeReportMissingWav( char const *wav )
  1601. {
  1602. static CUtlSymbolTable wavErrors;
  1603. CUtlSymbol sym;
  1604. sym = wavErrors.Find( wav );
  1605. if ( UTL_INVAL_SYMBOL == sym )
  1606. {
  1607. // See if file exists
  1608. if ( g_pFullFileSystem->FileExists( wav ) )
  1609. {
  1610. DevWarning( "Bad Audio file '%s'\n", wav );
  1611. }
  1612. else
  1613. {
  1614. DevWarning( "Missing wav file '%s'\n", wav );
  1615. }
  1616. wavErrors.AddString( wav );
  1617. }
  1618. }
  1619. static float Audio_GetWaveDuration( char const *pName )
  1620. {
  1621. if ( IsX360() )
  1622. {
  1623. // should have precached
  1624. return 0;
  1625. }
  1626. char formatBuffer[1024];
  1627. WAVEFORMATEX *pfmt = (WAVEFORMATEX *)formatBuffer;
  1628. InFileRIFF riff( pName, *g_pSndIO );
  1629. if ( riff.RIFFName() != RIFF_WAVE )
  1630. {
  1631. MaybeReportMissingWav( pName );
  1632. return 0.0f;
  1633. }
  1634. // set up the iterator for the whole file (root RIFF is a chunk)
  1635. IterateRIFF walk( riff, riff.RIFFSize() );
  1636. int format = 0;
  1637. int formatSize = 0;
  1638. int sampleCount = 0;
  1639. // This chunk must be first as it contains the wave's format
  1640. // break out when we've parsed it
  1641. while ( walk.ChunkAvailable() && ( format == 0 || sampleCount == 0 ) )
  1642. {
  1643. switch( walk.ChunkName() )
  1644. {
  1645. case WAVE_FMT:
  1646. if ( walk.ChunkSize() <= sizeof( formatBuffer ) )
  1647. {
  1648. walk.ChunkRead( formatBuffer );
  1649. formatSize = walk.ChunkSize();
  1650. format = LittleWord( pfmt->wFormatTag );
  1651. }
  1652. break;
  1653. case WAVE_DATA:
  1654. if ( format != 0 )
  1655. {
  1656. int dataSize = walk.ChunkSize();
  1657. if ( format == WAVE_FORMAT_ADPCM )
  1658. {
  1659. // Dummy size for now
  1660. sampleCount = dataSize;
  1661. }
  1662. else
  1663. {
  1664. sampleCount = dataSize / ( LittleWord( pfmt->wBitsPerSample ) >> 3 );
  1665. }
  1666. }
  1667. break;
  1668. default:
  1669. ChunkError( walk.ChunkName() );
  1670. break;
  1671. }
  1672. walk.ChunkNext();
  1673. }
  1674. // Not really a WAVE file or no format chunk, bail
  1675. if ( !format || !sampleCount )
  1676. return 0.0f;
  1677. float sampleRate = LittleDWord( pfmt->nSamplesPerSec );
  1678. if ( format == WAVE_FORMAT_ADPCM )
  1679. {
  1680. // Determine actual duration
  1681. sampleCount = ADPCMSampleCount( (ADPCMWAVEFORMAT *)formatBuffer, sampleCount );
  1682. }
  1683. return (float)sampleCount / sampleRate;
  1684. }
  1685. //-----------------------------------------------------------------------------
  1686. // Purpose: Fast method for determining duration of .wav/.mp3, exposed to server as well
  1687. // Input : *pName -
  1688. // Output : float
  1689. //-----------------------------------------------------------------------------
  1690. float AudioSource_GetSoundDuration( char const *pName )
  1691. {
  1692. #if defined( MP3_SUPPORT )
  1693. if ( Audio_IsMP3( pName ) )
  1694. {
  1695. return Audio_GetMP3Duration( pName );
  1696. }
  1697. #endif
  1698. CSfxTable *pSound = S_PrecacheSound( pName );
  1699. if ( pSound )
  1700. {
  1701. return AudioSource_GetSoundDuration( pSound );
  1702. }
  1703. return Audio_GetWaveDuration( pName );
  1704. }
  1705. float AudioSource_GetSoundDuration( CSfxTable *pSfx )
  1706. {
  1707. if ( pSfx && pSfx->pSource )
  1708. {
  1709. return (float)pSfx->pSource->SampleCount() / (float)pSfx->pSource->SampleRate();
  1710. }
  1711. return 0;
  1712. }
  1713. CAudioSourceCachedInfo::CAudioSourceCachedInfo() :
  1714. infolong( 0 ),
  1715. flagsbyte( 0 ),
  1716. m_dataStart( 0 ),
  1717. m_dataSize( 0 ),
  1718. m_loopStart( 0 ),
  1719. m_sampleCount( 0 ),
  1720. m_usCachedDataSize( 0 ),
  1721. m_pCachedData( 0 ),
  1722. m_usHeaderSize( 0 ),
  1723. m_pHeader( 0 ),
  1724. m_pSentence( 0 )
  1725. {
  1726. }
  1727. CAudioSourceCachedInfo& CAudioSourceCachedInfo::operator =( const CAudioSourceCachedInfo& src )
  1728. {
  1729. if ( this == &src )
  1730. return *this;
  1731. infolong = src.infolong;
  1732. flagsbyte = src.flagsbyte;
  1733. SetDataStart( src.DataStart() );
  1734. SetDataSize( src.DataSize() );
  1735. SetLoopStart( src.LoopStart() );
  1736. SetSampleCount( src.SampleCount() );
  1737. CSentence *scopy = NULL;
  1738. if ( src.Sentence() )
  1739. {
  1740. scopy = new CSentence();
  1741. *scopy = *src.Sentence();
  1742. }
  1743. SetSentence( scopy );
  1744. byte *data = NULL;
  1745. Assert( src.CachedDataSize() == 0 || src.CachedData() );
  1746. m_usCachedDataSize = 0;
  1747. if ( src.CachedData() && src.CachedDataSize() > 0 )
  1748. {
  1749. SetCachedDataSize( src.CachedDataSize() );
  1750. data = new byte[ src.CachedDataSize() ];
  1751. Assert( data );
  1752. Q_memcpy( data, src.CachedData(), src.CachedDataSize() );
  1753. }
  1754. SetCachedData( data );
  1755. data = NULL;
  1756. Assert( src.HeaderSize() == 0 || src.HeaderData() );
  1757. m_usHeaderSize = 0;
  1758. if ( src.HeaderData() && src.HeaderSize() > 0 )
  1759. {
  1760. SetHeaderSize( src.HeaderSize() );
  1761. data = new byte[ src.HeaderSize() ];
  1762. Assert( data );
  1763. Q_memcpy( data, src.HeaderData(), src.HeaderSize() );
  1764. }
  1765. SetHeaderData( data );
  1766. return *this;
  1767. }
  1768. CAudioSourceCachedInfo::CAudioSourceCachedInfo( const CAudioSourceCachedInfo& src )
  1769. {
  1770. if ( this == &src )
  1771. {
  1772. Assert( 0 );
  1773. return;
  1774. }
  1775. infolong = src.infolong;
  1776. flagsbyte = src.flagsbyte;
  1777. SetDataStart( src.DataStart() );
  1778. SetDataSize( src.DataSize() );
  1779. SetLoopStart( src.LoopStart() );
  1780. SetSampleCount( src.SampleCount() );
  1781. CSentence *scopy = NULL;
  1782. if ( src.Sentence() )
  1783. {
  1784. scopy = new CSentence();
  1785. *scopy = *src.Sentence();
  1786. }
  1787. SetSentence( scopy );
  1788. byte *data = NULL;
  1789. Assert( src.CachedDataSize() == 0 || src.CachedData() );
  1790. m_usCachedDataSize = 0;
  1791. if ( src.CachedData() && src.CachedDataSize() > 0 )
  1792. {
  1793. SetCachedDataSize( src.CachedDataSize() );
  1794. data = new byte[ src.CachedDataSize() ];
  1795. Assert( data );
  1796. Q_memcpy( data, src.CachedData(), src.CachedDataSize() );
  1797. }
  1798. SetCachedData( data );
  1799. data = NULL;
  1800. Assert( src.HeaderSize() == 0 || src.HeaderData() );
  1801. m_usHeaderSize = 0;
  1802. if ( src.HeaderData() && src.HeaderSize() > 0 )
  1803. {
  1804. SetHeaderSize( src.HeaderSize() );
  1805. data = new byte[ src.HeaderSize() ];
  1806. Assert( data );
  1807. Q_memcpy( data, src.HeaderData(), src.HeaderSize() );
  1808. }
  1809. SetHeaderData( data );
  1810. }
  1811. CAudioSourceCachedInfo::~CAudioSourceCachedInfo()
  1812. {
  1813. Clear();
  1814. }
  1815. void CAudioSourceCachedInfo::Clear()
  1816. {
  1817. infolong = 0;
  1818. flagsbyte = 0;
  1819. m_dataStart = 0;
  1820. m_dataSize = 0;
  1821. m_loopStart = 0;
  1822. m_sampleCount = 0;
  1823. delete m_pSentence;
  1824. m_pSentence = NULL;
  1825. delete[] m_pCachedData;
  1826. m_pCachedData = NULL;
  1827. m_usCachedDataSize = 0;
  1828. delete[] m_pHeader;
  1829. m_pHeader = NULL;
  1830. m_usHeaderSize = 0;
  1831. }
  1832. void CAudioSourceCachedInfo::RemoveData()
  1833. {
  1834. delete[] m_pCachedData;
  1835. m_pCachedData = NULL;
  1836. m_usCachedDataSize = 0;
  1837. flags.m_bCachedData = false;
  1838. }
  1839. void CAudioSourceCachedInfo::Save( CUtlBuffer& buf )
  1840. {
  1841. buf.PutInt( infolong );
  1842. buf.PutChar( flagsbyte );
  1843. buf.PutInt( m_dataStart );
  1844. buf.PutInt( m_dataSize );
  1845. buf.PutInt( m_loopStart );
  1846. buf.PutInt( m_sampleCount );
  1847. if ( flags.m_bSentence )
  1848. {
  1849. m_pSentence->CacheSaveToBuffer( buf, CACHED_SENTENCE_VERSION );
  1850. }
  1851. Assert( m_usCachedDataSize < 65535 );
  1852. if ( flags.m_bCachedData && m_pCachedData )
  1853. {
  1854. buf.PutInt( m_usCachedDataSize );
  1855. buf.Put( m_pCachedData, m_usCachedDataSize );
  1856. }
  1857. Assert( m_usHeaderSize <= 32767 );
  1858. if ( flags.m_bHeader )
  1859. {
  1860. buf.PutShort( m_usHeaderSize );
  1861. buf.Put( m_pHeader, m_usHeaderSize );
  1862. }
  1863. }
  1864. void CAudioSourceCachedInfo::Restore( CUtlBuffer& buf )
  1865. {
  1866. // Wipe any old data!!!
  1867. Clear();
  1868. infolong = buf.GetInt();
  1869. flagsbyte = buf.GetChar();
  1870. m_dataStart = buf.GetInt();
  1871. m_dataSize = buf.GetInt();
  1872. m_loopStart = buf.GetInt();
  1873. m_sampleCount = buf.GetInt();
  1874. if ( flags.m_bSentence )
  1875. {
  1876. m_pSentence = new CSentence();
  1877. Assert( m_pSentence );
  1878. m_pSentence->CacheRestoreFromBuffer( buf );
  1879. }
  1880. if ( flags.m_bCachedData )
  1881. {
  1882. m_usCachedDataSize = buf.GetInt();
  1883. Assert( m_usCachedDataSize > 0 && m_usCachedDataSize < 65535 );
  1884. if ( m_usCachedDataSize > 0 )
  1885. {
  1886. byte *data = new byte[ m_usCachedDataSize ];
  1887. buf.Get( data, m_usCachedDataSize );
  1888. SetCachedData( data );
  1889. }
  1890. }
  1891. if ( flags.m_bHeader )
  1892. {
  1893. m_usHeaderSize = buf.GetShort();
  1894. Assert( m_usHeaderSize > 0 && m_usHeaderSize <= 32767 );
  1895. if ( m_usHeaderSize > 0 )
  1896. {
  1897. byte *data = new byte[ m_usHeaderSize ];
  1898. buf.Get( data, m_usHeaderSize );
  1899. SetHeaderData( data );
  1900. }
  1901. }
  1902. }
  1903. int CAudioSourceCachedInfo::s_CurrentType = CAudioSource::AUDIO_SOURCE_MAXTYPE;
  1904. CSfxTable *CAudioSourceCachedInfo::s_pSfx = NULL;
  1905. bool CAudioSourceCachedInfo::s_bIsPrecacheSound = false;
  1906. void CAudioSourceCachedInfo::Rebuild( char const *filename )
  1907. {
  1908. // Wipe any old data
  1909. Clear();
  1910. Assert( s_pSfx );
  1911. Assert( s_CurrentType != CAudioSource::AUDIO_SOURCE_MAXTYPE );
  1912. #if 0
  1913. // Never cachify something which is not in the client precache list
  1914. if ( s_bIsPrecacheSound != s_pSfx->IsPrecachedSound() )
  1915. {
  1916. Msg( "Logic bug, precaching entry for '%s' which is not in precache list\n",
  1917. filename );
  1918. }
  1919. #endif
  1920. SetType( s_CurrentType );
  1921. CAudioSource *as = NULL;
  1922. // Note though these instantiate a specific AudioSource subclass, it doesn't matter, we just need one for .wav and one for .mp3
  1923. switch ( s_CurrentType )
  1924. {
  1925. default:
  1926. case CAudioSource::AUDIO_SOURCE_VOICE:
  1927. break;
  1928. case CAudioSource::AUDIO_SOURCE_WAV:
  1929. as = new CAudioSourceMemWave( s_pSfx );
  1930. break;
  1931. case CAudioSource::AUDIO_SOURCE_MP3:
  1932. #if defined( MP3_SUPPORT )
  1933. as = new CAudioSourceMP3Cache( s_pSfx );
  1934. #endif
  1935. break;
  1936. }
  1937. if ( as )
  1938. {
  1939. as->GetCacheData( this );
  1940. delete as;
  1941. }
  1942. }
  1943. // Versions
  1944. // 3: The before time
  1945. // 4: Changed MP3 caching to ensure we store proper sample rate, removed hack to not cache vo/
  1946. // 5: Fixed bug that could result in incorrect mp3 datasizes in the sound cache
  1947. #define AUDIOSOURCE_CACHE_VERSION 5
  1948. class CAudioSourceCache : public IAudioSourceCache
  1949. {
  1950. public:
  1951. struct SearchPathCache : CUtlCachedFileData< CAudioSourceCachedInfo >
  1952. {
  1953. SearchPathCache( const char *pszRepositoryFilename, const char *pszSearchPath, UtlCachedFileDataType_t eOutOfDateMethod )
  1954. : CUtlCachedFileData( pszRepositoryFilename, AUDIOSOURCE_CACHE_VERSION, AsyncLookaheadMetaChecksum, eOutOfDateMethod )
  1955. {
  1956. V_strcpy_safe( m_szSearchPath, pszSearchPath );
  1957. // Delete any existing cache if it's out of date
  1958. IsUpToDate();
  1959. // Load up existing cache file
  1960. Init();
  1961. }
  1962. char m_szSearchPath[ MAX_PATH ];
  1963. virtual ~SearchPathCache()
  1964. {
  1965. Shutdown();
  1966. }
  1967. };
  1968. CAudioSourceCache()
  1969. {
  1970. m_nServerCount = -1;
  1971. m_bSndCacheDebug = false;
  1972. }
  1973. bool Init( unsigned int memSize );
  1974. void Shutdown();
  1975. void CheckSaveDirtyCaches();
  1976. void CheckCacheBuild();
  1977. void BuildCache( char const *pszSearchPath );
  1978. static unsigned int AsyncLookaheadMetaChecksum( void );
  1979. void LevelInit( char const *mapname );
  1980. void LevelShutdown();
  1981. virtual CAudioSourceCachedInfo *GetInfo( int audiosourcetype, bool soundisprecached, CSfxTable *sfx );
  1982. virtual void RebuildCacheEntry( int audiosourcetype, bool soundisprecached, CSfxTable *sfx );
  1983. virtual void ForceRecheckDiskInfo();
  1984. private:
  1985. SearchPathCache *LookUpCacheEntry( const char *szCleanedFilename, int audiosourcetype, bool soundisprecached, CSfxTable *sfx );
  1986. SearchPathCache *FindCacheForSearchPath( const char *pszSearchPath );
  1987. SearchPathCache *CreateCacheForSearchPath( const char *pszSearchPath );
  1988. static void GetSoundFilename( char *szResult, int nResultSize, const char *pszInputFilename );
  1989. void RemoveCache( char const *cachename );
  1990. // List of all loaded caches
  1991. CUtlVector<SearchPathCache*> m_vecCaches;
  1992. int m_nServerCount;
  1993. bool m_bSndCacheDebug;
  1994. };
  1995. static CAudioSourceCache g_ASCache;
  1996. IAudioSourceCache *audiosourcecache = &g_ASCache;
  1997. unsigned int CAudioSourceCachedInfoHandle_t::s_nCurrentFlushCount = 1;
  1998. //-----------------------------------------------------------------------------
  1999. // Purpose:
  2000. //-----------------------------------------------------------------------------
  2001. void CAudioSourceCachedInfoHandle_t::InvalidateCache()
  2002. {
  2003. ++s_nCurrentFlushCount;
  2004. }
  2005. //-----------------------------------------------------------------------------
  2006. // Purpose:
  2007. // Output : Returns true on success, false on failure.
  2008. //-----------------------------------------------------------------------------
  2009. bool CAudioSourceCache::Init( unsigned int memSize )
  2010. {
  2011. #if defined( _DEBUG )
  2012. Msg( "CAudioSourceCache: Init\n" );
  2013. #endif
  2014. m_bSndCacheDebug = CommandLine()->FindParm( "-sndcachedebug" ) ? true : false;
  2015. if ( !wavedatacache->Init( memSize ) )
  2016. {
  2017. Error( "Unable to init wavedatacache system\n" );
  2018. return false;
  2019. }
  2020. if ( IsX360() )
  2021. {
  2022. // 360 doesn't use audio source caches
  2023. return true;
  2024. }
  2025. // Gather up list of search paths
  2026. CUtlVector< CUtlString > vecSearchPaths;
  2027. GetSearchPath( vecSearchPaths, "game" );
  2028. // Create corresponding caches
  2029. FOR_EACH_VEC( vecSearchPaths, idxSearchPath )
  2030. {
  2031. // Standardize the name
  2032. char szSearchPath[ MAX_PATH ];
  2033. V_strcpy_safe( szSearchPath, vecSearchPaths[idxSearchPath] );
  2034. V_FixSlashes( szSearchPath );
  2035. V_AppendSlash( szSearchPath, sizeof(szSearchPath ) );
  2036. // See if we already have a cache for this search path.
  2037. bool bFound = false;
  2038. FOR_EACH_VEC( m_vecCaches, idxCache )
  2039. {
  2040. if ( V_stricmp( szSearchPath, m_vecCaches[idxCache]->m_szSearchPath ) == 0 )
  2041. {
  2042. Assert( V_strcmp( szSearchPath, m_vecCaches[idxCache]->m_szSearchPath ) == 0 ); // case *should* match exactly
  2043. bFound = true;
  2044. break;
  2045. }
  2046. }
  2047. if ( bFound )
  2048. continue;
  2049. // Add a ceche
  2050. SearchPathCache *pCache = CreateCacheForSearchPath( szSearchPath );
  2051. m_vecCaches.AddToTail( pCache );
  2052. }
  2053. return true;
  2054. }
  2055. CAudioSourceCache::SearchPathCache *CAudioSourceCache::FindCacheForSearchPath( const char *pszSearchPath )
  2056. {
  2057. FOR_EACH_VEC( m_vecCaches, idx )
  2058. {
  2059. SearchPathCache *pCache = m_vecCaches[idx];
  2060. if ( V_stricmp( pCache->m_szSearchPath, pszSearchPath ) == 0 )
  2061. {
  2062. return pCache;
  2063. }
  2064. }
  2065. return NULL;
  2066. }
  2067. CAudioSourceCache::SearchPathCache *CAudioSourceCache::CreateCacheForSearchPath( const char *pszSearchPath )
  2068. {
  2069. // Make sure search path ends in a slash
  2070. char szSearchPath[ MAX_PATH ];
  2071. V_strcpy_safe( szSearchPath, pszSearchPath );
  2072. V_AppendSlash( szSearchPath, sizeof(szSearchPath) );
  2073. // Set the filename for the cache.
  2074. UtlCachedFileDataType_t eOutOfDateMethod = UTL_CACHED_FILE_USE_FILESIZE;
  2075. char szCacheName[ MAX_PATH + 32 ];
  2076. V_strcpy_safe( szCacheName, szSearchPath );
  2077. char *dotVpkSlash = V_stristr( szCacheName, ".vpk" CORRECT_PATH_SEPARATOR_S );
  2078. if ( dotVpkSlash )
  2079. {
  2080. Assert( dotVpkSlash[5] == '\0' );
  2081. char *d = dotVpkSlash+4; // backup to where the slash is
  2082. Assert( *d == CORRECT_PATH_SEPARATOR );
  2083. V_strcpy( d, ".sound.cache" );
  2084. }
  2085. else
  2086. {
  2087. V_strcat_safe( szCacheName, "sound" CORRECT_PATH_SEPARATOR_S "sound.cache" );
  2088. eOutOfDateMethod = UTL_CACHED_FILE_USE_TIMESTAMP;
  2089. }
  2090. return new SearchPathCache( szCacheName, szSearchPath, eOutOfDateMethod );
  2091. }
  2092. //-----------------------------------------------------------------------------
  2093. void CAudioSourceCache::Shutdown()
  2094. {
  2095. #if defined( _DEBUG )
  2096. Msg( "CAudioSourceCache: Shutdown\n" );
  2097. #endif
  2098. CheckSaveDirtyCaches();
  2099. m_vecCaches.PurgeAndDeleteElements();
  2100. wavedatacache->Shutdown();
  2101. }
  2102. //-----------------------------------------------------------------------------
  2103. // Purpose: Called by Host_Init on engine startup to rebuild everything if needed
  2104. //-----------------------------------------------------------------------------
  2105. void CAudioSourceCache::CheckCacheBuild()
  2106. {
  2107. if ( IsX360() )
  2108. {
  2109. return;
  2110. }
  2111. // !FIXME! We'll just do everything lazily for now!
  2112. FOR_EACH_VEC( m_vecCaches, idx )
  2113. {
  2114. }
  2115. }
  2116. //-----------------------------------------------------------------------------
  2117. void CAudioSourceCache::CheckSaveDirtyCaches()
  2118. {
  2119. FOR_EACH_VEC( m_vecCaches, idx )
  2120. {
  2121. SearchPathCache *pCache = m_vecCaches[idx];
  2122. if ( pCache->IsDirty() && pCache->GetNumElements() > 0 )
  2123. {
  2124. Msg( "Saving %s\n", pCache->GetRepositoryFileName() );
  2125. pCache->Save();
  2126. }
  2127. }
  2128. }
  2129. //-----------------------------------------------------------------------------
  2130. // Purpose: Static method
  2131. // Output : unsigned int
  2132. //-----------------------------------------------------------------------------
  2133. unsigned int CAudioSourceCache::AsyncLookaheadMetaChecksum( void )
  2134. {
  2135. if ( IsX360() )
  2136. {
  2137. return 0;
  2138. }
  2139. CRC32_t crc;
  2140. CRC32_Init( &crc );
  2141. float f = SND_ASYNC_LOOKAHEAD_SECONDS;
  2142. CRC32_ProcessBuffer( &crc, &f, sizeof( f ) );
  2143. // Finish
  2144. CRC32_Final( &crc );
  2145. return (unsigned int)crc;
  2146. }
  2147. //-----------------------------------------------------------------------------
  2148. // Purpose:
  2149. // Input : *mapname -
  2150. //-----------------------------------------------------------------------------
  2151. void CAudioSourceCache::LevelInit( char const *mapname )
  2152. {
  2153. CheckSaveDirtyCaches();
  2154. }
  2155. //-----------------------------------------------------------------------------
  2156. // Purpose:
  2157. //-----------------------------------------------------------------------------
  2158. void CAudioSourceCache::LevelShutdown()
  2159. {
  2160. CheckSaveDirtyCaches();
  2161. }
  2162. //-----------------------------------------------------------------------------
  2163. void CAudioSourceCache::GetSoundFilename( char *szResult, int nResultSize, const char *pszInputFilename )
  2164. {
  2165. V_snprintf( szResult, nResultSize, "sound/%s", pszInputFilename );
  2166. V_FixSlashes( szResult );
  2167. V_RemoveDotSlashes( szResult );
  2168. V_strlower( szResult );
  2169. }
  2170. //-----------------------------------------------------------------------------
  2171. CAudioSourceCache::SearchPathCache *CAudioSourceCache::LookUpCacheEntry( const char *fn, int audiosourcetype, bool soundisprecached, CSfxTable *sfx )
  2172. {
  2173. if ( IsX360() )
  2174. {
  2175. return NULL;
  2176. }
  2177. // Hack to remember the type of audiosource to create if we need to recreate it
  2178. CAudioSourceCachedInfo::s_CurrentType = audiosourcetype;
  2179. CAudioSourceCachedInfo::s_pSfx = sfx;
  2180. CAudioSourceCachedInfo::s_bIsPrecacheSound = soundisprecached;
  2181. // Get cleaned up filename
  2182. char szRelFilename[ 256 ];
  2183. GetSoundFilename( szRelFilename, sizeof( szRelFilename ), sfx->GetFileName() );
  2184. // Get absolute filename. This thing had better exist in the filesystem somewhere
  2185. char szAbsFilename[ 1024 ];
  2186. if ( !g_pFullFileSystem->RelativePathToFullPath( szRelFilename, "game", szAbsFilename, sizeof(szAbsFilename) ) )
  2187. {
  2188. return NULL;
  2189. }
  2190. // now try to figure out which search path this corresponds to
  2191. FOR_EACH_VEC( m_vecCaches, idx )
  2192. {
  2193. SearchPathCache *pCache = m_vecCaches[idx];
  2194. if ( V_strncmp( pCache->m_szSearchPath, szAbsFilename, V_strlen( pCache->m_szSearchPath ) ) == 0 )
  2195. {
  2196. return pCache;
  2197. }
  2198. }
  2199. Warning( "Cannot figure out which search path %s came from. Absolute path is %s\n", szRelFilename, szAbsFilename );
  2200. SearchPathCache *pCache = NULL;
  2201. return pCache;
  2202. }
  2203. //-----------------------------------------------------------------------------
  2204. // Purpose:
  2205. //-----------------------------------------------------------------------------
  2206. CAudioSourceCachedInfo *CAudioSourceCache::GetInfo( int audiosourcetype, bool soundisprecached, CSfxTable *sfx )
  2207. {
  2208. VPROF("CAudioSourceCache::GetInfo");
  2209. if ( IsX360() )
  2210. {
  2211. // 360 not using
  2212. return NULL;
  2213. }
  2214. Assert( sfx );
  2215. char fn[ 512 ];
  2216. GetSoundFilename( fn, sizeof( fn ), sfx->GetFileName() );
  2217. CAudioSourceCachedInfo *info = NULL;
  2218. SearchPathCache *pCache = LookUpCacheEntry( fn, audiosourcetype, soundisprecached, sfx );
  2219. if ( !pCache )
  2220. return NULL;
  2221. info = pCache->Get( fn );
  2222. // Is this applicable anymore now that we have a cache per search path?
  2223. // if ( info && info->Format() == 0 )
  2224. // {
  2225. // if ( g_pFullFileSystem->FileExists( fn, "BSP" ) )
  2226. // {
  2227. // DevMsg( 1, "Forced rebuild of bsp cache sound '%s'\n", fn );
  2228. // info = pCache->RebuildItem( fn );
  2229. // Assert( info->Format() != 0 );
  2230. // }
  2231. // }
  2232. return info;
  2233. }
  2234. void CAudioSourceCache::RebuildCacheEntry( int audiosourcetype, bool soundisprecached, CSfxTable *sfx )
  2235. {
  2236. VPROF("CAudioSourceCache::RebuildCacheEntry");
  2237. if ( IsX360() )
  2238. {
  2239. // 360 not using
  2240. return;
  2241. }
  2242. Assert( sfx );
  2243. char fn[ 512 ];
  2244. GetSoundFilename( fn, sizeof( fn ), sfx->GetFileName() );
  2245. SearchPathCache *pCache = LookUpCacheEntry( fn, audiosourcetype, soundisprecached, sfx );
  2246. if ( !pCache )
  2247. return;
  2248. pCache->RebuildItem( fn );
  2249. }
  2250. //-----------------------------------------------------------------------------
  2251. void CAudioSourceCache::ForceRecheckDiskInfo()
  2252. {
  2253. FOR_EACH_VEC( m_vecCaches, idx )
  2254. {
  2255. m_vecCaches[ idx ]->ForceRecheckDiskInfo();
  2256. }
  2257. }
  2258. //-----------------------------------------------------------------------------
  2259. void CAudioSourceCache::RemoveCache( char const *cachename )
  2260. {
  2261. if ( IsX360() )
  2262. {
  2263. return;
  2264. }
  2265. if ( g_pFullFileSystem->FileExists( cachename, "MOD" ) )
  2266. {
  2267. if ( !g_pFullFileSystem->IsFileWritable( cachename, "MOD" ) )
  2268. {
  2269. g_pFullFileSystem->SetFileWritable( cachename, true, "MOD" );
  2270. }
  2271. g_pFullFileSystem->RemoveFile( cachename, "MOD" );
  2272. }
  2273. }
  2274. //-----------------------------------------------------------------------------
  2275. void CAudioSourceCache::BuildCache( char const *pszSearchPath )
  2276. {
  2277. // Get absolute path
  2278. char szAbsPath[ MAX_PATH ];
  2279. V_MakeAbsolutePath( szAbsPath, sizeof(szAbsPath), pszSearchPath );
  2280. V_FixSlashes( szAbsPath );
  2281. // Add a search path to the filesystem. We'll add one search path as a kludge so we
  2282. // can use the existing file finder system easily
  2283. g_pFullFileSystem->AddSearchPath( szAbsPath, "soundcache_kludge", PATH_ADD_TO_HEAD );
  2284. Msg( "Finding .wav files...\n");
  2285. CUtlVector< CUtlString > vecFilenames;
  2286. AddFilesToList( vecFilenames, "sound", "soundcache_kludge", "wav" );
  2287. Msg( "Finding .mp3 files...\n");
  2288. AddFilesToList( vecFilenames, "sound", "soundcache_kludge", "mp3" );
  2289. Msg( "Found %d audio files.\n", vecFilenames.Count() );
  2290. g_pFullFileSystem->RemoveSearchPaths( "soundcache_kludge" );
  2291. if ( vecFilenames.Count() < 1 )
  2292. {
  2293. Warning(" No audio files found. Not building cache\n" );
  2294. return;
  2295. }
  2296. // FindCacheForSearchPath expects an absolute search path, but if we're working with a VPK we'll have the path to
  2297. // the file, wherein the proper path to the file is /foo/bar.vpk, but the *search path* should be /foo/bar.vpk/
  2298. char szAsSearchPath[MAX_PATH] = { 0 };
  2299. V_strncpy( szAsSearchPath, szAbsPath, sizeof( szAsSearchPath ) );
  2300. V_AppendSlash( szAsSearchPath, sizeof( szAsSearchPath ) );
  2301. SearchPathCache *pCache = FindCacheForSearchPath( szAsSearchPath );
  2302. if ( !pCache )
  2303. {
  2304. // This cache might not have existed on startup
  2305. pCache = CreateCacheForSearchPath( szAbsPath );
  2306. m_vecCaches.AddToTail( pCache );
  2307. }
  2308. g_pFullFileSystem->AddSearchPath( szAbsPath, "game", PATH_ADD_TO_HEAD );
  2309. int nLenAbsPath = V_strlen( szAbsPath );
  2310. int iLastShownPct = -1;
  2311. FOR_EACH_VEC( vecFilenames, idxFilename )
  2312. {
  2313. const char *pszFilename = vecFilenames[ idxFilename ];
  2314. if ( V_strnicmp( pszFilename, szAbsPath, nLenAbsPath ) != 0 )
  2315. {
  2316. Warning( "Sound %s doesn't begin with search path %s\n", pszFilename, szAbsPath );
  2317. Assert( false );
  2318. continue;
  2319. }
  2320. const char *pszRelName = pszFilename + nLenAbsPath;
  2321. if ( *pszRelName == '/' || *pszRelName == '\\' )
  2322. ++pszRelName;
  2323. if ( V_strnicmp( pszRelName, "sound" CORRECT_PATH_SEPARATOR_S, 6 ) != 0 )
  2324. {
  2325. Warning( "Relative name %s doesn't begin with leading 'sound' directory?\n", pszRelName );
  2326. Assert( false );
  2327. continue;
  2328. }
  2329. const char *pszName = pszRelName + 6;
  2330. // Show progress
  2331. int iPct = idxFilename * 100 / vecFilenames.Count();
  2332. if ( iPct != iLastShownPct )
  2333. {
  2334. Msg( " %3d%% %s\n", iPct, pszName );
  2335. iLastShownPct = iPct;
  2336. }
  2337. CAudioSourceCachedInfo::s_bIsPrecacheSound = true;
  2338. CAudioSourceCachedInfo::s_CurrentType = CAudioSource::AUDIO_SOURCE_WAV;
  2339. char szExt[ 10 ] = { 0 };
  2340. V_ExtractFileExtension( pszFilename, szExt, sizeof( szExt ) );
  2341. if ( V_stricmp( szExt, "mp3" ) == 0 )
  2342. {
  2343. CAudioSourceCachedInfo::s_CurrentType = CAudioSource::AUDIO_SOURCE_MP3;
  2344. }
  2345. CAudioSourceCachedInfo::s_pSfx = S_DummySfx( pszName );
  2346. const CAudioSourceCachedInfo *pInfo = pCache->Get( pszRelName );
  2347. if ( !pInfo )
  2348. {
  2349. Warning( "Failed to cache info for %s\n", pszFilename );
  2350. }
  2351. }
  2352. g_pFullFileSystem->RemoveSearchPath( szAbsPath, "game" );
  2353. if ( pCache->IsDirty() )
  2354. {
  2355. Msg( "Saving %s\n", pCache->GetRepositoryFileName() );
  2356. pCache->Save();
  2357. }
  2358. else
  2359. {
  2360. Msg( "No changes detected; not saving %s\n", pCache->GetRepositoryFileName() );
  2361. }
  2362. }
  2363. void CheckCacheBuild()
  2364. {
  2365. g_ASCache.CheckCacheBuild();
  2366. }
  2367. CON_COMMAND( snd_buildcache, "<directory or VPK filename> Rebulds sound cache for a given search path.\n" )
  2368. {
  2369. if ( args.ArgC() < 2 )
  2370. {
  2371. ConMsg( "Usage: snd_buildcache <directory or VPK filename>\n" );
  2372. return;
  2373. }
  2374. // Allow them to eitehr specify multiple args, or comma-seperated list.
  2375. // You cannot easily pas multiple args on the (OS) command line.
  2376. for ( int idxArg = 1 ; idxArg < args.ArgC() ; ++idxArg )
  2377. {
  2378. CUtlStringList vecPaths;
  2379. V_SplitString( args[idxArg], ",", vecPaths );
  2380. FOR_EACH_VEC( vecPaths, idxPath )
  2381. {
  2382. g_ASCache.BuildCache( vecPaths[idxPath] );
  2383. }
  2384. }
  2385. // And now quit the game, because we mucked with search paths and the game is almost certainly not
  2386. // going to work anymore
  2387. Msg( "Quitting the game because we probably screwed up the search paths...\n" );
  2388. extern void HostState_Shutdown();
  2389. HostState_Shutdown();
  2390. }