You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
818 lines
20 KiB
818 lines
20 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//============================================================================= |
|
|
|
#include "movieobjects/dmetrack.h" |
|
|
|
#include "tier0/dbg.h" |
|
#include "datamodel/dmelementfactoryhelper.h" |
|
#include "movieobjects/dmeclip.h" |
|
#include "movieobjects/dmetrackgroup.h" |
|
|
|
#include "movieobjects_interfaces.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// The solo track |
|
//----------------------------------------------------------------------------- |
|
DmElementHandle_t CDmeTrack::m_hSoloTrack[ DMECLIP_TYPE_COUNT ] = |
|
{ |
|
DMELEMENT_HANDLE_INVALID, |
|
DMELEMENT_HANDLE_INVALID, |
|
DMELEMENT_HANDLE_INVALID, |
|
DMELEMENT_HANDLE_INVALID, |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// CDmeTrack - common container class for clip objects |
|
//----------------------------------------------------------------------------- |
|
IMPLEMENT_ELEMENT_FACTORY( DmeTrack, CDmeTrack ); |
|
|
|
void CDmeTrack::OnConstruction() |
|
{ |
|
m_hOwner = DMELEMENT_HANDLE_INVALID; |
|
|
|
m_Flags.ClearAllFlags(); |
|
m_Clips.Init( this, "children" ); |
|
m_Collapsed.InitAndSet( this, "collapsed", true ); |
|
m_Mute.InitAndSet( this, "mute", false ); |
|
m_Synched.InitAndSet( this, "synched", true ); |
|
m_ClipType.InitAndSet( this, "clipType", DMECLIP_UNKNOWN, FATTRIB_HAS_CALLBACK | FATTRIB_HAS_PRE_CALLBACK ); |
|
|
|
m_Volume.InitAndSet( this, "volume", 1.0 ); |
|
|
|
} |
|
|
|
void CDmeTrack::OnDestruction() |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Methods of IDmElement |
|
//----------------------------------------------------------------------------- |
|
void CDmeTrack::OnAttributeChanged( CDmAttribute *pAttribute ) |
|
{ |
|
BaseClass::OnAttributeChanged( pAttribute ); |
|
|
|
// Attach callbacks to detected sorted conditions if we're a film clip |
|
if ( pAttribute == m_ClipType.GetAttribute() ) |
|
{ |
|
if ( m_ClipType == DMECLIP_FILM ) |
|
{ |
|
m_Flags.ClearFlag( IS_SORTED ); |
|
} |
|
return; |
|
} |
|
|
|
// This gets called when start/end time of children change, or if the array changes |
|
// This is a hack, since any OnAttributeChanged call that gets chained here from another element will trigger this |
|
// At some point, we'll probably have to start sending more data through OnAttributeChanged, (like an event string or chain path) |
|
// or perhaps add a new callback OnElementChanged() with this data |
|
if ( pAttribute == m_Clips.GetAttribute() || ( pAttribute->GetOwner() != this ) ) |
|
{ |
|
if ( !m_Flags.IsFlagSet( SUPPRESS_DIRTY_ORDERING ) ) |
|
{ |
|
m_Flags.ClearFlag( IS_SORTED ); |
|
} |
|
return; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Clip type |
|
//----------------------------------------------------------------------------- |
|
DmeClipType_t CDmeTrack::GetClipType() const |
|
{ |
|
return (DmeClipType_t)m_ClipType.Get(); |
|
} |
|
|
|
void CDmeTrack::SetClipType( DmeClipType_t type ) |
|
{ |
|
m_ClipType = type; |
|
} |
|
|
|
void CDmeTrack::SetCollapsed( bool state ) |
|
{ |
|
m_Collapsed = state; |
|
|
|
} |
|
|
|
bool CDmeTrack::IsCollapsed() const |
|
{ |
|
return m_Collapsed.Get(); |
|
} |
|
|
|
void CDmeTrack::SetMute( bool state ) |
|
{ |
|
m_Mute = state; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Volume |
|
//----------------------------------------------------------------------------- |
|
void CDmeTrack::SetVolume( float state ) |
|
{ |
|
m_Volume = state; |
|
} |
|
float CDmeTrack::GetVolume() const |
|
{ |
|
return m_Volume.Get(); |
|
} |
|
|
|
// Is this track synched to the film track? |
|
void CDmeTrack::SetSynched( bool bState ) |
|
{ |
|
m_Synched = bState; |
|
} |
|
|
|
bool CDmeTrack::IsSynched() const |
|
{ |
|
return m_Synched; |
|
} |
|
|
|
bool CDmeTrack::IsMute( bool bCheckSoloing ) const |
|
{ |
|
// if we're muted, don't play regardless of whether we're solo |
|
CDmeTrack *pSoloTrack = bCheckSoloing ? GetSoloTrack() : NULL; |
|
return m_Mute.Get() || ( pSoloTrack != this && pSoloTrack != NULL ); |
|
} |
|
|
|
int CDmeTrack::GetClipCount() const |
|
{ |
|
return m_Clips.Count(); |
|
} |
|
|
|
CDmeClip *CDmeTrack::GetClip( int i ) const |
|
{ |
|
return m_Clips[ i ]; |
|
} |
|
|
|
const CUtlVector< DmElementHandle_t > &CDmeTrack::GetClips( ) const |
|
{ |
|
return m_Clips.Get(); |
|
} |
|
|
|
void CDmeTrack::AddClip( CDmeClip *clip ) |
|
{ |
|
if ( clip->GetClipType() == GetClipType() ) |
|
{ |
|
// FIXME: In the case of a non-overlapped track, |
|
// we could optimize this to insert the clip in sorted order, |
|
// then fix overlaps (fixing overlaps requires a sorted list) |
|
Assert( FindClip( clip ) < 0 ); |
|
m_Clips.AddToTail( clip ); |
|
} |
|
} |
|
|
|
void CDmeTrack::RemoveClip( int i ) |
|
{ |
|
// NOTE: Removal shouldn't cause sort order or fixup to become invalid |
|
CSuppressAutoFixup suppress( this, SUPPRESS_OVERLAP_FIXUP | SUPPRESS_DIRTY_ORDERING ); |
|
m_Clips.Remove( i ); |
|
} |
|
|
|
bool CDmeTrack::RemoveClip( CDmeClip *clip ) |
|
{ |
|
Assert( clip->GetClipType() == GetClipType() ); |
|
int i = FindClip( clip ); |
|
if ( i != -1 ) |
|
{ |
|
RemoveClip( i ); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
void CDmeTrack::RemoveAllClips() |
|
{ |
|
CSuppressAutoFixup suppress( this, SUPPRESS_OVERLAP_FIXUP | SUPPRESS_DIRTY_ORDERING ); |
|
m_Clips.RemoveAll(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the solo track, if any |
|
//----------------------------------------------------------------------------- |
|
CDmeTrack *CDmeTrack::GetSoloTrack( DmeClipType_t clipType ) |
|
{ |
|
return GetElement< CDmeTrack >( m_hSoloTrack[ clipType ] ); |
|
} |
|
|
|
void CDmeTrack::SetSoloTrack( DmeClipType_t clipType, CDmeTrack *pTrack ) |
|
{ |
|
m_hSoloTrack[ clipType ] = pTrack->GetHandle(); |
|
} |
|
|
|
bool CDmeTrack::IsSoloTrack() const |
|
{ |
|
return m_hSoloTrack[ GetClipType() ] == GetHandle(); |
|
} |
|
|
|
CDmeTrack *CDmeTrack::GetSoloTrack() const |
|
{ |
|
return GetSoloTrack( GetClipType() ); |
|
} |
|
|
|
void CDmeTrack::SetSoloTrack( ) |
|
{ |
|
m_hSoloTrack[ GetClipType() ] = GetHandle(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Methods related to finding clips |
|
//----------------------------------------------------------------------------- |
|
int CDmeTrack::FindClip( CDmeClip *clip ) |
|
{ |
|
Assert( clip->GetClipType() == GetClipType() ); |
|
int c = m_Clips.Count(); |
|
for ( int i = c - 1; i >= 0; --i ) |
|
{ |
|
if ( m_Clips[ i ] == clip ) |
|
return i; |
|
} |
|
return -1; |
|
} |
|
|
|
CDmeClip *CDmeTrack::FindNamedClip( const char *name ) |
|
{ |
|
int c = m_Clips.Count(); |
|
for ( int i = c - 1; i >= 0; --i ) |
|
{ |
|
CDmeClip *child = m_Clips[ i ]; |
|
if ( child && !Q_stricmp( child->GetName(), name ) ) |
|
return child; |
|
} |
|
return NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Find clips at, intersecting or within a particular time interval |
|
//----------------------------------------------------------------------------- |
|
void CDmeTrack::FindClipsAtTime( DmeTime_t time, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const |
|
{ |
|
if ( ( flags & DMESKIP_INVISIBLE ) && IsCollapsed() ) |
|
return; |
|
|
|
if ( ( flags & DMESKIP_MUTED ) && IsMute() ) |
|
return; |
|
|
|
int nClipCount = GetClipCount(); |
|
for ( int j = 0; j < nClipCount; ++j ) |
|
{ |
|
CDmeClip *pSubClip = GetClip( j ); |
|
if ( !pSubClip ) |
|
continue; |
|
if ( ( flags & DMESKIP_MUTED ) && pSubClip->IsMute() ) |
|
continue; |
|
|
|
if ( time.IsInRange( pSubClip->GetStartTime(), pSubClip->GetEndTime() ) ) |
|
{ |
|
clips.AddToTail( pSubClip ); |
|
} |
|
} |
|
} |
|
|
|
void CDmeTrack::FindClipsIntersectingTime( DmeTime_t startTime, DmeTime_t endTime, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const |
|
{ |
|
if ( ( flags & DMESKIP_INVISIBLE ) && IsCollapsed() ) |
|
return; |
|
|
|
if ( ( flags & DMESKIP_MUTED ) && IsMute() ) |
|
return; |
|
|
|
int nClipCount = GetClipCount(); |
|
for ( int j = 0; j < nClipCount; ++j ) |
|
{ |
|
CDmeClip *pSubClip = GetClip( j ); |
|
if ( !pSubClip ) |
|
continue; |
|
if ( ( flags & DMESKIP_MUTED ) && pSubClip->IsMute() ) |
|
continue; |
|
|
|
DmeTime_t clipStart = pSubClip->GetStartTime(); |
|
DmeTime_t clipEnd = pSubClip->GetEndTime(); |
|
if ( clipEnd >= startTime && clipStart < endTime ) |
|
{ |
|
clips.AddToTail( pSubClip ); |
|
} |
|
} |
|
} |
|
|
|
void CDmeTrack::FindClipsWithinTime( DmeTime_t startTime, DmeTime_t endTime, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const |
|
{ |
|
if ( ( flags & DMESKIP_INVISIBLE ) && IsCollapsed() ) |
|
return; |
|
|
|
if ( ( flags & DMESKIP_MUTED ) && IsMute() ) |
|
return; |
|
|
|
int nClipCount = GetClipCount(); |
|
for ( int j = 0; j < nClipCount; ++j ) |
|
{ |
|
CDmeClip *pSubClip = GetClip( j ); |
|
if ( !pSubClip ) |
|
continue; |
|
if ( ( flags & DMESKIP_MUTED ) && pSubClip->IsMute() ) |
|
continue; |
|
|
|
DmeTime_t clipStart = pSubClip->GetStartTime(); |
|
DmeTime_t clipEnd = pSubClip->GetEndTime(); |
|
if ( clipStart >= startTime && clipEnd <= endTime ) |
|
{ |
|
clips.AddToTail( pSubClip ); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Methods related to shifting clips |
|
//----------------------------------------------------------------------------- |
|
void CDmeTrack::ShiftAllClips( DmeTime_t dt ) |
|
{ |
|
if ( dt == DmeTime_t( 0 ) ) |
|
return; |
|
|
|
CSuppressAutoFixup suppress( this, SUPPRESS_OVERLAP_FIXUP | SUPPRESS_DIRTY_ORDERING ); |
|
|
|
int c = GetClipCount(); |
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
CDmeClip *pSubClip = m_Clips[ i ]; |
|
pSubClip->SetStartTime( pSubClip->GetStartTime() + dt ); |
|
} |
|
} |
|
|
|
void CDmeTrack::ShiftAllClipsAfter( DmeTime_t startTime, DmeTime_t dt, bool bTestStartingTime ) |
|
{ |
|
if ( dt == DmeTime_t( 0 ) ) |
|
return; |
|
|
|
int c = GetClipCount(); |
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
CDmeClip *pSubClip = GetClip( i ); |
|
DmeTime_t testTime = bTestStartingTime ? pSubClip->GetStartTime() : pSubClip->GetEndTime(); |
|
if ( startTime < testTime ) |
|
{ |
|
pSubClip->SetStartTime( pSubClip->GetStartTime() + dt ); |
|
} |
|
} |
|
} |
|
|
|
void CDmeTrack::ShiftAllClipsBefore( DmeTime_t endTime, DmeTime_t dt, bool bTestEndingTime ) |
|
{ |
|
if ( dt == DmeTime_t( 0 ) ) |
|
return; |
|
|
|
int c = GetClipCount(); |
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
CDmeClip *pSubClip = GetClip( i ); |
|
DmeTime_t testTime = bTestEndingTime ? pSubClip->GetEndTime() : pSubClip->GetStartTime(); |
|
if ( endTime > testTime ) |
|
{ |
|
DmeTime_t startTime = pSubClip->GetStartTime(); |
|
pSubClip->SetStartTime( startTime + dt ); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// A version that works only on film clips |
|
//----------------------------------------------------------------------------- |
|
void CDmeTrack::ShiftAllFilmClipsAfter( CDmeClip *pClip, DmeTime_t dt, bool bShiftClip ) |
|
{ |
|
Assert( IsFilmTrack() ); |
|
if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) || ( dt == DmeTime_t( 0 ) ) ) |
|
return; |
|
|
|
// This algorithm requires sorted clips |
|
SortClipsByStartTime(); |
|
|
|
int c = GetClipCount(); |
|
for ( int i = c; --i >= 0; ) |
|
{ |
|
CDmeClip *pSubClip = GetClip( i ); |
|
if ( pSubClip == pClip ) |
|
{ |
|
if ( bShiftClip ) |
|
{ |
|
pSubClip->SetStartTime( pSubClip->GetStartTime() + dt ); |
|
} |
|
return; |
|
} |
|
pSubClip->SetStartTime( pSubClip->GetStartTime() + dt ); |
|
} |
|
|
|
// Clip wasn't found! |
|
Assert( 0 ); |
|
} |
|
|
|
void CDmeTrack::ShiftAllFilmClipsBefore( CDmeClip *pClip, DmeTime_t dt, bool bShiftClip ) |
|
{ |
|
Assert( IsFilmTrack() ); |
|
if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) || ( dt == DmeTime_t( 0 ) ) ) |
|
return; |
|
|
|
// This algorithm requires sorted clips |
|
SortClipsByStartTime(); |
|
|
|
int c = GetClipCount(); |
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
CDmeClip *pSubClip = GetClip( i ); |
|
|
|
if ( pSubClip == pClip ) |
|
{ |
|
if ( bShiftClip ) |
|
{ |
|
pSubClip->SetStartTime( pSubClip->GetStartTime() + dt ); |
|
} |
|
return; |
|
} |
|
pSubClip->SetStartTime( pSubClip->GetStartTime() + dt ); |
|
} |
|
|
|
// Clip wasn't found! |
|
Assert( 0 ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Method to sort clips by start time |
|
//----------------------------------------------------------------------------- |
|
struct SortInfo_t |
|
{ |
|
DmeTime_t m_startTime; |
|
CDmeClip *m_pClip; |
|
}; |
|
|
|
static int ClipStartLessFunc( const void * lhs, const void * rhs ) |
|
{ |
|
SortInfo_t *pInfo1 = (SortInfo_t*)lhs; |
|
SortInfo_t *pInfo2 = (SortInfo_t*)rhs; |
|
if ( pInfo1->m_startTime == pInfo2->m_startTime ) |
|
return 0; |
|
|
|
return pInfo1->m_startTime < pInfo2->m_startTime ? -1 : 1; |
|
} |
|
|
|
void CDmeTrack::SortClipsByStartTime( ) |
|
{ |
|
// If we're not a film clip, then we haven't installed callbacks to make sorting fast. |
|
// The IS_SORTED flag is some random state |
|
if ( (m_ClipType == DMECLIP_FILM) && m_Flags.IsFlagSet( IS_SORTED ) ) |
|
return; |
|
m_Flags.SetFlag( IS_SORTED ); |
|
|
|
int c = GetClipCount(); |
|
if ( c <= 1 ) |
|
return; |
|
|
|
DmeTime_t lastTime; |
|
SortInfo_t *pSortInfo = (SortInfo_t*)_alloca( c * sizeof(SortInfo_t) ); |
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
CDmeClip *pSubClip = GetClip(i); |
|
pSortInfo[i].m_startTime = pSubClip ? pSubClip->GetStartTime() : DmeTime_t::InvalidTime(); |
|
pSortInfo[i].m_pClip = pSubClip; |
|
if ( lastTime > pSortInfo[i].m_startTime ) |
|
{ |
|
m_Flags.ClearFlag( IS_SORTED ); |
|
} |
|
lastTime = pSortInfo[i].m_startTime; |
|
} |
|
if ( m_Flags.IsFlagSet( IS_SORTED ) ) |
|
return; |
|
|
|
m_Flags.SetFlag( IS_SORTED ); |
|
qsort( pSortInfo, c, sizeof(SortInfo_t), ClipStartLessFunc ); |
|
|
|
CSuppressAutoFixup suppress( this, SUPPRESS_OVERLAP_FIXUP | SUPPRESS_DIRTY_ORDERING ); |
|
|
|
m_Clips.RemoveAll(); |
|
|
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
m_Clips.AddToTail( pSortInfo[i].m_pClip ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Shifts all clips to be non-overlapping |
|
//----------------------------------------------------------------------------- |
|
void CDmeTrack::FixOverlaps() |
|
{ |
|
int c = GetClipCount(); |
|
if ( c <= 1 ) |
|
return; |
|
|
|
SortClipsByStartTime(); |
|
|
|
CSuppressAutoFixup suppress( this, SUPPRESS_OVERLAP_FIXUP | SUPPRESS_DIRTY_ORDERING ); |
|
|
|
// Cull NULL clips |
|
int nActualCount = 0; |
|
CDmeClip **pClips = (CDmeClip**)_alloca( c * sizeof(CDmeClip*) ); |
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
CDmeClip *pCurr = GetClip( i ); |
|
if ( pCurr && ((i == 0) || (pClips[i-1] != pCurr)) ) |
|
{ |
|
pClips[nActualCount++] = pCurr; |
|
} |
|
} |
|
|
|
if ( nActualCount <= 1 ) |
|
return; |
|
|
|
CDmeClip *pPrev = pClips[0]; |
|
for ( int i = 1; i < nActualCount; ++i ) |
|
{ |
|
CDmeClip *pCurr = pClips[i]; |
|
|
|
DmeTime_t prevEndTime = pPrev->GetEndTime(); |
|
DmeTime_t startTime = pCurr->GetStartTime(); |
|
|
|
if ( startTime < prevEndTime ) |
|
{ |
|
pCurr->SetStartTime( prevEndTime ); |
|
} |
|
|
|
pPrev = pCurr; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Finds a clip at a particular time |
|
//----------------------------------------------------------------------------- |
|
CDmeClip* CDmeTrack::FindFilmClipAtTime( DmeTime_t localTime ) |
|
{ |
|
if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) ) |
|
return NULL; |
|
|
|
// This algorithm requires sorted clips |
|
SortClipsByStartTime(); |
|
|
|
int c = GetClipCount(); |
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
CDmeClip *pSubClip = GetClip( i ); |
|
if ( pSubClip && pSubClip->GetStartTime() <= localTime && pSubClip->GetEndTime() > localTime ) |
|
return pSubClip; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Find first clip in a specific time range |
|
//----------------------------------------------------------------------------- |
|
CDmeClip* CDmeTrack::FindFirstFilmClipIntesectingTime( DmeTime_t localStartTime, DmeTime_t localEndTime ) |
|
{ |
|
if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) ) |
|
return NULL; |
|
|
|
// This algorithm requires sorted clips |
|
SortClipsByStartTime(); |
|
|
|
int c = GetClipCount(); |
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
CDmeClip *pSubClip = GetClip( i ); |
|
if ( !pSubClip ) |
|
continue; |
|
if ( ( localStartTime < pSubClip->GetEndTime() ) && ( localEndTime >= pSubClip->GetStartTime() ) ) |
|
return static_cast<CDmeFilmClip*>( pSubClip ); |
|
if ( localEndTime <= pSubClip->GetStartTime() ) |
|
break; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Inserts space in a film track for a film clip |
|
//----------------------------------------------------------------------------- |
|
void CDmeTrack::InsertSpaceInFilmTrack( DmeTime_t localStartTime, DmeTime_t localEndTime ) |
|
{ |
|
if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) ) |
|
return; |
|
|
|
// This algorithm requires sorted clips |
|
SortClipsByStartTime(); |
|
|
|
CDmeClip *pClip = FindFirstFilmClipIntesectingTime( localStartTime, localEndTime ); |
|
if ( pClip ) |
|
{ |
|
DmeTime_t filmStart = pClip->GetStartTime(); |
|
DmeTime_t dt = localEndTime - filmStart; |
|
ShiftAllFilmClipsAfter( pClip, dt, true ); |
|
} |
|
|
|
return; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the next/previous clip in a film track |
|
//----------------------------------------------------------------------------- |
|
CDmeClip* CDmeTrack::FindPrevFilmClip( CDmeClip *pClip ) |
|
{ |
|
Assert( IsFilmTrack() ); |
|
if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) ) |
|
return NULL; |
|
|
|
// This algorithm requires sorted clips |
|
SortClipsByStartTime(); |
|
|
|
if ( !pClip ) |
|
return m_Clips[ m_Clips.Count() - 1 ]; |
|
|
|
// FIXME: Could use a binary search here based on time. |
|
// Probably doesn't matter though, since there will usually not be a ton of tracks |
|
CDmeClip *pPrevClip = NULL; |
|
|
|
int c = GetClipCount(); |
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
CDmeClip *pSubClip = GetClip( i ); |
|
if ( pSubClip == pClip ) |
|
return pPrevClip; |
|
|
|
pPrevClip = pSubClip; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
CDmeClip* CDmeTrack::FindNextFilmClip( CDmeClip *pClip ) |
|
{ |
|
Assert( IsFilmTrack() ); |
|
if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) ) |
|
return NULL; |
|
|
|
// This algorithm requires sorted clips |
|
SortClipsByStartTime(); |
|
|
|
if ( !pClip ) |
|
return m_Clips[ 0 ]; |
|
|
|
CDmeClip *pNextClip = NULL; |
|
|
|
int c = GetClipCount(); |
|
for ( int i = c; --i >= 0; ) |
|
{ |
|
CDmeClip *pSubClip = GetClip( i ); |
|
if ( pSubClip == pClip ) |
|
return pNextClip; |
|
|
|
pNextClip = pSubClip; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
void CDmeTrack::FindAdjacentFilmClips( CDmeClip *pClip, CDmeClip *&pPrevClip, CDmeClip *&pNextClip ) |
|
{ |
|
pPrevClip = pNextClip = NULL; |
|
|
|
Assert( IsFilmTrack() ); |
|
if ( !IsFilmTrack() || !pClip || ( m_Clips.Count() == 0 ) ) |
|
return; |
|
|
|
// This algorithm requires sorted clips |
|
SortClipsByStartTime(); |
|
|
|
int c = GetClipCount(); |
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
CDmeClip *pSubClip = GetClip( i ); |
|
if ( pSubClip == pClip ) |
|
{ |
|
pNextClip = ( i != c-1 ) ? GetClip( i+1 ) : NULL; |
|
return; |
|
} |
|
|
|
pPrevClip = pSubClip; |
|
} |
|
|
|
pPrevClip = NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Gets the start/end time of the owning clip in local time |
|
//----------------------------------------------------------------------------- |
|
void CDmeTrack::FindAdjacentFilmClips( DmeTime_t localTime, CDmeClip *&pPrevClip, CDmeClip *&pNextClip ) |
|
{ |
|
pPrevClip = pNextClip = NULL; |
|
|
|
Assert( IsFilmTrack() ); |
|
if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) ) |
|
return; |
|
|
|
// This algorithm requires sorted clips |
|
SortClipsByStartTime(); |
|
|
|
int c = GetClipCount(); |
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
CDmeClip *pSubClip = GetClip( i ); |
|
if ( localTime >= pSubClip->GetEndTime() ) |
|
{ |
|
pPrevClip = pSubClip; |
|
} |
|
if ( localTime < pSubClip->GetStartTime() ) |
|
{ |
|
pNextClip = pSubClip; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Fills all gaps in a film track with slugs |
|
//----------------------------------------------------------------------------- |
|
void CDmeTrack::FillAllGapsWithSlugs( const char *pSlugName, DmeTime_t startTime, DmeTime_t endTime ) |
|
{ |
|
if ( !IsFilmTrack() ) |
|
return; |
|
|
|
FixOverlaps(); |
|
|
|
// Create temporary slugs to fill in the gaps |
|
bool bSlugAdded = false; |
|
int c = GetClipCount(); |
|
for ( int i = 0; i < c; ++i ) |
|
{ |
|
CDmeClip *pFilmClip = GetClip(i); |
|
DmeTime_t clipStartTime = pFilmClip->GetStartTime(); |
|
if ( clipStartTime > startTime ) |
|
{ |
|
// There's a gap, create a slug |
|
CDmeFilmClip *pSlug = CreateSlugClip( pSlugName, startTime, clipStartTime, GetFileId() ); |
|
|
|
// This will add the slug to the end; so we don't have to |
|
// worry about iterating over it (we've cached off the initial count) |
|
AddClip( pSlug ); |
|
|
|
bSlugAdded = true; |
|
} |
|
startTime = pFilmClip->GetEndTime(); |
|
} |
|
|
|
if ( endTime > startTime ) |
|
{ |
|
// There's a gap, create a temporary slug |
|
CDmeFilmClip *pSlug = CreateSlugClip( pSlugName, startTime, endTime, GetFileId() ); |
|
|
|
// This will add the slug to the end; so we don't have to |
|
// worry about iterating over it (we've cached off the initial count) |
|
AddClip( pSlug ); |
|
|
|
bSlugAdded = true; |
|
} |
|
|
|
if ( bSlugAdded ) |
|
{ |
|
FixOverlaps(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// helper methods |
|
//----------------------------------------------------------------------------- |
|
CDmeTrackGroup *GetParentTrackGroup( CDmeTrack *pTrack ) |
|
{ |
|
DmAttributeReferenceIterator_t hAttr = g_pDataModel->FirstAttributeReferencingElement( pTrack->GetHandle() ); |
|
for ( ; hAttr != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID; hAttr = g_pDataModel->NextAttributeReferencingElement( hAttr ) ) |
|
{ |
|
CDmAttribute *pAttr = g_pDataModel->GetAttribute( hAttr ); |
|
if ( !pAttr ) |
|
continue; |
|
|
|
CDmeTrackGroup *pTrackGroup = CastElement< CDmeTrackGroup >( pAttr->GetOwner() ); |
|
if ( pTrackGroup ) |
|
return pTrackGroup; |
|
} |
|
return NULL; |
|
}
|
|
|