sfxInternal.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #ifndef _SFXINTERNAL_H_
  23. #define _SFXINTERNAL_H_
  24. #ifndef _THREADPOOL_H_
  25. #include "platform/threads/threadPool.h"
  26. #endif
  27. #ifndef _ASYNCUPDATE_H_
  28. #include "platform/async/asyncUpdate.h"
  29. #endif
  30. #ifndef _ASYNCPACKETSTREAM_H_
  31. #include "platform/async/asyncPacketStream.h"
  32. #endif
  33. #ifndef _ASYNCPACKETQUEUE_H_
  34. #include "platform/async/asyncPacketQueue.h"
  35. #endif
  36. #ifndef _SFXSTREAM_H_
  37. #include "sfx/sfxStream.h"
  38. #endif
  39. #ifndef _SFXBUFFER_H_
  40. #include "sfx/sfxBuffer.h"
  41. #endif
  42. #ifndef _SFXVOICE_H_
  43. #include "sfx/sfxVoice.h"
  44. #endif
  45. #ifndef _CONSOLE_H_
  46. #include "console/console.h"
  47. #endif
  48. #ifndef _TSINGLETON_H_
  49. #include "core/util/tSingleton.h"
  50. #endif
  51. /// @file
  52. /// Mostly internal definitions for sound stream handling.
  53. /// The code here is used by SFXBuffer for asynchronously loading
  54. /// sample data from sound files, both for streaming buffers
  55. /// as well as for "normal" buffers.
  56. ///
  57. /// This is all pretty low-level code here.
  58. namespace SFXInternal {
  59. typedef AsyncUpdateThread SFXUpdateThread;
  60. typedef AsyncUpdateList SFXBufferProcessList;
  61. //--------------------------------------------------------------------------
  62. // Async sound packets.
  63. //--------------------------------------------------------------------------
  64. /// Sound stream packets are raw byte buffers containing PCM sample data.
  65. class SFXStreamPacket : public AsyncPacket< U8 >
  66. {
  67. public:
  68. typedef AsyncPacket< U8 > Parent;
  69. SFXStreamPacket() {}
  70. SFXStreamPacket( U8* data, U32 size, bool ownMemory = false )
  71. : Parent( data, size, ownMemory ) {}
  72. /// The format of the sound samples in the packet.
  73. SFXFormat mFormat;
  74. /// @return the number of samples contained in the packet.
  75. U32 getSampleCount() const { return ( mSizeActual / mFormat.getBytesPerSample() ); }
  76. };
  77. //--------------------------------------------------------------------------
  78. // Async SFXStream I/O.
  79. //--------------------------------------------------------------------------
  80. /// Asynchronous sound data stream that delivers sound data
  81. /// in discrete packets.
  82. class SFXAsyncStream : public AsyncPacketBufferedInputStream< SFXStreamRef, SFXStreamPacket >
  83. {
  84. public:
  85. typedef AsyncPacketBufferedInputStream< SFXStreamRef, SFXStreamPacket > Parent;
  86. enum
  87. {
  88. /// The number of seconds of sample data to load per streaming packet by default.
  89. /// Set this reasonably high to ensure the system is able to cope with latencies
  90. /// in the buffer update chain.
  91. DEFAULT_STREAM_PACKET_LENGTH = 8
  92. };
  93. protected:
  94. /// If true, the stream reads one packet of silence beyond the
  95. /// sound streams actual sound data. This is to avoid wrap-around
  96. /// playback queues running into old data when there is a delay
  97. /// in playback being stopped.
  98. ///
  99. /// @note The silence packet is <em>not</em> counting towards stream
  100. /// playback time.
  101. bool mReadSilenceAtEnd;
  102. // AsyncPacketStream.
  103. virtual SFXStreamPacket* _newPacket( U32 packetSize )
  104. {
  105. SFXStreamPacket* packet = Parent::_newPacket( packetSize );
  106. packet->mFormat = getSourceStream()->getFormat();
  107. return packet;
  108. }
  109. virtual void _requestNext();
  110. virtual void _onArrival( SFXStreamPacket* const& packet );
  111. virtual void _newReadItem( PacketReadItemRef& outRef, SFXStreamPacket* packet, U32 numElements )
  112. {
  113. if( !this->mNumRemainingSourceElements && mReadSilenceAtEnd )
  114. packet->mIsLast = false;
  115. Parent::_newReadItem( outRef, packet, numElements );
  116. }
  117. public:
  118. /// Construct a new async sound stream reading data from "stream".
  119. ///
  120. /// @param stream The sound data source stream.
  121. /// @param isIncremental If true, "stream" is read in packets of "streamPacketLength" size
  122. /// each; otherwise the stream is read in a single packet containing the entire stream.
  123. /// @param streamPacketLength Seconds of sample data to read per streaming packet. Only
  124. /// relevant if "isIncremental" is true.
  125. /// @param numReadAhead Number of stream packets to read and buffer in advance.
  126. /// @param isLooping If true, the packet stream infinitely loops over "stream".
  127. SFXAsyncStream( const SFXStreamRef& stream,
  128. bool isIncremental,
  129. U32 streamPacketLength = DEFAULT_STREAM_PACKET_LENGTH,
  130. U32 numReadAhead = DEFAULT_STREAM_LOOKAHEAD,
  131. bool isLooping = false );
  132. /// Returns true if the stream will read a packet of silence after the actual sound data.
  133. U32 getReadSilenceAtEnd() const { return mReadSilenceAtEnd; }
  134. /// Set whether the stream should read one packet of silence past the
  135. /// actual sound data. This is useful for situations where continued
  136. /// playback may run into old data.
  137. void setReadSilenceAtEnd( bool value ) { mReadSilenceAtEnd = value; }
  138. /// Return the playback time of a single sound packet in milliseconds.
  139. /// For non-incremental streams, this will be the duration of the
  140. /// entire stream.
  141. U32 getPacketDuration() const
  142. {
  143. const SFXFormat& format = getSourceStream()->getFormat();
  144. return format.getDuration( mPacketSize / format.getBytesPerSample() );
  145. }
  146. };
  147. //--------------------------------------------------------------------------
  148. // Voice time source wrapper.
  149. //--------------------------------------------------------------------------
  150. /// Wrapper around SFXVoice that yields the raw underlying sample position
  151. /// rather than the virtualized position returned by SFXVoice::getPosition().
  152. class SFXVoiceTimeSource
  153. {
  154. public:
  155. typedef void Parent;
  156. protected:
  157. /// The voice to sample the position from.
  158. SFXVoice* mVoice;
  159. /// Last position returned by voice.
  160. mutable U32 mLastPos;
  161. public:
  162. SFXVoiceTimeSource( SFXVoice* voice )
  163. : mVoice( voice ), mLastPos( 0 ) {}
  164. U32 getPosition() const
  165. {
  166. U32 samplePos = mVoice->_tell();
  167. // The device playback cursor may snap back to an undefined value as soon
  168. // as all the data has been consumed. However, for us to be a reliable
  169. // time source, we can't let that happen so as soon as the device play cursor
  170. // goes back to a sample position we have already passed, we start reporting an
  171. // end-of-stream position.
  172. if( samplePos < mLastPos && mVoice->mBuffer != NULL )
  173. samplePos = mVoice->mBuffer->getNumSamples();
  174. else
  175. mLastPos = samplePos;
  176. return samplePos;
  177. }
  178. };
  179. //--------------------------------------------------------------------------
  180. // Async sound packet queue.
  181. //--------------------------------------------------------------------------
  182. /// An async stream queue that writes sound packets to SFXBuffers in sync
  183. /// to the playback of an SFXVoice.
  184. ///
  185. /// Sound packet queues use sample counts as tick counts.
  186. class SFXAsyncQueue : public AsyncPacketQueue< SFXStreamPacket*, SFXVoiceTimeSource, SFXBuffer* >
  187. {
  188. public:
  189. typedef AsyncPacketQueue< SFXStreamPacket*, SFXVoiceTimeSource, SFXBuffer* > Parent;
  190. enum
  191. {
  192. /// The number of stream packets that the playback queue for streaming
  193. /// sounds will be sliced into. This should generally be left at
  194. /// three since there is an overhead incurred for each additional
  195. /// segment. Having three segments gives one segment for current
  196. /// immediate playback, one segment as intermediate buffer, and one segment
  197. /// for stream writes.
  198. DEFAULT_STREAM_QUEUE_LENGTH = 3,
  199. };
  200. /// Construct a new sound queue that pushes sound packets to "buffer" in sync
  201. /// to the playback of "voice".
  202. ///
  203. /// @param voice The SFXVoice to synchronize to.
  204. /// @param buffer The sound buffer to push sound packets to.
  205. SFXAsyncQueue( SFXVoice* voice,
  206. SFXBuffer* buffer,
  207. bool looping = false )
  208. : Parent( DEFAULT_STREAM_QUEUE_LENGTH,
  209. voice,
  210. buffer,
  211. ( looping
  212. ? 0
  213. : ( buffer->getDuration() * ( buffer->getFormat().getSamplesPerSecond() / 1000 ) ) - voice->mOffset ) ) {}
  214. };
  215. //--------------------------------------------------------------------------
  216. // SFXBuffer with a wrap-around buffering scheme.
  217. //--------------------------------------------------------------------------
  218. /// Buffer that uses wrap-around packet buffering.
  219. ///
  220. /// This class automatically coordinates retrieval and submitting of
  221. /// sound packets and also protects against play cursors running beyond
  222. /// the last packet by making sure some silence is submitted after the
  223. /// last packet (does not count towards playback time).
  224. ///
  225. /// @note Note that the reaction times of this class depend on the SFXDevice
  226. /// triggering timely SFXBuffer:update() calls.
  227. class SFXWrapAroundBuffer : public SFXBuffer
  228. {
  229. public:
  230. typedef SFXBuffer Parent;
  231. protected:
  232. /// Absolute byte offset into the sound stream that the next packet write
  233. /// will occur at. This is <em>not</em> an offset into the device buffer
  234. /// in order to allow us to track how far in the source stream we are.
  235. U32 mWriteOffset;
  236. /// Size of the device buffer in bytes.
  237. U32 mBufferSize;
  238. // SFXBuffer.
  239. virtual void _flush()
  240. {
  241. mWriteOffset = 0;
  242. }
  243. /// Copy "length" bytes from "data" into the device at "offset".
  244. virtual bool _copyData( U32 offset, const U8* data, U32 length ) = 0;
  245. // SFXBuffer.
  246. virtual void write( SFXStreamPacket* const* packets, U32 num );
  247. /// @return the sample position in the sound stream as determined from the
  248. /// given buffer offset.
  249. U32 getSamplePos( U32 bufferOffset ) const
  250. {
  251. if( !mBufferSize )
  252. return ( bufferOffset / getFormat().getBytesPerSample() );
  253. const U32 writeOffset = mWriteOffset; // Concurrent writes on this one.
  254. const U32 writeOffsetRelative = writeOffset % mBufferSize;
  255. U32 numBufferedBytes;
  256. if( !writeOffset )
  257. numBufferedBytes = 0;
  258. else if( writeOffsetRelative > bufferOffset )
  259. numBufferedBytes = writeOffsetRelative - bufferOffset;
  260. else
  261. // Wrap-around.
  262. numBufferedBytes = mBufferSize - bufferOffset + writeOffsetRelative;
  263. const U32 bytePos = writeOffset - numBufferedBytes;
  264. return ( bytePos / getFormat().getBytesPerSample() );
  265. }
  266. public:
  267. SFXWrapAroundBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description );
  268. SFXWrapAroundBuffer( SFXDescription* description )
  269. : Parent( description ), mBufferSize( 0 ), mWriteOffset(0) {}
  270. virtual U32 getMemoryUsed() const { return mBufferSize; }
  271. };
  272. //--------------------------------------------------------------------------
  273. // Global state.
  274. //--------------------------------------------------------------------------
  275. enum
  276. {
  277. /// Soft limit on milliseconds to spend on updating sound buffers
  278. /// when doing buffer updates on the main thread.
  279. MAIN_THREAD_PROCESS_TIMEOUT = 512,
  280. /// Default time interval between periodic sound updates in milliseconds.
  281. /// Only relevant for devices that perform periodic updates.
  282. DEFAULT_UPDATE_INTERVAL = 512,
  283. };
  284. /// Thread pool for sound I/O.
  285. ///
  286. /// We are using a separate pool for sound packets in order to be
  287. /// able to submit packet items from different threads. This would
  288. /// violate the invariant of the global thread pool that only the
  289. /// main thread is feeding the queues.
  290. ///
  291. /// Note that this also means that only at certain very well-defined
  292. /// points is it possible to safely flush the work item queue on this
  293. /// pool.
  294. ///
  295. /// @note Don't use this directly but rather use THREAD_POOL() instead.
  296. /// This way, the sound code may be easily switched to using a common
  297. /// pool later on.
  298. class SFXThreadPool : public ThreadPool, public ManagedSingleton< SFXThreadPool >
  299. {
  300. public:
  301. typedef ThreadPool Parent;
  302. /// Create a ThreadPool called "SFX" with two threads.
  303. SFXThreadPool()
  304. : Parent( "SFX", 2 ) {}
  305. // For ManagedSingleton.
  306. static const char* getSingletonName() { return "SFXThreadPool"; }
  307. };
  308. /// Dedicated thread that does sound buffer updates.
  309. /// May be NULL if sound API used does not do asynchronous buffer
  310. /// updates but rather uses per-frame polling.
  311. ///
  312. /// @note SFXDevice automatically polls if this is NULL.
  313. extern ThreadSafeRef< AsyncUpdateThread > gUpdateThread;
  314. /// List of buffers that need updating.
  315. ///
  316. /// It depends on the actual device whether this list is processed
  317. /// on a stream update thread or on the main thread.
  318. extern ThreadSafeRef< SFXBufferProcessList > gBufferUpdateList;
  319. /// List of buffers that are pending deletion.
  320. ///
  321. /// This is a messy issue. Buffers with live async states cannot be instantly
  322. /// deleted since they may still be running concurrent updates. However, they
  323. /// also cannot be deleted on the update thread since the StrongRefBase stuff
  324. /// isn't thread-safe (i.e weak references kept by client code would cause trouble).
  325. ///
  326. /// So, what we do is mark buffers for deletion, wait till they surface on the
  327. /// process list and then ping them back to this list to have them deleted by the
  328. /// SFXDevice itself on the main thread. A bit of overhead but only a fraction of
  329. /// the buffers will ever undergo this procedure.
  330. extern ThreadSafeDeque< SFXBuffer* > gDeadBufferList;
  331. /// Return the thread pool used for SFX work.
  332. inline ThreadPool& THREAD_POOL()
  333. {
  334. return *( SFXThreadPool::instance() );
  335. }
  336. /// Return the dedicated SFX update thread; NULL if updating on the main thread.
  337. inline ThreadSafeRef< SFXUpdateThread > UPDATE_THREAD()
  338. {
  339. return gUpdateThread;
  340. }
  341. /// Return the processing list for SFXBuffers that need updating.
  342. inline SFXBufferProcessList& UPDATE_LIST()
  343. {
  344. return *gBufferUpdateList;
  345. }
  346. /// Trigger an SFX update.
  347. inline bool TriggerUpdate()
  348. {
  349. ThreadSafeRef< SFXUpdateThread > sfxThread = UPDATE_THREAD();
  350. if( sfxThread != NULL )
  351. {
  352. sfxThread->triggerUpdate();
  353. return true;
  354. }
  355. else
  356. return false;
  357. }
  358. /// Delete all buffers currently on the dead buffer list.
  359. inline void PurgeDeadBuffers()
  360. {
  361. SFXBuffer* buffer;
  362. while( gDeadBufferList.tryPopFront( buffer ) )
  363. delete buffer;
  364. }
  365. /// Return true if the current thread is the one responsible for doing SFX updates.
  366. inline bool isSFXThread()
  367. {
  368. ThreadSafeRef< SFXUpdateThread > sfxThread = UPDATE_THREAD();
  369. dsize_t threadId;
  370. if( sfxThread != NULL )
  371. threadId = sfxThread->getId();
  372. else
  373. threadId = ThreadManager::getMainThreadId();
  374. return ThreadManager::compare( ThreadManager::getCurrentThreadId(), threadId );
  375. }
  376. } // namespace SFXInternal
  377. #endif // !_SFXINTERNAL_H_