sfxBuffer.cpp

Engine/source/sfx/sfxBuffer.cpp

More...

Detailed Description

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "sfx/sfxBuffer.h"
 25#include "sfx/sfxVoice.h"
 26#include "sfx/sfxDescription.h"
 27#include "sfx/sfxInternal.h"
 28
 29
 30//#define DEBUG_SPEW
 31
 32
 33Signal< void( SFXBuffer* ) > SFXBuffer::smBufferDestroyedSignal;
 34
 35
 36//-----------------------------------------------------------------------------
 37
 38SFXBuffer::SFXBuffer( const ThreadSafeRef< SFXStream>& stream, SFXDescription* description, bool createAsyncState )
 39   : mStatus( STATUS_Null ),
 40     mFormat( stream->getFormat() ),
 41     mDuration( stream->getDuration() ),
 42     mIsStreaming( description->mIsStreaming ),
 43     mIsLooping( description->mIsLooping ),
 44     mIsUnique( description->mIsStreaming ),
 45     mIsDead( false ),
 46     mUniqueVoice( NULL )
 47{
 48   using namespace SFXInternal;
 49   
 50   if( createAsyncState )
 51   {
 52      U32 packetLength = description->mStreamPacketSize;
 53      U32 readAhead = description->mStreamReadAhead;
 54
 55      ThreadSafeRef< SFXStream> streamRef( stream );
 56      mAsyncState = new AsyncState(
 57         new SFXAsyncStream
 58         ( streamRef, mIsStreaming, packetLength, readAhead,
 59           mIsStreaming ? description->mIsLooping : false ) );
 60   }
 61}
 62
 63//-----------------------------------------------------------------------------
 64
 65SFXBuffer::SFXBuffer( SFXDescription* description )
 66   : mStatus( STATUS_Ready ),
 67     mDuration( 0 ), // Must be set by subclass.
 68     mIsStreaming( false ), // Not streaming through our system.
 69     mIsLooping( description->mIsLooping ),
 70     mIsUnique( false ), // Must be set by subclass.
 71     mIsDead( false ),
 72     mUniqueVoice( NULL )
 73{
 74}
 75
 76//-----------------------------------------------------------------------------
 77
 78SFXBuffer::~SFXBuffer()
 79{
 80   smBufferDestroyedSignal.trigger( this );
 81}
 82
 83//-----------------------------------------------------------------------------
 84
 85void SFXBuffer::load()
 86{
 87   if( getStatus() == STATUS_Null )
 88   {
 89      AssertFatal( mAsyncState != NULL, "SFXBuffer::load() - no async state!" );
 90
 91      _setStatus( STATUS_Loading );
 92      SFXInternal::UPDATE_LIST().add( this );
 93      mAsyncState->mStream->start();
 94   }
 95}
 96
 97//-----------------------------------------------------------------------------
 98
 99bool SFXBuffer::update()
100{
101   using namespace SFXInternal;
102
103   if( isDead() )
104   {
105      // Buffer requested to finish its async operations.
106      // Kill our async state and put us on the dead buffer list.
107
108      mAsyncState->mStream->stop();
109      mAsyncState = NULL;
110      gDeadBufferList.pushFront( this );
111      return false;
112   }
113   else if( isAtEnd() && isStreaming() )
114   {
115      // Streaming buffers remain in the update loop even if they
116      // have played in full to allow for stream seeks.
117      return true;
118   }
119
120   AssertFatal( mAsyncState != NULL, "SFXBuffer::update() - async state has already been released" );
121
122   bool needFurtherUpdates = true;
123   if( !isStreaming() )
124   {
125      // Not a streaming buffer.  If the async stream has its data
126      // ready, we load it and finish up on our async work.
127
128      SFXStreamPacket* packet;
129      while( mAsyncState->mStream->read( &packet, 1 ) )
130      {
131         bool isLast = packet->mIsLast;
132
133         // Write packet data into buffer.
134         write( &packet, 1 );
135         packet = NULL;
136         
137         if( isLast )
138         {
139            // Release async state.
140            mAsyncState = NULL;
141
142            // Once loaded, non-streaming buffers disappear from the SFX
143            // update thread.
144            needFurtherUpdates = false;
145
146            // Signal that we are ready.
147            _setStatus( STATUS_Ready );
148
149            #ifdef DEBUG_SPEW
150            Platform::outputDebugString( "[SFXBuffer] Buffer ready" );
151            #endif
152
153            break;
154         }
155      }
156   }
157   else
158   {
159      // A streaming buffer.
160      //
161      // If we don't have a queue, construct one now.  Note that when doing
162      // a stream seek on us, SFXVoice will drop our async stream and queue.
163      // Work on local copies of the pointers to allow having the async state
164      // be switched in parallel.
165      //
166      // Note that despite us being a streamed buffer, our unique voice may
167      // not yet have been assigned to us.
168
169      AsyncStatePtr state = mAsyncState;
170      if( !state->mQueue && !mUniqueVoice.isNull() )
171      {
172         // Make sure we have no data currently submitted to the device.
173         // This will stop and discard an outdated feed if we've been
174         // switching streams.
175         
176         _setStatus( STATUS_Loading );
177         _flush();
178
179         // Create a new queue.
180
181         state->mQueue = new SFXAsyncQueue( mUniqueVoice, this, mIsLooping );
182      }
183      
184      // Check the queue.
185
186      if( state->mQueue != NULL )
187      {
188         // Feed the queue, if necessary and possible.
189
190         while( state->mQueue->needPacket() )
191         {
192            // Try to read a packet.
193
194            SFXStreamPacket* packet;
195            if( !state->mStream->read( &packet, 1 ) )
196               break;
197
198            // Submit it.
199
200            state->mQueue->submitPacket( packet, packet->getSampleCount() );
201
202            #ifdef DEBUG_SPEW
203            Platform::outputDebugString( "[SFXBuffer] Stream packet queued" );
204            #endif
205
206            // Signal that the buffer has data ready.
207
208            _setStatus( STATUS_Ready );
209         }
210
211         // Detect buffer underrun and end-of-stream.
212
213         if( !isReady() && state->mQueue->isEmpty() )
214         {
215            #ifdef DEBUG_SPEW
216            Platform::outputDebugString( "[SFXBuffer] Stream blocked" );
217            #endif
218
219            _setStatus( STATUS_Blocked );
220         }
221         else if( state->mQueue->isAtEnd() )
222         {
223            #ifdef DEBUG_SPEW
224            Platform::outputDebugString( "[SFXBuffer] Stream at end" );
225            #endif
226
227            _setStatus( STATUS_AtEnd );
228            _flush();
229         }
230      }
231   }
232
233   return needFurtherUpdates;
234}
235
236//-----------------------------------------------------------------------------
237
238void SFXBuffer::destroySelf()
239{
240   AssertFatal( !isDead(), "SFXBuffer::destroySelf() - buffer already dead" );
241
242   mIsDead = true;
243   if( !mAsyncState )
244   {
245      // Easy way.  This buffer has finished all its async
246      // processing, so we can just kill it.
247
248      delete this;
249   }
250   else
251   {
252      // Hard way.  We will have to make the buffer finish
253      // all its concurrent stuff, so we mark it dead, make sure
254      // to see an update, and then wait for the buffer to surface
255      // on the dead buffer list.
256
257      SFXInternal::TriggerUpdate();
258   }
259}
260
261//-----------------------------------------------------------------------------
262
263void SFXBuffer::_setStatus( Status status )
264{
265   if( mStatus != status )
266   {
267      mOnStatusChange.trigger( this, status );
268      mStatus = status;
269   }
270}
271
272//-----------------------------------------------------------------------------
273
274SFXBuffer::AsyncState::AsyncState()
275   : mQueue( NULL )
276{
277}
278
279//-----------------------------------------------------------------------------
280
281SFXBuffer::AsyncState::AsyncState( SFXInternal::SFXAsyncStream* stream )
282   : mStream( stream ), mQueue( NULL )
283{
284}
285
286//-----------------------------------------------------------------------------
287
288SFXBuffer::AsyncState::~AsyncState()
289{
290   if( mQueue )
291      SAFE_DELETE( mQueue );
292}
293