sfxBuffer.cpp
Engine/source/sfx/sfxBuffer.cpp
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