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.

369 lines
8.8 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Sentence Mixing
  4. //
  5. //=============================================================================//
  6. #include "audio_pch.h"
  7. #include "vox_private.h"
  8. // memdbgon must be the last include file in a .cpp file!!!
  9. #include "tier0/memdbgon.h"
  10. //-----------------------------------------------------------------------------
  11. // Purpose: This replaces the old sentence logic that was integrated with the
  12. // sound code. Now it is a hierarchical mixer.
  13. //-----------------------------------------------------------------------------
  14. class CSentenceMixer : public CAudioMixer
  15. {
  16. public:
  17. CSentenceMixer( voxword_t *pWords );
  18. ~CSentenceMixer( void );
  19. // return number of samples mixed
  20. virtual int MixDataToDevice( IAudioDevice *pDevice, channel_t *pChannel, int sampleCount, int outputRate, int outputOffset );
  21. virtual int SkipSamples( channel_t *pChannel, int sampleCount, int outputRate, int outputOffset );
  22. virtual bool ShouldContinueMixing( void );
  23. virtual CAudioSource* GetSource( void );
  24. // get the current position (next sample to be mixed)
  25. virtual int GetSamplePosition( void );
  26. virtual float ModifyPitch( float pitch );
  27. virtual float GetVolumeScale( void );
  28. // BUGBUG: These are only applied to the current word, not the whole sentence!!!!
  29. virtual void SetSampleStart( int newPosition );
  30. virtual void SetSampleEnd( int newEndPosition );
  31. virtual void SetStartupDelaySamples( int delaySamples );
  32. virtual int GetMixSampleSize() { return m_pCurrentWordMixer ? m_pCurrentWordMixer->GetMixSampleSize() : 0; }
  33. virtual bool IsReadyToMix();
  34. virtual int GetPositionForSave() { return GetSamplePosition(); }
  35. virtual void SetPositionFromSaved( int savedPosition ) { SetSampleStart( savedPosition ); }
  36. private:
  37. CAudioMixer *LoadWord( int nWordIndex );
  38. void FreeWord( int nWordIndex );
  39. // identifies the active word
  40. int m_currentWordIndex;
  41. CAudioMixer *m_pCurrentWordMixer;
  42. // set when a transition to a new word occurs
  43. bool m_bNewWord;
  44. voxword_t m_VoxWords[CVOXWORDMAX];
  45. CAudioMixer *m_pWordMixers[CVOXWORDMAX];
  46. int m_nNumWords;
  47. };
  48. CAudioMixer *CreateSentenceMixer( voxword_t *pWords )
  49. {
  50. if ( pWords )
  51. {
  52. return new CSentenceMixer( pWords );
  53. }
  54. return NULL;
  55. }
  56. CSentenceMixer::CSentenceMixer( voxword_t *pWords )
  57. {
  58. // count the expected number of words
  59. m_nNumWords = 0;
  60. while ( pWords[m_nNumWords].sfx != NULL )
  61. {
  62. // get a private copy of the words
  63. m_VoxWords[m_nNumWords] = pWords[m_nNumWords];
  64. m_nNumWords++;
  65. if ( m_nNumWords >= ARRAYSIZE( m_VoxWords ) )
  66. {
  67. // very long sentence, prevent overflow
  68. break;
  69. }
  70. }
  71. // startup all the mixers now, this serves as a hint to the audio streamer
  72. // actual mixing will commence when they are ALL ready
  73. for ( int nWord = 0; nWord < m_nNumWords; nWord++ )
  74. {
  75. // it is possible to get a null mixer (due to wav error, etc)
  76. // the sentence will skip these words
  77. m_pWordMixers[nWord] = LoadWord( nWord );
  78. }
  79. Assert( m_nNumWords < ARRAYSIZE( m_pWordMixers ) );
  80. // find first valid word mixer
  81. m_currentWordIndex = 0;
  82. m_pCurrentWordMixer = NULL;
  83. for ( int nWord = 0; nWord < m_nNumWords; nWord++ )
  84. {
  85. if ( m_pWordMixers[nWord] )
  86. {
  87. m_currentWordIndex = nWord;
  88. m_pCurrentWordMixer = m_pWordMixers[nWord];
  89. break;
  90. }
  91. }
  92. m_bNewWord = ( m_pCurrentWordMixer != NULL );
  93. }
  94. CSentenceMixer::~CSentenceMixer( void )
  95. {
  96. // free all words
  97. for ( int nWord = 0; nWord < m_nNumWords; nWord++ )
  98. {
  99. FreeWord( nWord );
  100. }
  101. }
  102. //-----------------------------------------------------------------------------
  103. // Purpose:
  104. // Output : Returns true if mixing can commence, false otherwise
  105. //-----------------------------------------------------------------------------
  106. bool CSentenceMixer::IsReadyToMix()
  107. {
  108. if ( !m_pCurrentWordMixer )
  109. {
  110. // no word, but mixing has to commence in order to shutdown
  111. return true;
  112. }
  113. // all the words should be available before mixing the sentence
  114. for ( int nWord = m_currentWordIndex; nWord < m_nNumWords; nWord++ )
  115. {
  116. if ( m_pWordMixers[nWord] && !m_pWordMixers[nWord]->IsReadyToMix() )
  117. {
  118. // Still waiting for async data to arrive
  119. return false;
  120. }
  121. }
  122. if ( m_bNewWord )
  123. {
  124. m_bNewWord = false;
  125. int start = m_VoxWords[m_currentWordIndex].start;
  126. int end = m_VoxWords[m_currentWordIndex].end;
  127. // don't allow overlapped ranges
  128. if ( end <= start )
  129. {
  130. end = 0;
  131. }
  132. if ( start || end )
  133. {
  134. int sampleCount = m_pCurrentWordMixer->GetSource()->SampleCount();
  135. if ( start > 0 && start < 100 )
  136. {
  137. m_pCurrentWordMixer->SetSampleStart( (int)(sampleCount * 0.01f * start) );
  138. }
  139. if ( end > 0 && end < 100 )
  140. {
  141. m_pCurrentWordMixer->SetSampleEnd( (int)(sampleCount * 0.01f * end) );
  142. }
  143. }
  144. }
  145. return true;
  146. }
  147. bool CSentenceMixer::ShouldContinueMixing( void )
  148. {
  149. if ( m_pCurrentWordMixer )
  150. {
  151. // keep mixing until the words run out
  152. return true;
  153. }
  154. return false;
  155. }
  156. CAudioSource *CSentenceMixer::GetSource( void )
  157. {
  158. if ( m_pCurrentWordMixer )
  159. {
  160. return m_pCurrentWordMixer->GetSource();
  161. }
  162. return NULL;
  163. }
  164. // get the current position (next sample to be mixed)
  165. int CSentenceMixer::GetSamplePosition( void )
  166. {
  167. if ( m_pCurrentWordMixer )
  168. {
  169. return m_pCurrentWordMixer->GetSamplePosition();
  170. }
  171. return 0;
  172. }
  173. void CSentenceMixer::SetSampleStart( int newPosition )
  174. {
  175. if ( m_pCurrentWordMixer )
  176. {
  177. m_pCurrentWordMixer->SetSampleStart( newPosition );
  178. }
  179. }
  180. // End playback at newEndPosition
  181. void CSentenceMixer::SetSampleEnd( int newEndPosition )
  182. {
  183. if ( m_pCurrentWordMixer )
  184. {
  185. m_pCurrentWordMixer->SetSampleEnd( newEndPosition );
  186. }
  187. }
  188. void CSentenceMixer::SetStartupDelaySamples( int delaySamples )
  189. {
  190. if ( m_pCurrentWordMixer )
  191. {
  192. m_pCurrentWordMixer->SetStartupDelaySamples( delaySamples );
  193. }
  194. }
  195. //-----------------------------------------------------------------------------
  196. // Purpose: Free a word
  197. //-----------------------------------------------------------------------------
  198. void CSentenceMixer::FreeWord( int nWord )
  199. {
  200. if ( m_pWordMixers[nWord] )
  201. {
  202. delete m_pWordMixers[nWord];
  203. m_pWordMixers[nWord] = NULL;
  204. }
  205. if ( m_VoxWords[nWord].sfx )
  206. {
  207. // If this wave wasn't precached by the game code
  208. if ( !m_VoxWords[nWord].fKeepCached )
  209. {
  210. // If this was the last mixer that had a reference
  211. if ( m_VoxWords[nWord].sfx->pSource->CanDelete() )
  212. {
  213. // free the source
  214. delete m_VoxWords[nWord].sfx->pSource;
  215. m_VoxWords[nWord].sfx->pSource = NULL;
  216. }
  217. }
  218. }
  219. }
  220. //-----------------------------------------------------------------------------
  221. // Purpose: Load a word
  222. //-----------------------------------------------------------------------------
  223. CAudioMixer *CSentenceMixer::LoadWord( int nWord )
  224. {
  225. CAudioMixer *pMixer = NULL;
  226. if ( m_VoxWords[nWord].sfx )
  227. {
  228. CAudioSource *pSource = S_LoadSound( m_VoxWords[nWord].sfx, NULL );
  229. if ( pSource )
  230. {
  231. pSource->SetSentenceWord( true );
  232. pMixer = pSource->CreateMixer();
  233. }
  234. }
  235. return pMixer;
  236. }
  237. float CSentenceMixer::ModifyPitch( float pitch )
  238. {
  239. if ( m_pCurrentWordMixer )
  240. {
  241. if ( m_VoxWords[m_currentWordIndex].pitch > 0 )
  242. {
  243. pitch += (m_VoxWords[m_currentWordIndex].pitch - 100) * 0.01f;
  244. }
  245. }
  246. return pitch;
  247. }
  248. float CSentenceMixer::GetVolumeScale( void )
  249. {
  250. if ( m_pCurrentWordMixer )
  251. {
  252. if ( m_VoxWords[m_currentWordIndex].volume )
  253. {
  254. float volume = m_VoxWords[m_currentWordIndex].volume * 0.01;
  255. if ( volume < 1.0f )
  256. return volume;
  257. }
  258. }
  259. return 1.0f;
  260. }
  261. int CSentenceMixer::SkipSamples( channel_t *pChannel, int sampleCount, int outputRate, int outputOffset )
  262. {
  263. Assert( 0 );
  264. return 0;
  265. }
  266. // return number of samples mixed
  267. int CSentenceMixer::MixDataToDevice( IAudioDevice *pDevice, channel_t *pChannel, int sampleCount, int outputRate, int outputOffset )
  268. {
  269. if ( !m_pCurrentWordMixer )
  270. {
  271. return 0;
  272. }
  273. // save this to compute total output
  274. int startingOffset = outputOffset;
  275. while ( sampleCount > 0 && m_pCurrentWordMixer )
  276. {
  277. int outputCount = m_pCurrentWordMixer->MixDataToDevice( pDevice, pChannel, sampleCount, outputRate, outputOffset );
  278. outputOffset += outputCount;
  279. sampleCount -= outputCount;
  280. if ( !m_pCurrentWordMixer->ShouldContinueMixing() )
  281. {
  282. bool bMouth = SND_IsMouth( pChannel );
  283. if ( bMouth )
  284. {
  285. SND_ClearMouth( pChannel );
  286. }
  287. // advance to next valid word mixer
  288. do
  289. {
  290. m_currentWordIndex++;
  291. if ( m_currentWordIndex >= m_nNumWords )
  292. {
  293. // end of sentence
  294. m_pCurrentWordMixer = NULL;
  295. break;
  296. }
  297. m_pCurrentWordMixer = m_pWordMixers[m_currentWordIndex];
  298. }
  299. while ( m_pCurrentWordMixer == NULL );
  300. if ( m_pCurrentWordMixer )
  301. {
  302. m_bNewWord = true;
  303. pChannel->sfx = m_VoxWords[m_currentWordIndex].sfx;
  304. if ( bMouth )
  305. {
  306. SND_UpdateMouth( pChannel );
  307. }
  308. if ( !IsReadyToMix() )
  309. {
  310. // current word isn't ready, stop mixing
  311. break;
  312. }
  313. }
  314. }
  315. }
  316. return outputOffset - startingOffset;
  317. }