Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

370 lines
8.8 KiB

  1. //========= Copyright � 1996-2005, 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( 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 bool IsSetSampleStartSupported() const;
  30. virtual void SetSampleStart( int newPosition );
  31. virtual void SetSampleEnd( int newEndPosition );
  32. virtual void SetStartupDelaySamples( int delaySamples );
  33. virtual int GetMixSampleSize() { return m_pCurrentWordMixer ? m_pCurrentWordMixer->GetMixSampleSize() : 0; }
  34. virtual bool IsReadyToMix();
  35. virtual int GetPositionForSave() { return GetSamplePosition(); }
  36. virtual void SetPositionFromSaved( int savedPosition ) { SetSampleStart( savedPosition ); }
  37. private:
  38. CAudioMixer *LoadWord( int nWordIndex );
  39. void FreeWord( int nWordIndex );
  40. // identifies the active word
  41. int m_currentWordIndex;
  42. CAudioMixer *m_pCurrentWordMixer;
  43. // set when a transition to a new word occurs
  44. bool m_bNewWord;
  45. voxword_t m_VoxWords[CVOXWORDMAX];
  46. CAudioMixer *m_pWordMixers[CVOXWORDMAX];
  47. int m_nNumWords;
  48. };
  49. CAudioMixer *CreateSentenceMixer( voxword_t *pWords )
  50. {
  51. if ( pWords )
  52. {
  53. return new CSentenceMixer( pWords );
  54. }
  55. return NULL;
  56. }
  57. CSentenceMixer::CSentenceMixer( voxword_t *pWords )
  58. {
  59. // count the expected number of words
  60. m_nNumWords = 0;
  61. while ( pWords[m_nNumWords].sfx != NULL )
  62. {
  63. // get a private copy of the words
  64. m_VoxWords[m_nNumWords] = pWords[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. bool CSentenceMixer::IsSetSampleStartSupported() const
  174. {
  175. return true;
  176. }
  177. void CSentenceMixer::SetSampleStart( int newPosition )
  178. {
  179. if ( m_pCurrentWordMixer )
  180. {
  181. m_pCurrentWordMixer->SetSampleStart( newPosition );
  182. }
  183. }
  184. // End playback at newEndPosition
  185. void CSentenceMixer::SetSampleEnd( int newEndPosition )
  186. {
  187. if ( m_pCurrentWordMixer )
  188. {
  189. m_pCurrentWordMixer->SetSampleEnd( newEndPosition );
  190. }
  191. }
  192. void CSentenceMixer::SetStartupDelaySamples( int delaySamples )
  193. {
  194. if ( m_pCurrentWordMixer )
  195. {
  196. m_pCurrentWordMixer->SetStartupDelaySamples( delaySamples );
  197. }
  198. }
  199. //-----------------------------------------------------------------------------
  200. // Purpose: Free a word
  201. //-----------------------------------------------------------------------------
  202. void CSentenceMixer::FreeWord( int nWord )
  203. {
  204. if ( m_pWordMixers[nWord] )
  205. {
  206. delete m_pWordMixers[nWord];
  207. m_pWordMixers[nWord] = NULL;
  208. }
  209. if ( m_VoxWords[nWord].sfx )
  210. {
  211. // If this wave wasn't precached by the game code
  212. if ( !m_VoxWords[nWord].fKeepCached )
  213. {
  214. // If this was the last mixer that had a reference
  215. if ( m_VoxWords[nWord].sfx->pSource->CanDelete() )
  216. {
  217. // free the source
  218. delete m_VoxWords[nWord].sfx->pSource;
  219. m_VoxWords[nWord].sfx->pSource = NULL;
  220. }
  221. }
  222. }
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Purpose: Load a word
  226. //-----------------------------------------------------------------------------
  227. CAudioMixer *CSentenceMixer::LoadWord( int nWord )
  228. {
  229. CAudioMixer *pMixer = NULL;
  230. if ( m_VoxWords[nWord].sfx )
  231. {
  232. SoundError soundError;
  233. CAudioSource *pSource = S_LoadSound( m_VoxWords[nWord].sfx, NULL, soundError );
  234. if ( pSource )
  235. {
  236. pSource->SetSentenceWord( true );
  237. SoundError soundError;
  238. pMixer = pSource->CreateMixer( 0, 0, false, soundError, nullptr );
  239. }
  240. }
  241. return pMixer;
  242. }
  243. float CSentenceMixer::ModifyPitch( float pitch )
  244. {
  245. if ( m_pCurrentWordMixer )
  246. {
  247. if ( m_VoxWords[m_currentWordIndex].pitch > 0 )
  248. {
  249. pitch += (m_VoxWords[m_currentWordIndex].pitch - 100) * 0.01f;
  250. }
  251. }
  252. return pitch;
  253. }
  254. float CSentenceMixer::GetVolumeScale( void )
  255. {
  256. if ( m_pCurrentWordMixer )
  257. {
  258. if ( m_VoxWords[m_currentWordIndex].volume )
  259. {
  260. float volume = m_VoxWords[m_currentWordIndex].volume * 0.01;
  261. if ( volume < 1.0f )
  262. return volume;
  263. }
  264. }
  265. return 1.0f;
  266. }
  267. int CSentenceMixer::SkipSamples( channel_t *pChannel, int sampleCount, int outputRate, int outputOffset )
  268. {
  269. Assert( 0 );
  270. return 0;
  271. }
  272. // return number of samples mixed
  273. int CSentenceMixer::MixDataToDevice( channel_t *pChannel, int sampleCount, int outputRate, int outputOffset )
  274. {
  275. if ( !m_pCurrentWordMixer )
  276. {
  277. return 0;
  278. }
  279. // save this to compute total output
  280. int startingOffset = outputOffset;
  281. while ( sampleCount > 0 && m_pCurrentWordMixer )
  282. {
  283. int outputCount = m_pCurrentWordMixer->MixDataToDevice( pChannel, sampleCount, outputRate, outputOffset );
  284. outputOffset += outputCount;
  285. sampleCount -= outputCount;
  286. if ( !m_pCurrentWordMixer->ShouldContinueMixing() )
  287. {
  288. if ( pChannel->flags.m_bHasMouth )
  289. {
  290. SND_ClearMouth( pChannel );
  291. }
  292. // advance to next valid word mixer
  293. do
  294. {
  295. m_currentWordIndex++;
  296. if ( m_currentWordIndex >= m_nNumWords )
  297. {
  298. // end of sentence
  299. m_pCurrentWordMixer = NULL;
  300. break;
  301. }
  302. m_pCurrentWordMixer = m_pWordMixers[m_currentWordIndex];
  303. }
  304. while ( m_pCurrentWordMixer == NULL );
  305. if ( m_pCurrentWordMixer )
  306. {
  307. m_bNewWord = true;
  308. pChannel->sfx = m_VoxWords[m_currentWordIndex].sfx;
  309. if ( !IsReadyToMix() )
  310. {
  311. // current word isn't ready, stop mixing
  312. break;
  313. }
  314. }
  315. }
  316. }
  317. return outputOffset - startingOffset;
  318. }