mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-02-25 09:33:50 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
291
Engine/source/sfx/sfxBuffer.cpp
Normal file
291
Engine/source/sfx/sfxBuffer.cpp
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 ),
|
||||
mIsStreaming( description->mIsStreaming ),
|
||||
mFormat( stream->getFormat() ),
|
||||
mDuration( stream->getDuration() ),
|
||||
mUniqueVoice( NULL ),
|
||||
mIsDead( false ),
|
||||
mIsLooping( description->mIsLooping ),
|
||||
mIsUnique( description->mIsStreaming )
|
||||
{
|
||||
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 ),
|
||||
mIsStreaming( false ), // Not streaming through our system.
|
||||
mDuration( 0 ), // Must be set by subclass.
|
||||
mUniqueVoice( NULL ),
|
||||
mIsDead( false ),
|
||||
mIsLooping( description->mIsLooping ),
|
||||
mIsUnique( false ) // Must be set by subclass.
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
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 );
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue