PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/ClassMaster2014/barahon/Cultivation_9_UnixSource/game2/gameSource/sound/SoundPlayer.cpp

https://gitlab.com/garheade/linux_camp
C++ | 584 lines | 308 code | 182 blank | 94 comment | 41 complexity | 597b1f370b34fbc3e533752f79cb9487 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /*
  2. * Modification History
  3. *
  4. * 2004-July-17 Jason Rohrer
  5. * Created.
  6. *
  7. * 2004-July-21 Jason Rohrer
  8. * Changed to use a callback to reduce latency.
  9. *
  10. * 2004-August-9 Jason Rohrer
  11. * Added a limit on the number of simultaneous sounds.
  12. *
  13. * 2004-August-12 Jason Rohrer
  14. * Parameterized the sample rate.
  15. *
  16. * 2004-August-13 Jason Rohrer
  17. * Added Mutex to protect members that are accessed by multiple threads.
  18. *
  19. * 2004-August-14 Jason Rohrer
  20. * Changed to fade sounds out before dropping them.
  21. *
  22. * 2004-August-15 Jason Rohrer
  23. * Added a function for getting the sample rate.
  24. * Added volume modifier parameter to playSoundNow function.
  25. *
  26. * 2004-August-20 Jason Rohrer
  27. * Added priority flags.
  28. *
  29. * 2004-August-23 Jason Rohrer
  30. * Added music.
  31. *
  32. * 2004-August-25 Jason Rohrer
  33. * Added lock in setMusicPlayer function.
  34. * Added function for setting music loudness.
  35. *
  36. * 2004-August-31 Jason Rohrer
  37. * Added function for removing filters.
  38. *
  39. * 2006-September-19 Jason Rohrer
  40. * Added global loudness and fade-in.
  41. *
  42. * 2006-October-2 Jason Rohrer
  43. * Added a separate lock for music player to deal with deadlock.
  44. */
  45. #include "SoundPlayer.h"
  46. #include "MusicPlayer.h"
  47. #include <stdio.h>
  48. // callback passed into portaudio
  49. static int portaudioCallback( void *inputBuffer, void *outputBuffer,
  50. unsigned long framesPerBuffer,
  51. PaTimestamp outTime, void *userData ) {
  52. SoundPlayer *player = (SoundPlayer *)userData;
  53. player->getSamples( outputBuffer, framesPerBuffer );
  54. return 0;
  55. }
  56. /**
  57. * Class that wraps SoundSamples in a PlayableSound.
  58. */
  59. class SamplesPlayableSound : public PlayableSound {
  60. public:
  61. /**
  62. * Constructs a playable sound.
  63. *
  64. * @param inSamples the samples to play.
  65. * Must be destroyed by caller.
  66. */
  67. SamplesPlayableSound( SoundSamples *inSamples );
  68. ~SamplesPlayableSound();
  69. // implements the PlayableSound interface
  70. virtual SoundSamples *getMoreSamples( unsigned long inNumSamples );
  71. virtual PlayableSound *copy();
  72. protected:
  73. SoundSamples *mRemainingSamples;
  74. };
  75. SamplesPlayableSound::SamplesPlayableSound( SoundSamples *inSamples )
  76. : mRemainingSamples( new SoundSamples( inSamples ) ) {
  77. }
  78. SamplesPlayableSound::~SamplesPlayableSound() {
  79. delete mRemainingSamples;
  80. }
  81. SoundSamples *SamplesPlayableSound::getMoreSamples(
  82. unsigned long inNumSamples ) {
  83. SoundSamples *returnSamples = new SoundSamples( mRemainingSamples,
  84. inNumSamples );
  85. mRemainingSamples->trim( returnSamples->mSampleCount );
  86. return returnSamples;
  87. }
  88. PlayableSound *SamplesPlayableSound::copy() {
  89. return new SamplesPlayableSound( mRemainingSamples );
  90. }
  91. SoundPlayer::SoundPlayer( int inSampleRate,
  92. int inMaxSimultaneousRealtimeSounds,
  93. void *inMusicPlayer,
  94. double inMusicLoudness,
  95. double inGlobalLoudness )
  96. : mLock( new MutexLock() ),
  97. mMusicPlayerLock( new MutexLock() ),
  98. mSampleRate( inSampleRate ),
  99. mMaxSimultaneousRealtimeSounds( inMaxSimultaneousRealtimeSounds ),
  100. mMusicPlayer( inMusicPlayer ),
  101. mMusicLoudness( inMusicLoudness ),
  102. mGlobalLoudness( inGlobalLoudness ),
  103. mFadingIn( false ),
  104. mNumFadeFramesRemaining( 0 ),
  105. mRealtimeSounds( new SimpleVector<PlayableSound *>() ),
  106. mPriorityFlags( new SimpleVector<char>() ),
  107. mSoundLoudnessModifiers( new SimpleVector<double>() ),
  108. mSoundDroppedFlags( new SimpleVector<char>() ),
  109. mFilterChain( new SimpleVector<SoundFilter *>() ) {
  110. PaError error = Pa_Initialize();
  111. if( error == paNoError ) {
  112. error = Pa_OpenStream(
  113. &mAudioStream,
  114. paNoDevice,// default input device
  115. 0, // no input
  116. paFloat32, // 32 bit floating point input
  117. NULL,
  118. Pa_GetDefaultOutputDeviceID(),
  119. 2, // stereo output
  120. paFloat32, // 32 bit floating point output
  121. NULL,
  122. mSampleRate,
  123. 1024, // frames per buffer
  124. 0, // number of buffers, if zero then use default minimum
  125. paClipOff, // we won't output out of range samples so
  126. // don't bother clipping them
  127. portaudioCallback,
  128. (void *)this ); // pass self-pointer to callback function
  129. if( error == paNoError ) {
  130. error = Pa_StartStream( mAudioStream );
  131. if( error == paNoError ) {
  132. mAudioInitialized = true;
  133. }
  134. else {
  135. fprintf( stderr, "Error starting audio stream\n" );
  136. Pa_CloseStream( mAudioStream );
  137. }
  138. }
  139. else {
  140. fprintf( stderr, "Error opening audio stream\n" );
  141. Pa_Terminate();
  142. }
  143. }
  144. else {
  145. fprintf( stderr, "Error initializing audio framework\n" );
  146. }
  147. if( error != paNoError ) {
  148. fprintf( stderr, "Error number: %d\n", error );
  149. fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( error ) );
  150. mAudioInitialized = false;
  151. }
  152. }
  153. SoundPlayer::~SoundPlayer() {
  154. if( mAudioInitialized ) {
  155. PaError error = Pa_StopStream( mAudioStream );
  156. if( error == paNoError ) {
  157. error = Pa_CloseStream( mAudioStream );
  158. if( error != paNoError ) {
  159. fprintf( stderr, "Error closingaudio stream\n" );
  160. }
  161. }
  162. else {
  163. fprintf( stderr, "Error stopping audio stream\n" );
  164. }
  165. Pa_Terminate();
  166. if( error != paNoError ) {
  167. fprintf( stderr, "Error number: %d\n", error);
  168. fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( error) );
  169. }
  170. }
  171. delete mLock;
  172. delete mMusicPlayerLock;
  173. int i;
  174. int numSounds = mRealtimeSounds->size();
  175. for( i=0; i<numSounds; i++ ) {
  176. delete *( mRealtimeSounds->getElement( i ) );
  177. }
  178. delete mRealtimeSounds;
  179. delete mPriorityFlags;
  180. delete mSoundLoudnessModifiers;
  181. delete mSoundDroppedFlags;
  182. int numFilters = mFilterChain->size();
  183. for( i=0; i<numFilters; i++ ) {
  184. delete *( mFilterChain->getElement( i ) );
  185. }
  186. delete mFilterChain;
  187. }
  188. void SoundPlayer::setMusicPlayer( void *inMusicPlayer ) {
  189. mMusicPlayerLock->lock();
  190. mMusicPlayer = inMusicPlayer;
  191. mMusicPlayerLock->unlock();
  192. }
  193. void SoundPlayer::setMusicLoudness( double inMusicLoudness ) {
  194. mLock->lock();
  195. mMusicLoudness = inMusicLoudness;
  196. mLock->unlock();
  197. }
  198. void SoundPlayer::setGlobalLoudness( double inLoudness ) {
  199. mLock->lock();
  200. mGlobalLoudness = inLoudness;
  201. mLock->unlock();
  202. }
  203. void SoundPlayer::fadeIn( double inFadeTimeInSeconds ) {
  204. mLock->lock();
  205. mFadingIn = true;
  206. mNumFadeFramesRemaining = (int)( mSampleRate * inFadeTimeInSeconds );
  207. mLock->unlock();
  208. }
  209. void SoundPlayer::getSamples( void *outputBuffer,
  210. unsigned long inFramesInBuffer ) {
  211. SoundSamples *mixingBuffer = new SoundSamples( inFramesInBuffer );
  212. unsigned long bufferLength = inFramesInBuffer;
  213. mLock->lock();
  214. // add each pending realtime sound to the buffer
  215. int i = 0;
  216. // we may be removing sounds from the buffer as we use them up
  217. // i is adjusted inside the while loop
  218. while( i<mRealtimeSounds->size() ) {
  219. PlayableSound *realtimeSound = *( mRealtimeSounds->getElement( i ) );
  220. double loudnessModifier =
  221. *( mSoundLoudnessModifiers->getElement( i ) );
  222. SoundSamples *samples = realtimeSound->getMoreSamples( bufferLength );
  223. unsigned long mixLength = samples->mSampleCount;
  224. char shouldDrop = *( mSoundDroppedFlags->getElement( i ) );
  225. // fade out if we should drop
  226. float fadeFactor = 1;
  227. unsigned long maxJ = mixLength - 1;
  228. for( unsigned long j=0; j<mixLength; j++ ) {
  229. mixingBuffer->mLeftChannel[j] +=
  230. loudnessModifier * fadeFactor * samples->mLeftChannel[j];
  231. mixingBuffer->mRightChannel[j] +=
  232. loudnessModifier * fadeFactor * samples->mRightChannel[j];
  233. if( shouldDrop ) {
  234. fadeFactor = ( maxJ - j ) / (float)maxJ;
  235. }
  236. }
  237. delete samples;
  238. if( mixLength < bufferLength || shouldDrop ) {
  239. // we have used up all samples of this sound or
  240. // it is flagged to be dropped
  241. delete realtimeSound;
  242. mRealtimeSounds->deleteElement( i );
  243. mPriorityFlags->deleteElement( i );
  244. mSoundLoudnessModifiers->deleteElement( i );
  245. mSoundDroppedFlags->deleteElement( i );
  246. // don't increment i, since the next element drops into the current
  247. // index
  248. }
  249. else {
  250. // increment i to move on to the next sound
  251. i++;
  252. }
  253. }
  254. // Deadlock problem:
  255. // MusicPlayer locks the globalLock while it accesses music data from world
  256. // However, world thread, while it has globalLock locked, tries to set
  257. // the music volume, which requires locking our main lock mLock
  258. //
  259. // Bad order of events:
  260. // World locks globalLock
  261. // SoundPlayer locks mLock
  262. // SoundPlayer calls MusicPlayer to get samples
  263. // MusicPlayer waits for globalLock to get notes
  264. // World calls setMusicLoudness, waiting for SoundPlayer's mLock
  265. // => DEADLOCK
  266. // grab a local copy of music loudness before unlocking main lock
  267. double localMusicLoundless = mMusicLoudness;
  268. // unlock main lock while we access the music player
  269. mLock->unlock();
  270. // lock the specific lock
  271. mMusicPlayerLock->lock();
  272. if( mMusicPlayer != NULL ) {
  273. // cast out of void *
  274. MusicPlayer *player = (MusicPlayer *)mMusicPlayer;
  275. // mix in the music
  276. SoundSamples *musicSamples =
  277. player->getMoreMusic( inFramesInBuffer );
  278. for( unsigned long j=0; j<inFramesInBuffer; j++ ) {
  279. mixingBuffer->mLeftChannel[j] +=
  280. musicSamples->mLeftChannel[j] * localMusicLoundless;
  281. mixingBuffer->mRightChannel[j] +=
  282. musicSamples->mRightChannel[j] * localMusicLoundless;
  283. }
  284. delete musicSamples;
  285. }
  286. mMusicPlayerLock->unlock();
  287. // re-lock the main lock
  288. mLock->lock();
  289. // filter the samples
  290. int numFilters = mFilterChain->size();
  291. SoundSamples *filteredSamples = new SoundSamples( mixingBuffer );
  292. delete mixingBuffer;
  293. for( i=0; i<numFilters; i++ ) {
  294. SoundFilter *filter = *( mFilterChain->getElement( i ) );
  295. SoundSamples *nextOutput = filter->filterSamples( filteredSamples );
  296. delete filteredSamples;
  297. filteredSamples = nextOutput;
  298. }
  299. mLock->unlock();
  300. float *samples = (float *)outputBuffer;
  301. unsigned long numSamples = 2 * bufferLength;
  302. unsigned long j;
  303. unsigned long frameNumber = 0;
  304. // if fading in, adjust global volume as we go
  305. double globalLoudnessDelta = 0;
  306. if( mFadingIn ) {
  307. // want to reach loudness of 1 after mNumFadeFramesRemaining
  308. double loudnessChangeLeft = 1 - mGlobalLoudness;
  309. globalLoudnessDelta = loudnessChangeLeft / mNumFadeFramesRemaining;
  310. }
  311. for( j=0; j<numSamples; j+=2 ) {
  312. samples[j] =
  313. mGlobalLoudness * filteredSamples->mLeftChannel[frameNumber];
  314. samples[j+1] =
  315. mGlobalLoudness * filteredSamples->mRightChannel[frameNumber];
  316. frameNumber++;
  317. if( mFadingIn ) {
  318. mGlobalLoudness += globalLoudnessDelta;
  319. if( mGlobalLoudness >= 1 ) {
  320. // done
  321. mGlobalLoudness = 1;
  322. mFadingIn = false;
  323. mNumFadeFramesRemaining = 0;
  324. }
  325. }
  326. }
  327. if( mFadingIn ) {
  328. // not done fading in yet
  329. // update frames remaining
  330. mNumFadeFramesRemaining -= inFramesInBuffer;
  331. }
  332. delete filteredSamples;
  333. }
  334. void SoundPlayer::checkForExcessSounds() {
  335. int numSounds = mRealtimeSounds->size();
  336. if( numSounds > mMaxSimultaneousRealtimeSounds ) {
  337. // flag the oldest unflagged, low-priority sound
  338. // skip sounds that are already flagged or are priority sounds
  339. int i=0;
  340. while( i<numSounds &&
  341. ( *( mSoundDroppedFlags->getElement( i ) ) ||
  342. *( mPriorityFlags->getElement( i ) ) ) ) {
  343. i++;
  344. }
  345. if( i<numSounds ) {
  346. *( mSoundDroppedFlags->getElement( i ) ) = true;
  347. }
  348. else {
  349. // else all low-priority sounds are already flagged
  350. // try again, ignoring priority flags
  351. i=0;
  352. while( i<numSounds && *( mSoundDroppedFlags->getElement( i ) ) ) {
  353. i++;
  354. }
  355. if( i<numSounds ) {
  356. *( mSoundDroppedFlags->getElement( i ) ) = true;
  357. }
  358. // else all sounds are already flagged
  359. }
  360. }
  361. }
  362. void SoundPlayer::playSoundNow( SoundSamples *inSamples,
  363. char inPriorityFlag,
  364. double inLoudnessModifier ) {
  365. mLock->lock();
  366. mRealtimeSounds->push_back( new SamplesPlayableSound( inSamples ) );
  367. mPriorityFlags->push_back( inPriorityFlag );
  368. mSoundLoudnessModifiers->push_back( inLoudnessModifier );
  369. mSoundDroppedFlags->push_back( false );
  370. checkForExcessSounds();
  371. mLock->unlock();
  372. }
  373. void SoundPlayer::playSoundNow( PlayableSound *inSound,
  374. char inPriorityFlag,
  375. double inLoudnessModifier ) {
  376. mLock->lock();
  377. mRealtimeSounds->push_back( inSound->copy() );
  378. mPriorityFlags->push_back( inPriorityFlag );
  379. mSoundLoudnessModifiers->push_back( inLoudnessModifier );
  380. mSoundDroppedFlags->push_back( false );
  381. checkForExcessSounds();
  382. mLock->unlock();
  383. }
  384. void SoundPlayer::addMoreMusic( SoundSamples *inSamples ) {
  385. }
  386. void SoundPlayer::addFilter( SoundFilter *inFilter ) {
  387. mLock->lock();
  388. mFilterChain->push_back( inFilter );
  389. mLock->unlock();
  390. }
  391. void SoundPlayer::removeAllFilters() {
  392. mLock->lock();
  393. int numFilters = mFilterChain->size();
  394. for( int i=0; i<numFilters; i++ ) {
  395. delete *( mFilterChain->getElement( i ) );
  396. }
  397. mFilterChain->deleteAll();
  398. mLock->unlock();
  399. }
  400. unsigned long SoundPlayer::getSampleRate() {
  401. return mSampleRate;
  402. }