1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-16 16:10:06 +02:00

WaveTrack.cpp does not depend on ODManager.cpp ...

... Breaking up an s.c.c. of 6 into 3 components:
ODManager, ODTask, ODWaveTrackTaskQueue
UndoManager
WaveClip, WaveTrack

Rewrite the OD tasks and queues to hold weak pointers to tracks, so the
track destructor need not notify them.
This commit is contained in:
Paul Licameli 2019-06-05 11:46:07 -04:00
parent 71250b1dc3
commit 86320838de
16 changed files with 101 additions and 120 deletions

View File

@ -210,7 +210,7 @@ void ProjectFileManager::EnqueueODTasks()
//add the track to the already created tasks that correspond to the od flags in the wavetrack.
for(unsigned int i=0;i<newTasks.size();i++) {
if(newTasks[i]->GetODType() & odFlags)
newTasks[i]->AddWaveTrack(wt);
newTasks[i]->AddWaveTrack(wt->SharedPointer< WaveTrack >());
}
//create whatever NEW tasks we need to.
@ -236,7 +236,7 @@ void ProjectFileManager::EnqueueODTasks()
}
if(newTask)
{
newTask->AddWaveTrack(wt);
newTask->AddWaveTrack(wt->SharedPointer< WaveTrack >());
newTasks.push_back(std::move(newTask));
}
}

View File

@ -17,6 +17,7 @@ Paul Licameli split from ProjectManager.cpp
#include "Track.h"
#include "UndoManager.h"
#include "ViewInfo.h"
#include "WaveTrack.h"
#include "ondemand/ODComputeSummaryTask.h"
#include "ondemand/ODManager.h"
@ -167,7 +168,7 @@ void ProjectHistory::PopState(const UndoState &state)
// PRL: Is it correct to add all tracks to one task, even if they
// are not partnered channels? Rather than
// make one task for each?
computeTask->AddWaveTrack(wt);
computeTask->AddWaveTrack(wt->SharedPointer< WaveTrack >());
}
});
}

View File

@ -53,8 +53,6 @@ Track classes.
#include "Prefs.h"
#include "ondemand/ODManager.h"
#include "effects/TimeWarper.h"
#include "prefs/SpectrogramSettings.h"
#include "prefs/TracksPrefs.h"
@ -213,10 +211,6 @@ void WaveTrack::Merge(const Track &orig)
WaveTrack::~WaveTrack()
{
//Let the ODManager know this WaveTrack is disappearing.
//Deschedules tasks associated with this track.
if(ODManager::IsInstanceCreated())
ODManager::Instance()->RemoveWaveTrack(this);
}
double WaveTrack::GetOffset() const

View File

@ -2256,7 +2256,7 @@ void Effect::ReplaceProcessedTracks(const bool bGoodResult)
// Swap the wavecache track the ondemand task uses, since now the NEW
// one will be kept in the project
if (ODManager::IsInstanceCreated()) {
ODManager::Instance()->ReplaceWaveTrack( t, o.get() );
ODManager::Instance()->ReplaceWaveTrack( t, o );
}
}
}

View File

@ -588,7 +588,7 @@ ProgressResult FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
//for each wavetrack within the stream add coded blockfiles
for (int c = 0; c < sc->m_stream->codec->channels; c++) {
WaveTrack *t = stream[c].get();
auto t = stream[c];
odTask->AddWaveTrack(t);
auto maxBlockSize = t->GetMaxBlockSize();

View File

@ -505,7 +505,7 @@ ProgressResult FLACImportFileHandle::Import(TrackFactory *trackFactory,
bool moreThanStereo = mNumChannels>2;
for (const auto &channel : mChannels)
{
mDecoderTask->AddWaveTrack(channel.get());
mDecoderTask->AddWaveTrack(channel);
if(moreThanStereo)
{
//if we have 3 more channels, they get imported on seperate tracks, so we add individual tasks for each.

View File

@ -445,7 +445,7 @@ ProgressResult PCMImportFileHandle::Import(TrackFactory *trackFactory,
bool moreThanStereo = mInfo.channels>2;
for (const auto &channel : channels)
{
computeTask->AddWaveTrack(channel.get());
computeTask->AddWaveTrack(channel);
if(moreThanStereo)
{
//if we have 3 more channels, they get imported on seperate tracks, so we add individual tasks for each.

View File

@ -116,8 +116,9 @@ void ODComputeSummaryTask::DoSomeInternal()
mWaveTrackMutex.Lock();
for(size_t i=0;i<mWaveTracks.size();i++)
{
if(success && mWaveTracks[i])
mWaveTracks[i]->AddInvalidRegion(blockStartSample,blockEndSample);
auto waveTrack = mWaveTracks[i].lock();
if(success && waveTrack)
waveTrack->AddInvalidRegion(blockStartSample,blockEndSample);
}
mWaveTrackMutex.Unlock();
}
@ -182,13 +183,14 @@ void ODComputeSummaryTask::Update()
for(size_t j=0;j<mWaveTracks.size();j++)
{
if(mWaveTracks[j])
auto waveTrack = mWaveTracks[j].lock();
if(waveTrack)
{
BlockArray *blocks;
Sequence *seq;
//gather all the blockfiles that we should process in the wavetrack.
for (const auto &clip : mWaveTracks[j]->GetAllClips()) {
for (const auto &clip : waveTrack->GetAllClips()) {
seq = clip->GetSequence();
//This lock may be way too big since the whole file is one sequence.
//TODO: test for large files and find a way to break it down.

View File

@ -102,8 +102,9 @@ void ODDecodeTask::DoSomeInternal()
mWaveTrackMutex.Lock();
for(size_t i=0;i<mWaveTracks.size();i++)
{
if(mWaveTracks[i])
mWaveTracks[i]->AddInvalidRegion(blockStartSample,blockEndSample);
auto waveTrack = mWaveTracks[i].lock();
if(waveTrack)
waveTrack->AddInvalidRegion(blockStartSample,blockEndSample);
}
mWaveTrackMutex.Unlock();
}
@ -138,13 +139,14 @@ void ODDecodeTask::Update()
for(size_t j=0;j<mWaveTracks.size();j++)
{
if(mWaveTracks[j])
auto waveTrack = mWaveTracks[j].lock();
if(waveTrack)
{
BlockArray *blocks;
Sequence *seq;
//gather all the blockfiles that we should process in the wavetrack.
for (const auto &clip : mWaveTracks[j]->GetAllClips()) {
for (const auto &clip : waveTrack->GetAllClips()) {
seq = clip->GetSequence();
//TODO:this lock is way to big since the whole file is one sequence. find a way to break it down.
seq->LockDeleteUpdateMutex();

View File

@ -257,7 +257,7 @@ void ODManager::AddNewTask(std::unique_ptr<ODTask> &&mtask, bool lockMutex)
{
//search for a task containing the lead track. wavetrack removal is threadsafe and bound to the mQueuesMutex
//note that GetWaveTrack is not threadsafe, but we are assuming task is not running on a different thread yet.
if(mQueues[i]->ContainsWaveTrack(task->GetWaveTrack(0)))
if(mQueues[i]->ContainsWaveTrack(task->GetWaveTrack(0).get()))
queue = mQueues[i].get();
}
@ -472,20 +472,9 @@ void ODManager::Quit()
}
}
///removes a wavetrack and notifies its associated tasks to stop using its reference.
void ODManager::RemoveWaveTrack(WaveTrack* track)
{
mQueuesMutex.Lock();
for(unsigned int i=0;i<mQueues.size();i++)
{
if(mQueues[i]->ContainsWaveTrack(track))
mQueues[i]->RemoveWaveTrack(track);
}
mQueuesMutex.Unlock();
}
///replace the wavetrack whose wavecache the gui watches for updates
void ODManager::ReplaceWaveTrack(Track *oldTrack, Track *newTrack)
void ODManager::ReplaceWaveTrack(Track *oldTrack,
const std::shared_ptr<Track> &newTrack)
{
mQueuesMutex.Lock();
for(unsigned int i=0;i<mQueues.size();i++)
@ -496,13 +485,14 @@ void ODManager::ReplaceWaveTrack(Track *oldTrack, Track *newTrack)
}
///if it shares a queue/task, creates a NEW queue/task for the track, and removes it from any previously existing tasks.
void ODManager::MakeWaveTrackIndependent(WaveTrack* track)
void ODManager::MakeWaveTrackIndependent(
const std::shared_ptr< WaveTrack > &track)
{
ODWaveTrackTaskQueue* owner=NULL;
mQueuesMutex.Lock();
for(unsigned int i=0;i<mQueues.size();i++)
{
if(mQueues[i]->ContainsWaveTrack(track))
if(mQueues[i]->ContainsWaveTrack(track.get()))
{
owner = mQueues[i].get();
break;
@ -519,7 +509,10 @@ void ODManager::MakeWaveTrackIndependent(WaveTrack* track)
///better design in the future.
///@return returns success. Some ODTask conditions require that the tasks finish before merging.
///e.g. they have different effects being processed at the same time.
bool ODManager::MakeWaveTrackDependent(WaveTrack* dependentTrack,WaveTrack* masterTrack)
bool ODManager::MakeWaveTrackDependent(
const std::shared_ptr< WaveTrack > &dependentTrack,
WaveTrack* masterTrack
)
{
//First, check to see if the task lists are mergeable. If so, we can simply add this track to the other task and queue,
//then DELETE this one.
@ -535,7 +528,7 @@ bool ODManager::MakeWaveTrackDependent(WaveTrack* dependentTrack,WaveTrack* mast
{
masterQueue = mQueues[i].get();
}
else if(mQueues[i]->ContainsWaveTrack(dependentTrack))
else if(mQueues[i]->ContainsWaveTrack(dependentTrack.get()))
{
dependentQueue = mQueues[i].get();
dependentIndex = i;

View File

@ -67,19 +67,20 @@ class ODManager final
///Wakes the queue loop up by signalling its condition variable.
void SignalTaskQueueLoop();
///removes a wavetrack and notifies its associated tasks to stop using its reference.
void RemoveWaveTrack(WaveTrack* track);
///if it shares a queue/task, creates a NEW queue/task for the track, and removes it from any previously existing tasks.
void MakeWaveTrackIndependent(WaveTrack* track);
void MakeWaveTrackIndependent( const std::shared_ptr< WaveTrack > &track);
///attach the track in question to another, already existing track's queues and tasks. Remove the task/tracks.
///Returns success if it was possible.. Some ODTask conditions make it impossible until the Tasks finish.
bool MakeWaveTrackDependent(WaveTrack* dependentTrack,WaveTrack* masterTrack);
bool MakeWaveTrackDependent(
const std::shared_ptr< WaveTrack > &dependentTrack,
WaveTrack* masterTrack
);
///if oldTrack is being watched,
///replace the wavetrack whose wavecache the gui watches for updates
void ReplaceWaveTrack(Track *oldTrack, Track *newTrack);
void ReplaceWaveTrack(Track *oldTrack,
const std::shared_ptr< Track > &newTrack);
///Adds a task to the running queue. Threas-safe.
void AddTask(ODTask* task);

View File

@ -182,7 +182,7 @@ bool ODTask::IsTaskAssociatedWithProject(AudacityProject* proj)
mWaveTrackMutex.Lock();
for(int i=0;i<(int)mWaveTracks.size();i++)
{
if(mWaveTracks[i]==tr)
if ( mWaveTracks[i].lock().get() == tr )
{
//if we find one, then the project is associated with us;return true
mWaveTrackMutex.Unlock();
@ -252,19 +252,19 @@ bool ODTask::IsComplete()
}
WaveTrack* ODTask::GetWaveTrack(int i)
std::shared_ptr< WaveTrack > ODTask::GetWaveTrack(int i)
{
WaveTrack* track = NULL;
std::shared_ptr< WaveTrack > track;
mWaveTrackMutex.Lock();
if(i<(int)mWaveTracks.size())
track = mWaveTracks[i];
track = mWaveTracks[i].lock();
mWaveTrackMutex.Unlock();
return track;
}
///Sets the wavetrack that will be analyzed for ODPCMAliasBlockFiles that will
///have their summaries computed and written to disk.
void ODTask::AddWaveTrack(WaveTrack* track)
void ODTask::AddWaveTrack( const std::shared_ptr< WaveTrack > &track)
{
mWaveTracks.push_back(track);
}
@ -321,7 +321,7 @@ void ODTask::DemandTrackUpdate(WaveTrack* track, double seconds)
mWaveTrackMutex.Lock();
for(size_t i=0;i<mWaveTracks.size();i++)
{
if(track == mWaveTracks[i])
if ( track == mWaveTracks[i].lock().get() )
{
auto newDemandSample = (sampleCount)(seconds * track->GetRate());
demandSampleChanged = newDemandSample != GetDemandSample();
@ -342,21 +342,22 @@ void ODTask::StopUsingWaveTrack(WaveTrack* track)
mWaveTrackMutex.Lock();
for(size_t i=0;i<mWaveTracks.size();i++)
{
if(mWaveTracks[i] == track)
mWaveTracks[i]=NULL;
if(mWaveTracks[i].lock().get() == track)
mWaveTracks[i].reset();
}
mWaveTrackMutex.Unlock();
}
///Replaces all instances to a wavetrack with a NEW one, effectively transferring the task.
void ODTask::ReplaceWaveTrack(Track *oldTrack, Track *newTrack)
void ODTask::ReplaceWaveTrack(Track *oldTrack,
const std::shared_ptr< Track > &newTrack)
{
mWaveTrackMutex.Lock();
for(size_t i=0;i<mWaveTracks.size();i++)
{
if(oldTrack == mWaveTracks[i])
if(oldTrack == mWaveTracks[i].lock().get())
{
mWaveTracks[i] = static_cast<WaveTrack*>( newTrack );
mWaveTracks[i] = std::static_pointer_cast<WaveTrack>( newTrack );
}
}
mWaveTrackMutex.Unlock();

View File

@ -84,12 +84,13 @@ class ODTask /* not final */
///Replaces all instances to a wavetrack with a NEW one, effectively transferring the task.
///ODTask has no wavetrack, so it does nothing. But subclasses that do should override this.
virtual void ReplaceWaveTrack(Track *oldTrack, Track *newTrack);
virtual void ReplaceWaveTrack(Track *oldTrack,
const std::shared_ptr< Track > &newTrack);
///Adds a WaveTrack to do the task for
void AddWaveTrack(WaveTrack* track);
void AddWaveTrack( const std::shared_ptr< WaveTrack > &track);
virtual int GetNumWaveTracks();
virtual WaveTrack* GetWaveTrack(int i);
virtual std::shared_ptr< WaveTrack > GetWaveTrack(int i);
///changes the tasks associated with this Waveform to process the task from a different point in the track
virtual void DemandTrackUpdate(WaveTrack* track, double seconds);
@ -156,7 +157,7 @@ class ODTask /* not final */
//for a function not a member var.
ODLock mBlockUntilTerminateMutex;
std::vector<WaveTrack*> mWaveTracks;
std::vector< std::weak_ptr< WaveTrack > > mWaveTracks;
ODLock mWaveTrackMutex;
sampleCount mDemandSample;

View File

@ -64,7 +64,8 @@ bool ODWaveTrackTaskQueue::CanMergeWith(ODWaveTrackTaskQueue* otherQueue)
/// sets the NeedODUpdateFlag since we don't want the head task to finish without haven't dealt with the depednent
///
///@param track the track to bring into the tasks AND tracklist for this queue
void ODWaveTrackTaskQueue::MergeWaveTrack(WaveTrack* track)
void ODWaveTrackTaskQueue::MergeWaveTrack(
const std::shared_ptr< WaveTrack > &track)
{
AddWaveTrack(track);
mTasksMutex.Lock();
@ -83,7 +84,7 @@ bool ODWaveTrackTaskQueue::ContainsWaveTrack(const WaveTrack* track)
mTracksMutex.Lock();
for(unsigned int i=0;i<mTracks.size();i++)
{
if(mTracks[i]==track)
if ( mTracks[i].lock().get() == track )
{
mTracksMutex.Unlock();
return true;
@ -93,7 +94,8 @@ bool ODWaveTrackTaskQueue::ContainsWaveTrack(const WaveTrack* track)
return false;
}
///Adds a track to the associated list.
void ODWaveTrackTaskQueue::AddWaveTrack(WaveTrack* track)
void ODWaveTrackTaskQueue::AddWaveTrack(
const std::shared_ptr< WaveTrack > &track)
{
mTracksMutex.Lock();
@ -117,8 +119,7 @@ void ODWaveTrackTaskQueue::AddTask(std::unique_ptr<ODTask> &&mtask)
{
//task->GetWaveTrack(i) may return NULL, but we handle it by checking before using.
//The other worry that the WaveTrack returned and was deleted in the meantime is also
//handled since mQueuesMutex is locked one up in the stack from here,
//and WaveTrack deletion is bound to that.
//handled by keeping standard weak pointers to tracks, which give thread safety.
mTracks.push_back(task->GetWaveTrack(i));
}
@ -126,32 +127,15 @@ void ODWaveTrackTaskQueue::AddTask(std::unique_ptr<ODTask> &&mtask)
}
///Removes a track from the list. Also notifies mTasks to stop using references
///to the instance in a thread-safe manner (may block)
void ODWaveTrackTaskQueue::RemoveWaveTrack(WaveTrack* track)
{
if(track)
{
mTasksMutex.Lock();
for(unsigned int i=0;i<mTasks.size();i++)
mTasks[i]->StopUsingWaveTrack(track);
mTasksMutex.Unlock();
mTracksMutex.Lock();
for(unsigned int i=0;i<mTracks.size();i++)
if(mTracks[i]==track)
mTracks.erase(mTracks.begin()+i--);//decrement i after the removal.
mTracksMutex.Unlock();
}
}
//if the wavetrack is in this queue, and is not the only wavetrack, clones the tasks and schedules it.
void ODWaveTrackTaskQueue::MakeWaveTrackIndependent(WaveTrack* track)
void ODWaveTrackTaskQueue::MakeWaveTrackIndependent(
const std::shared_ptr< WaveTrack > &track)
{
// First remove expired weak pointers
Compress();
mTracksMutex.Lock();
if(mTracks.size()<2)
{
//if there is only one track, it is already independent.
@ -161,17 +145,16 @@ void ODWaveTrackTaskQueue::MakeWaveTrackIndependent(WaveTrack* track)
for(unsigned int i=0;i<mTracks.size();i++)
{
if(mTracks[i]==track)
if ( mTracks[i].lock() == track )
{
mTracksMutex.Unlock();//release the lock, since RemoveWaveTrack is a public threadsafe method.
RemoveWaveTrack(mTracks[i]);
mTracks[i].reset();
//clone the items in order and add them to the ODManager.
mTasksMutex.Lock();
for(unsigned int j=0;j<mTasks.size();j++)
{
auto task = mTasks[j]->Clone();
task->AddWaveTrack(track);
mTasks[j]->StopUsingWaveTrack( track.get() );
//AddNewTask requires us to relinquish this lock. However, it is safe because ODManager::MakeWaveTrackIndependent
//has already locked the m_queuesMutex.
mTasksMutex.Unlock();
@ -181,7 +164,6 @@ void ODWaveTrackTaskQueue::MakeWaveTrackIndependent(WaveTrack* track)
mTasksMutex.Lock();
}
mTasksMutex.Unlock();
mTracksMutex.Lock();
break;
}
}
@ -207,7 +189,8 @@ void ODWaveTrackTaskQueue::DemandTrackUpdate(WaveTrack* track, double seconds)
//Replaces all instances of a wavetracck with a NEW one (effectively transferes the task.)
void ODWaveTrackTaskQueue::ReplaceWaveTrack(Track *oldTrack, Track *newTrack)
void ODWaveTrackTaskQueue::ReplaceWaveTrack(Track *oldTrack,
const std::shared_ptr<Track> &newTrack)
{
if(oldTrack)
{
@ -218,26 +201,17 @@ void ODWaveTrackTaskQueue::ReplaceWaveTrack(Track *oldTrack, Track *newTrack)
mTracksMutex.Lock();
for(unsigned int i=0;i<mTracks.size();i++)
if(mTracks[i]==oldTrack)
mTracks[i] = static_cast<WaveTrack*>( newTrack );
if ( mTracks[i].lock().get() == oldTrack )
mTracks[i] = std::static_pointer_cast<WaveTrack>( newTrack );
mTracksMutex.Unlock();
}
}
//returns the wavetrack at position x.
WaveTrack* ODWaveTrackTaskQueue::GetWaveTrack(size_t x)
{
WaveTrack* ret = NULL;
mTracksMutex.Lock();
if (x < mTracks.size())
ret = mTracks[x];
mTracksMutex.Unlock();
return ret;
}
///returns the number of wavetracks in this queue.
int ODWaveTrackTaskQueue::GetNumWaveTracks()
{
Compress();
int ret = 0;
mTracksMutex.Lock();
ret=mTracks.size();
@ -271,6 +245,8 @@ ODTask* ODWaveTrackTaskQueue::GetTask(size_t x)
//returns true if either tracks or tasks are empty
bool ODWaveTrackTaskQueue::IsEmpty()
{
Compress();
bool isEmpty;
mTracksMutex.Lock();
isEmpty = mTracks.size()<=0;
@ -342,3 +318,13 @@ void ODWaveTrackTaskQueue::FillTipForWaveTrack( const WaveTrack * t, wxString &t
}
}
void ODWaveTrackTaskQueue::Compress()
{
mTracksMutex.Lock();
auto begin = mTracks.begin(), end = mTracks.end(),
new_end = std::remove_if( begin, end,
[]( const std::weak_ptr<WaveTrack> &ptr ){ return ptr.expired(); } );
mTracks.erase( new_end, end );
mTracksMutex.Unlock();
}

View File

@ -42,31 +42,26 @@ class ODWaveTrackTaskQueue final
///Adds a track to the associated list.
void AddWaveTrack(WaveTrack* track);
///Removes a track from the list. Also notifies mTasks to stop using references
///to the instance in a thread-safe manner (may block)
void RemoveWaveTrack(WaveTrack* track);
void AddWaveTrack( const std::shared_ptr< WaveTrack > &track);
///changes the tasks associated with this Waveform to process the task from a different point in the track
void DemandTrackUpdate(WaveTrack* track, double seconds);
///replaces all instances of a WaveTrack within this task with another.
void ReplaceWaveTrack(Track *oldTrack, Track *newTrack);
void ReplaceWaveTrack(Track *oldTrack,
const std::shared_ptr<Track> &newTrack);
//if the wavetrack is in this queue, and is not the only wavetrack, clones the tasks and schedules it.
void MakeWaveTrackIndependent(WaveTrack* track);
void MakeWaveTrackIndependent( const std::shared_ptr< WaveTrack > &track);
///returns whether or not this queue's task list and another's can merge together, as when we make two mono tracks stereo.
bool CanMergeWith(ODWaveTrackTaskQueue* otherQueue);
void MergeWaveTrack(WaveTrack* track);
void MergeWaveTrack( const std::shared_ptr< WaveTrack > &track);
//returns true if the agrument is in the WaveTrack list.
bool ContainsWaveTrack(const WaveTrack* track);
//returns the wavetrack at position x.
WaveTrack* GetWaveTrack(size_t x);
///returns the number of wavetracks in this queue.
int GetNumWaveTracks();
@ -96,12 +91,15 @@ class ODWaveTrackTaskQueue final
protected:
// Remove expired weak pointers to tracks
void Compress();
//because we need to save this around for the tool tip.
wxString mTipMsg;
///the list of tracks associated with this queue.
std::vector<WaveTrack*> mTracks;
std::vector< std::weak_ptr< WaveTrack > > mTracks;
ODLock mTracksMutex;
///the list of tasks associated with the tracks. This class owns these tasks.

View File

@ -889,7 +889,8 @@ void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &)
//On Demand - join the queues together.
if (ODManager::IsInstanceCreated())
if (!ODManager::Instance()->MakeWaveTrackDependent(partner, pTrack))
if (!ODManager::Instance()
->MakeWaveTrackDependent(partner->SharedPointer<WaveTrack>(), pTrack))
{
;
//TODO: in the future, we will have to check the return value of MakeWaveTrackDependent -
@ -927,7 +928,8 @@ void WaveTrackMenuTable::SplitStereo(bool stereo)
//On Demand - have each channel add its own.
if (ODManager::IsInstanceCreated())
ODManager::Instance()->MakeWaveTrackIndependent(channel);
ODManager::Instance()->MakeWaveTrackIndependent(
channel->SharedPointer<WaveTrack>() );
//make sure no channel is smaller than its minimum height
if (view.GetHeight() < view.GetMinimizedHeight())
view.SetHeight(view.GetMinimizedHeight());