BsOAAudioSource.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsOAAudioSource.h"
  4. #include "BsOAAudio.h"
  5. #include "BsOAAudioClip.h"
  6. #include "AL/al.h"
  7. namespace bs
  8. {
  9. OAAudioSource::OAAudioSource()
  10. : mSavedTime(0.0f), mSavedState(AudioSourceState::Stopped), mState(AudioSourceState::Stopped)
  11. , mGloballyPaused(false), mStreamBuffers(), mBusyBuffers(), mStreamProcessedPosition(0), mStreamQueuedPosition(0)
  12. , mIsStreaming(false)
  13. {
  14. gOAAudio()._registerSource(this);
  15. rebuild();
  16. }
  17. OAAudioSource::~OAAudioSource()
  18. {
  19. clear();
  20. gOAAudio()._unregisterSource(this);
  21. }
  22. void OAAudioSource::setClip(const HAudioClip& clip)
  23. {
  24. stop();
  25. Lock(mMutex);
  26. AudioSource::setClip(clip);
  27. applyClip();
  28. }
  29. void OAAudioSource::setPosition(const Vector3& position)
  30. {
  31. AudioSource::setPosition(position);
  32. auto& contexts = gOAAudio()._getContexts();
  33. UINT32 numContexts = (UINT32)contexts.size();
  34. for (UINT32 i = 0; i < numContexts; i++)
  35. {
  36. if (contexts.size() > 1)
  37. alcMakeContextCurrent(contexts[i]);
  38. if (is3D())
  39. alSource3f(mSourceIDs[i], AL_POSITION, position.x, position.y, position.z);
  40. else
  41. alSource3f(mSourceIDs[i], AL_POSITION, 0.0f, 0.0f, 0.0f);
  42. }
  43. }
  44. void OAAudioSource::setVelocity(const Vector3& velocity)
  45. {
  46. AudioSource::setVelocity(velocity);
  47. auto& contexts = gOAAudio()._getContexts();
  48. UINT32 numContexts = (UINT32)contexts.size();
  49. for (UINT32 i = 0; i < numContexts; i++)
  50. {
  51. if (contexts.size() > 1)
  52. alcMakeContextCurrent(contexts[i]);
  53. if (is3D())
  54. alSource3f(mSourceIDs[i], AL_VELOCITY, velocity.x, velocity.y, velocity.z);
  55. else
  56. alSource3f(mSourceIDs[i], AL_VELOCITY, 0.0f, 0.0f, 0.0f);
  57. }
  58. }
  59. void OAAudioSource::setVolume(float volume)
  60. {
  61. AudioSource::setVolume(volume);
  62. auto& contexts = gOAAudio()._getContexts();
  63. UINT32 numContexts = (UINT32)contexts.size();
  64. for (UINT32 i = 0; i < numContexts; i++)
  65. {
  66. if (contexts.size() > 1)
  67. alcMakeContextCurrent(contexts[i]);
  68. alSourcef(mSourceIDs[i], AL_GAIN, mVolume);
  69. }
  70. }
  71. void OAAudioSource::setPitch(float pitch)
  72. {
  73. AudioSource::setPitch(pitch);
  74. auto& contexts = gOAAudio()._getContexts();
  75. UINT32 numContexts = (UINT32)contexts.size();
  76. for (UINT32 i = 0; i < numContexts; i++)
  77. {
  78. if (contexts.size() > 1)
  79. alcMakeContextCurrent(contexts[i]);
  80. alSourcef(mSourceIDs[i], AL_PITCH, pitch);
  81. }
  82. }
  83. void OAAudioSource::setIsLooping(bool loop)
  84. {
  85. AudioSource::setIsLooping(loop);
  86. // When streaming we handle looping manually
  87. if (requiresStreaming())
  88. loop = false;
  89. auto& contexts = gOAAudio()._getContexts();
  90. UINT32 numContexts = (UINT32)contexts.size();
  91. for (UINT32 i = 0; i < numContexts; i++)
  92. {
  93. if (contexts.size() > 1)
  94. alcMakeContextCurrent(contexts[i]);
  95. alSourcef(mSourceIDs[i], AL_LOOPING, loop);
  96. }
  97. }
  98. void OAAudioSource::setPriority(INT32 priority)
  99. {
  100. AudioSource::setPriority(priority);
  101. // Do nothing, OpenAL doesn't support priorities (perhaps emulate the behaviour by manually disabling sources?)
  102. }
  103. void OAAudioSource::setMinDistance(float distance)
  104. {
  105. AudioSource::setMinDistance(distance);
  106. auto& contexts = gOAAudio()._getContexts();
  107. UINT32 numContexts = (UINT32)contexts.size();
  108. for (UINT32 i = 0; i < numContexts; i++)
  109. {
  110. if (contexts.size() > 1)
  111. alcMakeContextCurrent(contexts[i]);
  112. alSourcef(mSourceIDs[i], AL_REFERENCE_DISTANCE, distance);
  113. }
  114. }
  115. void OAAudioSource::setAttenuation(float attenuation)
  116. {
  117. AudioSource::setAttenuation(attenuation);
  118. auto& contexts = gOAAudio()._getContexts();
  119. UINT32 numContexts = (UINT32)contexts.size();
  120. for (UINT32 i = 0; i < numContexts; i++)
  121. {
  122. if (contexts.size() > 1)
  123. alcMakeContextCurrent(contexts[i]);
  124. alSourcef(mSourceIDs[i], AL_ROLLOFF_FACTOR, attenuation);
  125. }
  126. }
  127. void OAAudioSource::play()
  128. {
  129. mState = AudioSourceState::Playing;
  130. if (mGloballyPaused)
  131. return;
  132. if(requiresStreaming())
  133. {
  134. Lock(mMutex);
  135. if (!mIsStreaming)
  136. {
  137. startStreaming();
  138. stream(); // Stream first block on this thread to ensure something can play right away
  139. }
  140. }
  141. auto& contexts = gOAAudio()._getContexts();
  142. UINT32 numContexts = (UINT32)contexts.size();
  143. for (UINT32 i = 0; i < numContexts; i++)
  144. {
  145. if (contexts.size() > 1)
  146. alcMakeContextCurrent(contexts[i]);
  147. alSourcePlay(mSourceIDs[i]);
  148. // Non-3D clips need to play only on a single source
  149. // Note: I'm still creating sourcs objects (and possibly queuing streaming buffers) for these non-playing
  150. // sources. It would be possible to optimize them out at cost of more complexity. At this time it doesn't feel
  151. // worth it.
  152. if(!is3D())
  153. break;
  154. }
  155. }
  156. void OAAudioSource::pause()
  157. {
  158. mState = AudioSourceState::Paused;
  159. auto& contexts = gOAAudio()._getContexts();
  160. UINT32 numContexts = (UINT32)contexts.size();
  161. for (UINT32 i = 0; i < numContexts; i++)
  162. {
  163. if (contexts.size() > 1)
  164. alcMakeContextCurrent(contexts[i]);
  165. alSourcePause(mSourceIDs[i]);
  166. }
  167. }
  168. void OAAudioSource::stop()
  169. {
  170. mState = AudioSourceState::Stopped;
  171. auto& contexts = gOAAudio()._getContexts();
  172. UINT32 numContexts = (UINT32)contexts.size();
  173. for (UINT32 i = 0; i < numContexts; i++)
  174. {
  175. if (contexts.size() > 1)
  176. alcMakeContextCurrent(contexts[i]);
  177. alSourceStop(mSourceIDs[i]);
  178. alSourcef(mSourceIDs[i], AL_SEC_OFFSET, 0.0f);
  179. }
  180. {
  181. Lock(mMutex);
  182. mStreamProcessedPosition = 0;
  183. mStreamQueuedPosition = 0;
  184. if (mIsStreaming)
  185. stopStreaming();
  186. }
  187. }
  188. void OAAudioSource::setGlobalPause(bool pause)
  189. {
  190. if (mGloballyPaused == pause)
  191. return;
  192. mGloballyPaused = pause;
  193. if (getState() == AudioSourceState::Playing)
  194. {
  195. if (pause)
  196. {
  197. auto& contexts = gOAAudio()._getContexts();
  198. UINT32 numContexts = (UINT32)contexts.size();
  199. for (UINT32 i = 0; i < numContexts; i++)
  200. {
  201. if (contexts.size() > 1)
  202. alcMakeContextCurrent(contexts[i]);
  203. alSourcePause(mSourceIDs[i]);
  204. }
  205. }
  206. else
  207. {
  208. play();
  209. }
  210. }
  211. }
  212. void OAAudioSource::setTime(float time)
  213. {
  214. if (!mAudioClip.isLoaded())
  215. return;
  216. AudioSourceState state = getState();
  217. stop();
  218. bool needsStreaming = requiresStreaming();
  219. float clipTime;
  220. {
  221. Lock(mMutex);
  222. if (!needsStreaming)
  223. clipTime = time;
  224. else
  225. {
  226. if (mAudioClip.isLoaded())
  227. mStreamProcessedPosition = (UINT32)(time * mAudioClip->getFrequency() * mAudioClip->getNumChannels());
  228. else
  229. mStreamProcessedPosition = 0;
  230. mStreamQueuedPosition = mStreamProcessedPosition;
  231. clipTime = 0.0f;
  232. }
  233. }
  234. auto& contexts = gOAAudio()._getContexts();
  235. UINT32 numContexts = (UINT32)contexts.size();
  236. for (UINT32 i = 0; i < numContexts; i++)
  237. {
  238. if (contexts.size() > 1)
  239. alcMakeContextCurrent(contexts[i]);
  240. alSourcef(mSourceIDs[i], AL_SEC_OFFSET, clipTime);
  241. }
  242. if (state != AudioSourceState::Stopped)
  243. play();
  244. if (state == AudioSourceState::Paused)
  245. pause();
  246. }
  247. float OAAudioSource::getTime() const
  248. {
  249. Lock(mMutex);
  250. auto& contexts = gOAAudio()._getContexts();
  251. if (contexts.size() > 1)
  252. alcMakeContextCurrent(contexts[0]);
  253. bool needsStreaming = requiresStreaming();
  254. float time;
  255. if (!needsStreaming)
  256. {
  257. alGetSourcef(mSourceIDs[0], AL_SEC_OFFSET, &time);
  258. return time;
  259. }
  260. else
  261. {
  262. float timeOffset = 0.0f;
  263. if (mAudioClip.isLoaded())
  264. timeOffset = (float)mStreamProcessedPosition / mAudioClip->getFrequency() / mAudioClip->getNumChannels();
  265. // When streaming, the returned offset is relative to the last queued buffer
  266. alGetSourcef(mSourceIDs[0], AL_SEC_OFFSET, &time);
  267. return timeOffset + time;
  268. }
  269. }
  270. void OAAudioSource::clear()
  271. {
  272. mSavedState = getState();
  273. mSavedTime = getTime();
  274. stop();
  275. auto& contexts = gOAAudio()._getContexts();
  276. UINT32 numContexts = (UINT32)contexts.size();
  277. Lock(mMutex);
  278. for (UINT32 i = 0; i < numContexts; i++)
  279. {
  280. if (contexts.size() > 1)
  281. alcMakeContextCurrent(contexts[i]);
  282. alSourcei(mSourceIDs[i], AL_BUFFER, 0);
  283. alDeleteSources(1, &mSourceIDs[i]);
  284. }
  285. mSourceIDs.clear();
  286. }
  287. void OAAudioSource::rebuild()
  288. {
  289. auto& contexts = gOAAudio()._getContexts();
  290. UINT32 numContexts = (UINT32)contexts.size();
  291. {
  292. Lock(mMutex);
  293. for (UINT32 i = 0; i < numContexts; i++)
  294. {
  295. if (contexts.size() > 1)
  296. alcMakeContextCurrent(contexts[i]);
  297. UINT32 source = 0;
  298. alGenSources(1, &source);
  299. mSourceIDs.push_back(source);
  300. }
  301. }
  302. for (UINT32 i = 0; i < numContexts; i++)
  303. {
  304. if (contexts.size() > 1)
  305. alcMakeContextCurrent(contexts[i]);
  306. alSourcef(mSourceIDs[i], AL_PITCH, mPitch);
  307. alSourcef(mSourceIDs[i], AL_REFERENCE_DISTANCE, mMinDistance);
  308. alSourcef(mSourceIDs[i], AL_ROLLOFF_FACTOR, mAttenuation);
  309. if(requiresStreaming())
  310. alSourcef(mSourceIDs[i], AL_LOOPING, false);
  311. else
  312. alSourcef(mSourceIDs[i], AL_LOOPING, mLoop);
  313. if (is3D())
  314. {
  315. alSourcei(mSourceIDs[i], AL_SOURCE_RELATIVE, false);
  316. alSource3f(mSourceIDs[i], AL_POSITION, mPosition.x, mPosition.y, mPosition.z);
  317. alSource3f(mSourceIDs[i], AL_VELOCITY, mVelocity.x, mVelocity.y, mVelocity.z);
  318. }
  319. else
  320. {
  321. alSourcei(mSourceIDs[i], AL_SOURCE_RELATIVE, true);
  322. alSource3f(mSourceIDs[i], AL_POSITION, 0.0f, 0.0f, 0.0f);
  323. alSource3f(mSourceIDs[i], AL_VELOCITY, 0.0f, 0.0f, 0.0f);
  324. }
  325. {
  326. Lock(mMutex);
  327. if (!mIsStreaming)
  328. {
  329. UINT32 oaBuffer = 0;
  330. if (mAudioClip.isLoaded())
  331. {
  332. OAAudioClip* oaClip = static_cast<OAAudioClip*>(mAudioClip.get());
  333. oaBuffer = oaClip->_getOpenALBuffer();
  334. }
  335. alSourcei(mSourceIDs[i], AL_BUFFER, oaBuffer);
  336. }
  337. }
  338. }
  339. setTime(mSavedTime);
  340. if (mSavedState != AudioSourceState::Stopped)
  341. play();
  342. if (mSavedState == AudioSourceState::Paused)
  343. pause();
  344. }
  345. void OAAudioSource::startStreaming()
  346. {
  347. assert(!mIsStreaming);
  348. alGenBuffers(StreamBufferCount, mStreamBuffers);
  349. gOAAudio().startStreaming(this);
  350. memset(&mBusyBuffers, 0, sizeof(mBusyBuffers));
  351. mIsStreaming = true;
  352. }
  353. void OAAudioSource::stopStreaming()
  354. {
  355. assert(mIsStreaming);
  356. mIsStreaming = false;
  357. gOAAudio().stopStreaming(this);
  358. auto& contexts = gOAAudio()._getContexts();
  359. UINT32 numContexts = (UINT32)contexts.size();
  360. for (UINT32 i = 0; i < numContexts; i++)
  361. {
  362. if (contexts.size() > 1)
  363. alcMakeContextCurrent(contexts[i]);
  364. INT32 numQueuedBuffers;
  365. alGetSourcei(mSourceIDs[i], AL_BUFFERS_QUEUED, &numQueuedBuffers);
  366. UINT32 buffer;
  367. for (INT32 j = 0; j < numQueuedBuffers; j++)
  368. alSourceUnqueueBuffers(mSourceIDs[i], 1, &buffer);
  369. }
  370. alDeleteBuffers(StreamBufferCount, mStreamBuffers);
  371. }
  372. void OAAudioSource::stream()
  373. {
  374. Lock(mMutex);
  375. AudioDataInfo info;
  376. info.bitDepth = mAudioClip->getBitDepth();
  377. info.numChannels = mAudioClip->getNumChannels();
  378. info.sampleRate = mAudioClip->getFrequency();
  379. info.numSamples = 0;
  380. UINT32 totalNumSamples = mAudioClip->getNumSamples();
  381. // Note: It is safe to access contexts here only because it is guaranteed by the OAAudio manager that it will always
  382. // stop all streaming before changing contexts. Otherwise a mutex lock would be needed for every context access.
  383. auto& contexts = gOAAudio()._getContexts();
  384. UINT32 numContexts = (UINT32)contexts.size();
  385. for (UINT32 i = 0; i < numContexts; i++)
  386. {
  387. if (contexts.size() > 1)
  388. alcMakeContextCurrent(contexts[i]);
  389. INT32 numProcessedBuffers = 0;
  390. alGetSourcei(mSourceIDs[i], AL_BUFFERS_PROCESSED, &numProcessedBuffers);
  391. for (INT32 j = numProcessedBuffers; j > 0; j--)
  392. {
  393. UINT32 buffer;
  394. alSourceUnqueueBuffers(mSourceIDs[i], 1, &buffer);
  395. INT32 bufferIdx = -1;
  396. for (UINT32 k = 0; k < StreamBufferCount; k++)
  397. {
  398. if (buffer == mStreamBuffers[k])
  399. {
  400. bufferIdx = k;
  401. break;
  402. }
  403. }
  404. // Possibly some buffer from previous playback remained unqueued, in which case ignore it
  405. if (bufferIdx == -1)
  406. continue;
  407. mBusyBuffers[bufferIdx] &= ~(1 << bufferIdx);
  408. // Check if all sources are done with this buffer
  409. if (mBusyBuffers[bufferIdx] != 0)
  410. break;
  411. INT32 bufferSize;
  412. INT32 bufferBits;
  413. alGetBufferi(buffer, AL_SIZE, &bufferSize);
  414. alGetBufferi(buffer, AL_BITS, &bufferBits);
  415. if (bufferBits == 0)
  416. {
  417. LOGERR("Error decoding stream.");
  418. return;
  419. }
  420. else
  421. {
  422. UINT32 bytesPerSample = bufferBits / 8;
  423. mStreamProcessedPosition += bufferSize / bytesPerSample;
  424. }
  425. if (mStreamProcessedPosition == totalNumSamples) // Reached the end
  426. {
  427. mStreamProcessedPosition = 0;
  428. if (!mLoop) // Variable used on both threads and not thread safe, but it doesn't matter
  429. {
  430. stopStreaming();
  431. return;
  432. }
  433. }
  434. }
  435. }
  436. for(UINT32 i = 0; i < StreamBufferCount; i++)
  437. {
  438. if (mBusyBuffers[i] != 0)
  439. continue;
  440. if (fillBuffer(mStreamBuffers[i], info, totalNumSamples))
  441. {
  442. for (auto& source : mSourceIDs)
  443. alSourceQueueBuffers(source, 1, &mStreamBuffers[i]);
  444. mBusyBuffers[i] |= 1 << i;
  445. }
  446. else
  447. break;
  448. }
  449. }
  450. bool OAAudioSource::fillBuffer(UINT32 buffer, AudioDataInfo& info, UINT32 maxNumSamples)
  451. {
  452. UINT32 numRemainingSamples = maxNumSamples - mStreamQueuedPosition;
  453. if (numRemainingSamples == 0) // Reached the end
  454. {
  455. if (mLoop)
  456. {
  457. mStreamQueuedPosition = 0;
  458. numRemainingSamples = maxNumSamples;
  459. }
  460. else // If not looping, don't queue any more buffers, we're done
  461. return false;
  462. }
  463. // Read audio data
  464. UINT32 numSamples = std::min(numRemainingSamples, info.sampleRate * info.numChannels); // 1 second of data
  465. UINT32 sampleBufferSize = numSamples * (info.bitDepth / 8);
  466. UINT8* samples = (UINT8*)bs_stack_alloc(sampleBufferSize);
  467. OAAudioClip* audioClip = static_cast<OAAudioClip*>(mAudioClip.get());
  468. audioClip->getSamples(samples, mStreamQueuedPosition, numSamples);
  469. mStreamQueuedPosition += numSamples;
  470. info.numSamples = numSamples;
  471. gOAAudio()._writeToOpenALBuffer(buffer, samples, info);
  472. bs_stack_free(samples);
  473. return true;
  474. }
  475. void OAAudioSource::applyClip()
  476. {
  477. auto& contexts = gOAAudio()._getContexts();
  478. UINT32 numContexts = (UINT32)contexts.size();
  479. for (UINT32 i = 0; i < numContexts; i++)
  480. {
  481. if (contexts.size() > 1)
  482. alcMakeContextCurrent(contexts[i]);
  483. alSourcei(mSourceIDs[i], AL_SOURCE_RELATIVE, !is3D());
  484. if (!requiresStreaming())
  485. {
  486. UINT32 oaBuffer = 0;
  487. if (mAudioClip.isLoaded())
  488. {
  489. OAAudioClip* oaClip = static_cast<OAAudioClip*>(mAudioClip.get());
  490. oaBuffer = oaClip->_getOpenALBuffer();
  491. }
  492. alSourcei(mSourceIDs[i], AL_BUFFER, oaBuffer);
  493. }
  494. }
  495. // Looping is influenced by streaming mode, so re-apply it in case it changed
  496. setIsLooping(mLoop);
  497. }
  498. void OAAudioSource::onClipChanged()
  499. {
  500. AudioSourceState state = getState();
  501. float savedTime = getTime();
  502. stop();
  503. {
  504. Lock(mMutex);
  505. applyClip();
  506. }
  507. setTime(savedTime);
  508. if (state != AudioSourceState::Stopped)
  509. play();
  510. if (state == AudioSourceState::Paused)
  511. pause();
  512. }
  513. bool OAAudioSource::is3D() const
  514. {
  515. if (!mAudioClip.isLoaded())
  516. return true;
  517. return mAudioClip->is3D();
  518. }
  519. bool OAAudioSource::requiresStreaming() const
  520. {
  521. if (!mAudioClip.isLoaded())
  522. return false;
  523. AudioReadMode readMode = mAudioClip->getReadMode();
  524. bool isCompressed = readMode == AudioReadMode::LoadCompressed && mAudioClip->getFormat() != AudioFormat::PCM;
  525. return (readMode == AudioReadMode::Stream) || isCompressed;
  526. }
  527. }