mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 20:54:46 +00:00
while not a major issue per-se, the sheer number of times the engine has to jump back in memory and backfill data in a given class can add up. First run of... many.,
292 lines
8.4 KiB
C++
292 lines
8.4 KiB
C++
//-----------------------------------------------------------------------------
|
|
// Copyright (c) 2012 GarageGames, LLC
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to
|
|
// deal in the Software without restriction, including without limitation the
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "sfx/sfxBuffer.h"
|
|
#include "sfx/sfxVoice.h"
|
|
#include "sfx/sfxDescription.h"
|
|
#include "sfx/sfxInternal.h"
|
|
|
|
|
|
//#define DEBUG_SPEW
|
|
|
|
|
|
Signal< void( SFXBuffer* ) > SFXBuffer::smBufferDestroyedSignal;
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
SFXBuffer::SFXBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description, bool createAsyncState )
|
|
: mStatus( STATUS_Null ),
|
|
mFormat( stream->getFormat() ),
|
|
mDuration( stream->getDuration() ),
|
|
mIsStreaming( description->mIsStreaming ),
|
|
mIsLooping( description->mIsLooping ),
|
|
mIsUnique( description->mIsStreaming ),
|
|
mIsDead( false ),
|
|
mUniqueVoice( NULL )
|
|
{
|
|
using namespace SFXInternal;
|
|
|
|
if( createAsyncState )
|
|
{
|
|
U32 packetLength = description->mStreamPacketSize;
|
|
U32 readAhead = description->mStreamReadAhead;
|
|
|
|
ThreadSafeRef< SFXStream > streamRef( stream );
|
|
mAsyncState = new AsyncState(
|
|
new SFXAsyncStream
|
|
( streamRef, mIsStreaming, packetLength, readAhead,
|
|
mIsStreaming ? description->mIsLooping : false ) );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
SFXBuffer::SFXBuffer( SFXDescription* description )
|
|
: mStatus( STATUS_Ready ),
|
|
mDuration( 0 ), // Must be set by subclass.
|
|
mIsStreaming( false ), // Not streaming through our system.
|
|
mIsLooping( description->mIsLooping ),
|
|
mIsUnique( false ), // Must be set by subclass.
|
|
mIsDead( false ),
|
|
mUniqueVoice( NULL )
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
SFXBuffer::~SFXBuffer()
|
|
{
|
|
smBufferDestroyedSignal.trigger( this );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SFXBuffer::load()
|
|
{
|
|
if( getStatus() == STATUS_Null )
|
|
{
|
|
AssertFatal( mAsyncState != NULL, "SFXBuffer::load() - no async state!" );
|
|
|
|
_setStatus( STATUS_Loading );
|
|
SFXInternal::UPDATE_LIST().add( this );
|
|
mAsyncState->mStream->start();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool SFXBuffer::update()
|
|
{
|
|
using namespace SFXInternal;
|
|
|
|
if( isDead() )
|
|
{
|
|
// Buffer requested to finish its async operations.
|
|
// Kill our async state and put us on the dead buffer list.
|
|
|
|
mAsyncState->mStream->stop();
|
|
mAsyncState = NULL;
|
|
gDeadBufferList.pushFront( this );
|
|
return false;
|
|
}
|
|
else if( isAtEnd() && isStreaming() )
|
|
{
|
|
// Streaming buffers remain in the update loop even if they
|
|
// have played in full to allow for stream seeks.
|
|
return true;
|
|
}
|
|
|
|
AssertFatal( mAsyncState != NULL, "SFXBuffer::update() - async state has already been released" );
|
|
|
|
bool needFurtherUpdates = true;
|
|
if( !isStreaming() )
|
|
{
|
|
// Not a streaming buffer. If the async stream has its data
|
|
// ready, we load it and finish up on our async work.
|
|
|
|
SFXStreamPacket* packet;
|
|
while( mAsyncState->mStream->read( &packet, 1 ) )
|
|
{
|
|
bool isLast = packet->mIsLast;
|
|
|
|
// Write packet data into buffer.
|
|
write( &packet, 1 );
|
|
packet = NULL;
|
|
|
|
if( isLast )
|
|
{
|
|
// Release async state.
|
|
mAsyncState = NULL;
|
|
|
|
// Once loaded, non-streaming buffers disappear from the SFX
|
|
// update thread.
|
|
needFurtherUpdates = false;
|
|
|
|
// Signal that we are ready.
|
|
_setStatus( STATUS_Ready );
|
|
|
|
#ifdef DEBUG_SPEW
|
|
Platform::outputDebugString( "[SFXBuffer] Buffer ready" );
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// A streaming buffer.
|
|
//
|
|
// If we don't have a queue, construct one now. Note that when doing
|
|
// a stream seek on us, SFXVoice will drop our async stream and queue.
|
|
// Work on local copies of the pointers to allow having the async state
|
|
// be switched in parallel.
|
|
//
|
|
// Note that despite us being a streamed buffer, our unique voice may
|
|
// not yet have been assigned to us.
|
|
|
|
AsyncStatePtr state = mAsyncState;
|
|
if( !state->mQueue && !mUniqueVoice.isNull() )
|
|
{
|
|
// Make sure we have no data currently submitted to the device.
|
|
// This will stop and discard an outdated feed if we've been
|
|
// switching streams.
|
|
|
|
_setStatus( STATUS_Loading );
|
|
_flush();
|
|
|
|
// Create a new queue.
|
|
|
|
state->mQueue = new SFXAsyncQueue( mUniqueVoice, this, mIsLooping );
|
|
}
|
|
|
|
// Check the queue.
|
|
|
|
if( state->mQueue != NULL )
|
|
{
|
|
// Feed the queue, if necessary and possible.
|
|
|
|
while( state->mQueue->needPacket() )
|
|
{
|
|
// Try to read a packet.
|
|
|
|
SFXStreamPacket* packet;
|
|
if( !state->mStream->read( &packet, 1 ) )
|
|
break;
|
|
|
|
// Submit it.
|
|
|
|
state->mQueue->submitPacket( packet, packet->getSampleCount() );
|
|
|
|
#ifdef DEBUG_SPEW
|
|
Platform::outputDebugString( "[SFXBuffer] Stream packet queued" );
|
|
#endif
|
|
|
|
// Signal that the buffer has data ready.
|
|
|
|
_setStatus( STATUS_Ready );
|
|
}
|
|
|
|
// Detect buffer underrun and end-of-stream.
|
|
|
|
if( !isReady() && state->mQueue->isEmpty() )
|
|
{
|
|
#ifdef DEBUG_SPEW
|
|
Platform::outputDebugString( "[SFXBuffer] Stream blocked" );
|
|
#endif
|
|
|
|
_setStatus( STATUS_Blocked );
|
|
}
|
|
else if( state->mQueue->isAtEnd() )
|
|
{
|
|
#ifdef DEBUG_SPEW
|
|
Platform::outputDebugString( "[SFXBuffer] Stream at end" );
|
|
#endif
|
|
|
|
_setStatus( STATUS_AtEnd );
|
|
_flush();
|
|
}
|
|
}
|
|
}
|
|
|
|
return needFurtherUpdates;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SFXBuffer::destroySelf()
|
|
{
|
|
AssertFatal( !isDead(), "SFXBuffer::destroySelf() - buffer already dead" );
|
|
|
|
mIsDead = true;
|
|
if( !mAsyncState )
|
|
{
|
|
// Easy way. This buffer has finished all its async
|
|
// processing, so we can just kill it.
|
|
|
|
delete this;
|
|
}
|
|
else
|
|
{
|
|
// Hard way. We will have to make the buffer finish
|
|
// all its concurrent stuff, so we mark it dead, make sure
|
|
// to see an update, and then wait for the buffer to surface
|
|
// on the dead buffer list.
|
|
|
|
SFXInternal::TriggerUpdate();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SFXBuffer::_setStatus( Status status )
|
|
{
|
|
if( mStatus != status )
|
|
{
|
|
mOnStatusChange.trigger( this, status );
|
|
mStatus = status;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
SFXBuffer::AsyncState::AsyncState()
|
|
: mQueue( NULL )
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
SFXBuffer::AsyncState::AsyncState( SFXInternal::SFXAsyncStream* stream )
|
|
: mStream( stream ), mQueue( NULL )
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
SFXBuffer::AsyncState::~AsyncState()
|
|
{
|
|
if( mQueue )
|
|
SAFE_DELETE( mQueue );
|
|
}
|