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.

602 lines
16 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 "snd_dma.h"
  11. #include "snd_wave_mixer_mp3.h"
  12. #include "filesystem_engine.h"
  13. #include "utldict.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. #ifndef DEDICATED // have to test this because VPC is forcing us to compile this file.
  17. // How many bytes initial data bytes of the mp3 should be saved in the soundcache, in addition to the small amount of
  18. // metadata (playbackrate, etc). This will increase memory usage by
  19. // ( N * number-of-precached-mp3-sounds-in-the-whole-game ) at all times, as the soundcache is held in memory.
  20. //
  21. // Right now we're setting this to zero. The IsReadyToMix() logic at the data layer will delay mixing of the sound until
  22. // it arrives. Setting this to anything above zero, however, will allow the sound to start, so it needs to either be
  23. // enough to cover SND_ASYNC_LOOKAHEAD_SECONDS or none at all.
  24. #define MP3_STARTUP_DATA_SIZE_BYTES 0
  25. CUtlDict< CSentence *, int> g_PhonemeFileSentences;
  26. bool g_bAllPhonemesLoaded;
  27. void PhonemeMP3Shutdown( void )
  28. {
  29. g_PhonemeFileSentences.PurgeAndDeleteElements();
  30. g_bAllPhonemesLoaded = false;
  31. }
  32. void AddPhonemesFromFile( const char *pszFileName )
  33. {
  34. // If all Phonemes are loaded, do not load anymore
  35. if ( g_bAllPhonemesLoaded && g_PhonemeFileSentences.Count() != 0 )
  36. return;
  37. // Empty file name implies stop loading more phonemes
  38. if ( pszFileName == NULL )
  39. {
  40. g_bAllPhonemesLoaded = true;
  41. return;
  42. }
  43. // Load this file
  44. g_bAllPhonemesLoaded = false;
  45. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  46. if ( g_pFileSystem->ReadFile( pszFileName, "MOD", buf ) )
  47. {
  48. while ( 1 )
  49. {
  50. char token[4096];
  51. buf.GetString( token );
  52. V_FixSlashes( token );
  53. int iIndex = g_PhonemeFileSentences.Find( token );
  54. if ( iIndex != g_PhonemeFileSentences.InvalidIndex() )
  55. {
  56. delete g_PhonemeFileSentences.Element( iIndex );
  57. g_PhonemeFileSentences.Remove( token );
  58. }
  59. CSentence *pSentence = new CSentence;
  60. g_PhonemeFileSentences.Insert( token, pSentence );
  61. buf.GetString( token );
  62. if ( strlen( token ) <= 0 )
  63. break;
  64. if ( !stricmp( token, "{" ) )
  65. {
  66. pSentence->InitFromBuffer( buf );
  67. }
  68. }
  69. }
  70. }
  71. CAudioSourceMP3::CAudioSourceMP3( CSfxTable *pSfx )
  72. {
  73. m_sampleRate = 0;
  74. m_pSfx = pSfx;
  75. m_refCount = 0;
  76. m_dataStart = 0;
  77. int file = g_pSndIO->open( pSfx->GetFileName() );
  78. if ( file != -1 )
  79. {
  80. m_dataSize = g_pSndIO->size( file );
  81. g_pSndIO->close( file );
  82. }
  83. else
  84. {
  85. // No sound cache, the file isn't here, print this so that the relatively deep failure points that are about to
  86. // spew make a little more sense
  87. Warning( "MP3 is completely missing, sound system will be upset to learn of this [ %s ]\n", pSfx->GetFileName() );
  88. m_dataSize = 0;
  89. }
  90. m_nCachedDataSize = 0;
  91. m_bIsPlayOnce = false;
  92. m_bIsSentenceWord = false;
  93. m_bCheckedForPendingSentence = false;
  94. }
  95. CAudioSourceMP3::CAudioSourceMP3( CSfxTable *pSfx, CAudioSourceCachedInfo *info )
  96. {
  97. m_pSfx = pSfx;
  98. m_refCount = 0;
  99. m_sampleRate = info->SampleRate();
  100. m_dataSize = info->DataSize();
  101. m_dataStart = info->DataStart();
  102. m_nCachedDataSize = 0;
  103. m_bIsPlayOnce = false;
  104. m_bCheckedForPendingSentence = false;
  105. CheckAudioSourceCache();
  106. }
  107. CAudioSourceMP3::~CAudioSourceMP3()
  108. {
  109. }
  110. // mixer's references
  111. void CAudioSourceMP3::ReferenceAdd( CAudioMixer * )
  112. {
  113. m_refCount++;
  114. }
  115. void CAudioSourceMP3::ReferenceRemove( CAudioMixer * )
  116. {
  117. m_refCount--;
  118. if ( m_refCount == 0 && IsPlayOnce() )
  119. {
  120. SetPlayOnce( false ); // in case it gets used again
  121. CacheUnload();
  122. }
  123. }
  124. //-----------------------------------------------------------------------------
  125. // Purpose:
  126. // Output : Returns true on success, false on failure.
  127. //-----------------------------------------------------------------------------
  128. bool CAudioSourceMP3::IsAsyncLoad()
  129. {
  130. // If there's a bit of "cached data" then we don't have to lazy/async load (we still async load the remaining data,
  131. // but we run from the cache initially)
  132. return ( m_nCachedDataSize > 0 ) ? false : true;
  133. }
  134. // check reference count, return true if nothing is referencing this
  135. bool CAudioSourceMP3::CanDelete( void )
  136. {
  137. return m_refCount > 0 ? false : true;
  138. }
  139. //-----------------------------------------------------------------------------
  140. // Purpose:
  141. // Output : int
  142. //-----------------------------------------------------------------------------
  143. int CAudioSourceMP3::GetType()
  144. {
  145. return AUDIO_SOURCE_MP3;
  146. }
  147. //-----------------------------------------------------------------------------
  148. void CAudioSourceMP3::SetSentence( CSentence *pSentence )
  149. {
  150. CAudioSourceCachedInfo *info = m_AudioCacheHandle.FastGet();
  151. if ( !info )
  152. return;
  153. if ( info && info->Sentence() )
  154. return;
  155. CSentence *pNewSentence = new CSentence;
  156. pNewSentence->Append( 0.0f, *pSentence );
  157. pNewSentence->MakeRuntimeOnly();
  158. info->SetSentence( pNewSentence );
  159. }
  160. int CAudioSourceMP3::SampleRate()
  161. {
  162. if ( !m_sampleRate )
  163. {
  164. // This should've come from the sound cache. We can avoid sync I/O jank if and only if we've started streaming
  165. // data already for some other reason. (Despite the name, CreateWaveDataMemory is just creating a wrapper class
  166. // that manages access to the wave data cache)
  167. IWaveData *pData = CreateWaveDataMemory( *this );
  168. if ( !pData->IsReadyToMix() && SND_IsInGame() )
  169. {
  170. // If you hit this, you're creating a sound source that isn't in the sound cache, and asking for its sample
  171. // rate before it has streamed enough data in to read it from the underlying file. Your options are:
  172. // - Rebuild sound cache or figure out why this sound wasn't included.
  173. // - Precache this sound at level load so this doesn't happen during gameplay.
  174. // - Somehow call CacheLoad() on this source earlier so it has time to get data into memory so the data
  175. // shows up as IsReadyToMix here, and this crutch won't jank.
  176. Warning( "MP3 initialized with no sound cache, this may cause janking. [ %s ]\n", GetFileName() );
  177. // The code below will still go fine, but the mixer will emit a jank warning that the data wasn't ready and
  178. // do sync I/O
  179. }
  180. CAudioMixerWaveMP3 *pMixer = new CAudioMixerWaveMP3( pData );
  181. m_sampleRate = pMixer->GetStreamOutputRate();
  182. // pData ownership is passed to, and free'd by, pMixer
  183. delete pMixer;
  184. }
  185. return m_sampleRate;
  186. }
  187. void CAudioSourceMP3::GetCacheData( CAudioSourceCachedInfo *info )
  188. {
  189. // Don't want to replicate our cached sample rate back into the new cache, ensure we recompute it.
  190. CAudioMixerWaveMP3 *pTempMixer = new CAudioMixerWaveMP3( CreateWaveDataMemory(*this) );
  191. m_sampleRate = pTempMixer->GetStreamOutputRate();
  192. delete pTempMixer;
  193. AssertMsg( m_sampleRate, "Creating cache with invalid sample rate data" );
  194. if ( !m_sampleRate )
  195. {
  196. Warning( "Failed to find sample rate creating cache data for MP3, cache will be invalid [ %s ]\n", GetFileName() );
  197. }
  198. info->SetSampleRate( m_sampleRate );
  199. info->SetDataStart( 0 );
  200. int file = g_pSndIO->open( m_pSfx->GetFileName() );
  201. if ( !file )
  202. {
  203. Warning( "Failed to find file for building soundcache [ %s ]\n", m_pSfx->GetFileName() );
  204. // Don't re-use old cached value
  205. m_dataSize = 0;
  206. }
  207. else
  208. {
  209. m_dataSize = (int)g_pSndIO->size( file );
  210. }
  211. Assert( m_dataSize > 0 );
  212. // Do we need to actually load any audio data?
  213. #if MP3_STARTUP_DATA_SIZE_BYTES > 0 // We may have defined the startup data to nothingness
  214. if ( info->s_bIsPrecacheSound && m_dataSize > 0 )
  215. {
  216. // Ideally this would mimic the wave startup data code and figure out this calculation:
  217. // int bytesNeeded = m_channels * ( m_bits >> 3 ) * m_rate * SND_ASYNC_LOOKAHEAD_SECONDS;
  218. // (plus header)
  219. int dataSize = min( MP3_STARTUP_DATA_SIZE_BYTES, m_dataSize );
  220. byte *data = new byte[ dataSize ]();
  221. int readSize = g_pSndIO->read( data, dataSize, file );
  222. if ( readSize != dataSize )
  223. {
  224. Warning( "Building soundcache, expected %i bytes of data but got %i [ %s ]\n", dataSize, readSize, m_pSfx->GetFileName() );
  225. dataSize = readSize;
  226. }
  227. info->SetCachedDataSize( dataSize );
  228. info->SetCachedData( data );
  229. }
  230. #endif // MP3_STARTUP_DATA_SIZE_BYTES > 0
  231. g_pSndIO->close( file );
  232. // Data size gets computed in GetStartupData!!!
  233. info->SetDataSize( m_dataSize );
  234. }
  235. //-----------------------------------------------------------------------------
  236. // Purpose:
  237. // Output : char const
  238. //-----------------------------------------------------------------------------
  239. char const *CAudioSourceMP3::GetFileName()
  240. {
  241. return m_pSfx ? m_pSfx->GetFileName() : "NULL m_pSfx";
  242. }
  243. //-----------------------------------------------------------------------------
  244. // Purpose:
  245. //-----------------------------------------------------------------------------
  246. void CAudioSourceMP3::CheckAudioSourceCache()
  247. {
  248. Assert( m_pSfx );
  249. if ( !m_pSfx->IsPrecachedSound() )
  250. {
  251. return;
  252. }
  253. // This will "re-cache" this if it's not in this level's cache already
  254. m_AudioCacheHandle.Get( GetType(), true, m_pSfx, &m_nCachedDataSize );
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Purpose: NULL the wave data pointer (we haven't loaded yet)
  258. //-----------------------------------------------------------------------------
  259. CAudioSourceMP3Cache::CAudioSourceMP3Cache( CSfxTable *pSfx ) :
  260. CAudioSourceMP3( pSfx )
  261. {
  262. m_hCache = 0;
  263. }
  264. CAudioSourceMP3Cache::CAudioSourceMP3Cache( CSfxTable *pSfx, CAudioSourceCachedInfo *info ) :
  265. CAudioSourceMP3( pSfx, info )
  266. {
  267. m_hCache = 0;
  268. m_dataSize = info->DataSize();
  269. m_dataStart = info->DataStart();
  270. m_bNoSentence = false;
  271. }
  272. //-----------------------------------------------------------------------------
  273. // Purpose: Free any wave data we've allocated
  274. //-----------------------------------------------------------------------------
  275. CAudioSourceMP3Cache::~CAudioSourceMP3Cache( void )
  276. {
  277. CacheUnload();
  278. }
  279. int CAudioSourceMP3Cache::GetCacheStatus( void )
  280. {
  281. bool bCacheValid;
  282. int loaded = wavedatacache->IsDataLoadCompleted( m_hCache, &bCacheValid ) ? AUDIO_IS_LOADED : AUDIO_NOT_LOADED;
  283. if ( !bCacheValid )
  284. {
  285. wavedatacache->RestartDataLoad( &m_hCache, m_pSfx->GetFileName(), m_dataSize, m_dataStart );
  286. }
  287. return loaded;
  288. }
  289. void CAudioSourceMP3Cache::CacheLoad( void )
  290. {
  291. // Commence lazy load?
  292. if ( m_hCache != 0 )
  293. {
  294. GetCacheStatus();
  295. return;
  296. }
  297. m_hCache = wavedatacache->AsyncLoadCache( m_pSfx->GetFileName(), m_dataSize, m_dataStart );
  298. }
  299. void CAudioSourceMP3Cache::CacheUnload( void )
  300. {
  301. if ( m_hCache != 0 )
  302. {
  303. wavedatacache->Unload( m_hCache );
  304. }
  305. }
  306. char *CAudioSourceMP3Cache::GetDataPointer( void )
  307. {
  308. char *pMP3Data = NULL;
  309. bool dummy = false;
  310. if ( m_hCache == 0 )
  311. {
  312. CacheLoad();
  313. }
  314. wavedatacache->GetDataPointer(
  315. m_hCache,
  316. m_pSfx->GetFileName(),
  317. m_dataSize,
  318. m_dataStart,
  319. (void **)&pMP3Data,
  320. 0,
  321. &dummy );
  322. return pMP3Data;
  323. }
  324. int CAudioSourceMP3Cache::GetOutputData( void **pData, int samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] )
  325. {
  326. // how many bytes are available ?
  327. int totalSampleCount = m_dataSize - samplePosition;
  328. // may be asking for a sample out of range, clip at zero
  329. if ( totalSampleCount < 0 )
  330. totalSampleCount = 0;
  331. // clip max output samples to max available
  332. if ( sampleCount > totalSampleCount )
  333. sampleCount = totalSampleCount;
  334. // if we are returning some samples, store the pointer
  335. if ( sampleCount )
  336. {
  337. // Starting past end of "preloaded" data, just use regular cache
  338. if ( samplePosition >= m_nCachedDataSize )
  339. {
  340. *pData = GetDataPointer();
  341. }
  342. else
  343. {
  344. // Start async loader if we haven't already done so
  345. CacheLoad();
  346. // Return less data if we are about to run out of uncached data
  347. if ( samplePosition + sampleCount >= m_nCachedDataSize )
  348. {
  349. sampleCount = m_nCachedDataSize - samplePosition;
  350. }
  351. // Point at preloaded/cached data from .cache file for now
  352. *pData = GetCachedDataPointer();
  353. }
  354. if ( *pData )
  355. {
  356. *pData = (char *)*pData + samplePosition;
  357. }
  358. else
  359. {
  360. // Out of data or file i/o problem
  361. sampleCount = 0;
  362. }
  363. }
  364. return sampleCount;
  365. }
  366. CAudioMixer *CAudioSourceMP3Cache::CreateMixer( int initialStreamPosition )
  367. {
  368. CAudioMixer *pMixer = new CAudioMixerWaveMP3( CreateWaveDataMemory(*this) );
  369. return pMixer;
  370. }
  371. CSentence *CAudioSourceMP3Cache::GetSentence( void )
  372. {
  373. // Already checked and this wav doesn't have sentence data...
  374. if ( m_bNoSentence == true )
  375. {
  376. return NULL;
  377. }
  378. // Look up sentence from cache
  379. CAudioSourceCachedInfo *info = m_AudioCacheHandle.FastGet();
  380. if ( !info )
  381. {
  382. info = m_AudioCacheHandle.Get( CAudioSource::AUDIO_SOURCE_WAV, m_pSfx->IsPrecachedSound(), m_pSfx, &m_nCachedDataSize );
  383. }
  384. Assert( info );
  385. if ( !info )
  386. {
  387. m_bNoSentence = true;
  388. return NULL;
  389. }
  390. CSentence *sentence = info->Sentence();
  391. if ( !sentence )
  392. {
  393. if ( !m_bCheckedForPendingSentence )
  394. {
  395. int iSentence = g_PhonemeFileSentences.Find( m_pSfx->GetFileName() );
  396. if ( iSentence != g_PhonemeFileSentences.InvalidIndex() )
  397. {
  398. sentence = g_PhonemeFileSentences.Element( iSentence );
  399. SetSentence( sentence );
  400. }
  401. m_bCheckedForPendingSentence = true;
  402. }
  403. }
  404. if ( !sentence )
  405. {
  406. m_bNoSentence = true;
  407. return NULL;
  408. }
  409. if ( sentence->m_bIsValid )
  410. {
  411. return sentence;
  412. }
  413. m_bNoSentence = true;
  414. return NULL;
  415. }
  416. //-----------------------------------------------------------------------------
  417. // CAudioSourceStreamMP3
  418. //-----------------------------------------------------------------------------
  419. CAudioSourceStreamMP3::CAudioSourceStreamMP3( CSfxTable *pSfx ) :
  420. CAudioSourceMP3( pSfx )
  421. {
  422. }
  423. CAudioSourceStreamMP3::CAudioSourceStreamMP3( CSfxTable *pSfx, CAudioSourceCachedInfo *info ) :
  424. CAudioSourceMP3( pSfx, info )
  425. {
  426. m_dataSize = info->DataSize();
  427. }
  428. //-----------------------------------------------------------------------------
  429. // Purpose:
  430. //-----------------------------------------------------------------------------
  431. void CAudioSourceStreamMP3::Prefetch()
  432. {
  433. PrefetchDataStream( m_pSfx->GetFileName(), 0, m_dataSize );
  434. }
  435. CAudioMixer *CAudioSourceStreamMP3::CreateMixer( int intialStreamPosition )
  436. {
  437. // BUGBUG: Source constructs the IWaveData, mixer frees it, fix this?
  438. IWaveData *pWaveData = CreateWaveDataStream( *this, static_cast<IWaveStreamSource *>( this ), m_pSfx->GetFileName(), 0, m_dataSize, m_pSfx, 0 );
  439. if ( pWaveData )
  440. {
  441. CAudioMixer *pMixer = new CAudioMixerWaveMP3( pWaveData );
  442. if ( pMixer )
  443. {
  444. if ( !m_bCheckedForPendingSentence )
  445. {
  446. int iSentence = g_PhonemeFileSentences.Find( m_pSfx->GetFileName() );
  447. if ( iSentence != g_PhonemeFileSentences.InvalidIndex() )
  448. {
  449. SetSentence( g_PhonemeFileSentences.Element( iSentence ) );
  450. }
  451. m_bCheckedForPendingSentence = true;
  452. }
  453. return pMixer;
  454. }
  455. // no mixer but pWaveData was deleted in mixer's destructor
  456. // so no need to delete
  457. }
  458. return NULL;
  459. }
  460. int CAudioSourceStreamMP3::GetOutputData( void **pData, int samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] )
  461. {
  462. return 0;
  463. }
  464. bool Audio_IsMP3( const char *pName )
  465. {
  466. int len = strlen(pName);
  467. if ( len > 4 )
  468. {
  469. if ( !Q_strnicmp( &pName[len - 4], ".mp3", 4 ) )
  470. {
  471. return true;
  472. }
  473. }
  474. return false;
  475. }
  476. CAudioSource *Audio_CreateStreamedMP3( CSfxTable *pSfx )
  477. {
  478. CAudioSourceStreamMP3 *pMP3 = NULL;
  479. CAudioSourceCachedInfo *info = audiosourcecache->GetInfo( CAudioSource::AUDIO_SOURCE_MP3, pSfx->IsPrecachedSound(), pSfx );
  480. if ( info )
  481. {
  482. pMP3 = new CAudioSourceStreamMP3( pSfx, info );
  483. }
  484. else
  485. {
  486. pMP3 = new CAudioSourceStreamMP3( pSfx );
  487. }
  488. return pMP3;
  489. }
  490. CAudioSource *Audio_CreateMemoryMP3( CSfxTable *pSfx )
  491. {
  492. CAudioSourceMP3Cache *pMP3 = NULL;
  493. CAudioSourceCachedInfo *info = audiosourcecache->GetInfo( CAudioSource::AUDIO_SOURCE_MP3, pSfx->IsPrecachedSound(), pSfx );
  494. if ( info )
  495. {
  496. pMP3 = new CAudioSourceMP3Cache( pSfx, info );
  497. }
  498. else
  499. {
  500. pMP3 = new CAudioSourceMP3Cache( pSfx );
  501. }
  502. return pMP3;
  503. }
  504. #endif