365 lines
9.2 KiB
C++
365 lines
9.2 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
ODTask.cpp
|
|
|
|
Created by Michael Chinen (mchinen)
|
|
Audacity(R) is copyright (c) 1999-2008 Audacity Team.
|
|
License: GPL v2. See License.txt.
|
|
|
|
******************************************************************//**
|
|
|
|
\class ODTask
|
|
\brief ODTask is an abstract class that outlines the methods that will be used to
|
|
support On-Demand background loading of files. These ODTasks are generally meant to be run
|
|
in a background thread.
|
|
|
|
*//*******************************************************************/
|
|
|
|
|
|
#include "ODTask.h"
|
|
|
|
#include "ODManager.h"
|
|
#include "../WaveClip.h"
|
|
#include "../WaveTrack.h"
|
|
#include "../Project.h"
|
|
#include "../UndoManager.h"
|
|
//temporarily commented out till it is added to all projects
|
|
//#include "../Profiler.h"
|
|
|
|
|
|
wxDEFINE_EVENT(EVT_ODTASK_COMPLETE, wxCommandEvent);
|
|
|
|
/// Constructs an ODTask
|
|
ODTask::ODTask()
|
|
: mDemandSample(0)
|
|
{
|
|
|
|
static int sTaskNumber=0;
|
|
mPercentComplete=0;
|
|
mDoingTask=false;
|
|
mTerminate = false;
|
|
mNeedsODUpdate=false;
|
|
mIsRunning = false;
|
|
|
|
mTaskNumber=sTaskNumber++;
|
|
}
|
|
|
|
//outside code must ensure this task is not scheduled again.
|
|
void ODTask::TerminateAndBlock()
|
|
{
|
|
//one mutex pair for the value of mTerminate
|
|
mTerminateMutex.Lock();
|
|
mTerminate=true;
|
|
//release all data the derived class may have allocated
|
|
mTerminateMutex.Unlock();
|
|
|
|
//and one mutex pair for the exit of the function
|
|
mBlockUntilTerminateMutex.Lock();
|
|
//TODO lock mTerminate?
|
|
mBlockUntilTerminateMutex.Unlock();
|
|
|
|
//wait till we are out of doSome() to terminate.
|
|
Terminate();
|
|
}
|
|
|
|
///Do a modular part of the task. For example, if the task is to load the entire file, load one BlockFile.
|
|
///Relies on DoSomeInternal(), which the subclasses must implement.
|
|
///@param amountWork the percent amount of the total job to do. 1.0 represents the entire job. the default of 0.0
|
|
/// will do the smallest unit of work possible
|
|
void ODTask::DoSome(float amountWork)
|
|
{
|
|
SetIsRunning(true);
|
|
mBlockUntilTerminateMutex.Lock();
|
|
|
|
// wxPrintf("%s %i subtask starting on NEW thread with priority\n", GetTaskName(),GetTaskNumber());
|
|
|
|
mDoingTask=mTaskStarted=true;
|
|
|
|
float workUntil = amountWork+PercentComplete();
|
|
|
|
|
|
|
|
//check periodically to see if we should exit.
|
|
mTerminateMutex.Lock();
|
|
if(mTerminate)
|
|
{
|
|
mTerminateMutex.Unlock();
|
|
SetIsRunning(false);
|
|
mBlockUntilTerminateMutex.Unlock();
|
|
return;
|
|
}
|
|
mTerminateMutex.Unlock();
|
|
|
|
Update();
|
|
|
|
|
|
if(UsesCustomWorkUntilPercentage())
|
|
workUntil = ComputeNextWorkUntilPercentageComplete();
|
|
|
|
if(workUntil<PercentComplete())
|
|
workUntil = PercentComplete();
|
|
|
|
//Do Some of the task.
|
|
|
|
mTerminateMutex.Lock();
|
|
while(PercentComplete() < workUntil && PercentComplete() < 1.0 && !mTerminate)
|
|
{
|
|
wxThread::This()->Yield();
|
|
//release within the loop so we can cut the number of iterations short
|
|
|
|
DoSomeInternal(); //keep the terminate mutex on so we don't remo
|
|
mTerminateMutex.Unlock();
|
|
//check to see if ondemand has been called
|
|
if(GetNeedsODUpdate() && PercentComplete() < 1.0)
|
|
ODUpdate();
|
|
|
|
|
|
//But add the mutex lock back before we check the value again.
|
|
mTerminateMutex.Lock();
|
|
}
|
|
mTerminateMutex.Unlock();
|
|
mDoingTask=false;
|
|
|
|
mTerminateMutex.Lock();
|
|
//if it is not done, put it back onto the ODManager queue.
|
|
if(PercentComplete() < 1.0&& !mTerminate)
|
|
{
|
|
ODManager::Instance()->AddTask(this);
|
|
|
|
//we did a bit of progress - we should allow a resave.
|
|
ODLocker locker{ &AllProjects::Mutex() };
|
|
for ( auto pProject : AllProjects{} )
|
|
{
|
|
if(IsTaskAssociatedWithProject(pProject.get()))
|
|
{
|
|
//mark the changes so that the project can be resaved.
|
|
UndoManager::Get( *pProject ).SetODChangesFlag();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// wxPrintf("%s %i is %f done\n", GetTaskName(),GetTaskNumber(),PercentComplete());
|
|
}
|
|
else
|
|
{
|
|
//for profiling, uncomment and look in audacity.app/exe's folder for AudacityProfile.txt
|
|
//static int tempLog =0;
|
|
//if(++tempLog % 5==0)
|
|
//END_TASK_PROFILING("On Demand Drag and Drop 5 80 mb files into audacity, 5 wavs per task");
|
|
//END_TASK_PROFILING("On Demand open an 80 mb wav stereo file");
|
|
|
|
wxCommandEvent event( EVT_ODTASK_COMPLETE );
|
|
|
|
ODLocker locker{ &AllProjects::Mutex() };
|
|
for ( auto pProject : AllProjects{} )
|
|
{
|
|
if(IsTaskAssociatedWithProject(pProject.get()))
|
|
{
|
|
//this assumes tasks are only associated with one project.
|
|
pProject->wxEvtHandler::AddPendingEvent(event);
|
|
//mark the changes so that the project can be resaved.
|
|
UndoManager::Get( *pProject ).SetODChangesFlag();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// wxPrintf("%s %i complete\n", GetTaskName(),GetTaskNumber());
|
|
}
|
|
mTerminateMutex.Unlock();
|
|
SetIsRunning(false);
|
|
mBlockUntilTerminateMutex.Unlock();
|
|
}
|
|
|
|
bool ODTask::IsTaskAssociatedWithProject(AudacityProject* proj)
|
|
{
|
|
for (auto tr : TrackList::Get( *proj ).Any<const WaveTrack>())
|
|
{
|
|
//go over all tracks in the project
|
|
//look inside our task's track list for one that matches this projects one.
|
|
mWaveTrackMutex.Lock();
|
|
for(int i=0;i<(int)mWaveTracks.size();i++)
|
|
{
|
|
if ( mWaveTracks[i].lock().get() == tr )
|
|
{
|
|
//if we find one, then the project is associated with us;return true
|
|
mWaveTrackMutex.Unlock();
|
|
return true;
|
|
}
|
|
}
|
|
mWaveTrackMutex.Unlock();
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
void ODTask::ODUpdate()
|
|
{
|
|
Update();
|
|
ResetNeedsODUpdate();
|
|
}
|
|
|
|
void ODTask::SetIsRunning(bool value)
|
|
{
|
|
mIsRunningMutex.Lock();
|
|
mIsRunning=value;
|
|
mIsRunningMutex.Unlock();
|
|
}
|
|
|
|
bool ODTask::IsRunning()
|
|
{
|
|
bool ret;
|
|
mIsRunningMutex.Lock();
|
|
ret= mIsRunning;
|
|
mIsRunningMutex.Unlock();
|
|
return ret;
|
|
}
|
|
|
|
sampleCount ODTask::GetDemandSample() const
|
|
{
|
|
sampleCount retval;
|
|
mDemandSampleMutex.Lock();
|
|
retval = mDemandSample;
|
|
mDemandSampleMutex.Unlock();
|
|
return retval;
|
|
}
|
|
|
|
void ODTask::SetDemandSample(sampleCount sample)
|
|
{
|
|
|
|
mDemandSampleMutex.Lock();
|
|
mDemandSample=sample;
|
|
mDemandSampleMutex.Unlock();
|
|
}
|
|
|
|
|
|
///return the amount of the task that has been completed. 0.0 to 1.0
|
|
float ODTask::PercentComplete()
|
|
{
|
|
mPercentCompleteMutex.Lock();
|
|
float ret = mPercentComplete;
|
|
mPercentCompleteMutex.Unlock();
|
|
return ret;
|
|
}
|
|
|
|
///return
|
|
bool ODTask::IsComplete()
|
|
{
|
|
return PercentComplete() >= 1.0 && !IsRunning();
|
|
}
|
|
|
|
|
|
std::shared_ptr< WaveTrack > ODTask::GetWaveTrack(int i)
|
|
{
|
|
std::shared_ptr< WaveTrack > track;
|
|
mWaveTrackMutex.Lock();
|
|
if(i<(int)mWaveTracks.size())
|
|
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( const std::shared_ptr< WaveTrack > &track)
|
|
{
|
|
mWaveTracks.push_back(track);
|
|
}
|
|
|
|
|
|
int ODTask::GetNumWaveTracks()
|
|
{
|
|
int num;
|
|
mWaveTrackMutex.Lock();
|
|
num = (int)mWaveTracks.size();
|
|
mWaveTrackMutex.Unlock();
|
|
return num;
|
|
}
|
|
|
|
|
|
void ODTask::SetNeedsODUpdate()
|
|
{
|
|
mNeedsODUpdateMutex.Lock();
|
|
mNeedsODUpdate=true;
|
|
mNeedsODUpdateMutex.Unlock();
|
|
}
|
|
bool ODTask::GetNeedsODUpdate()
|
|
{
|
|
bool ret;
|
|
mNeedsODUpdateMutex.Lock();
|
|
ret=mNeedsODUpdate;
|
|
mNeedsODUpdateMutex.Unlock();
|
|
return ret;
|
|
}
|
|
|
|
void ODTask::ResetNeedsODUpdate()
|
|
{
|
|
mNeedsODUpdateMutex.Lock();
|
|
mNeedsODUpdate=false;
|
|
mNeedsODUpdateMutex.Unlock();
|
|
}
|
|
|
|
///does an od update and then recalculates the data.
|
|
void ODTask::RecalculatePercentComplete()
|
|
{
|
|
if(GetNeedsODUpdate())
|
|
{
|
|
ODUpdate();
|
|
CalculatePercentComplete();
|
|
}
|
|
}
|
|
|
|
///changes the tasks associated with this Waveform to process the task from a different point in the track
|
|
///@param track the track to update
|
|
///@param seconds the point in the track from which the tasks associated with track should begin processing from.
|
|
void ODTask::DemandTrackUpdate(WaveTrack* track, double seconds)
|
|
{
|
|
bool demandSampleChanged=false;
|
|
mWaveTrackMutex.Lock();
|
|
for(size_t i=0;i<mWaveTracks.size();i++)
|
|
{
|
|
if ( track == mWaveTracks[i].lock().get() )
|
|
{
|
|
auto newDemandSample = (sampleCount)(seconds * track->GetRate());
|
|
demandSampleChanged = newDemandSample != GetDemandSample();
|
|
SetDemandSample(newDemandSample);
|
|
break;
|
|
}
|
|
}
|
|
mWaveTrackMutex.Unlock();
|
|
|
|
if(demandSampleChanged)
|
|
SetNeedsODUpdate();
|
|
|
|
}
|
|
|
|
|
|
void ODTask::StopUsingWaveTrack(WaveTrack* track)
|
|
{
|
|
mWaveTrackMutex.Lock();
|
|
for(size_t i=0;i<mWaveTracks.size();i++)
|
|
{
|
|
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,
|
|
const std::shared_ptr< Track > &newTrack)
|
|
{
|
|
mWaveTrackMutex.Lock();
|
|
for(size_t i=0;i<mWaveTracks.size();i++)
|
|
{
|
|
if(oldTrack == mWaveTracks[i].lock().get())
|
|
{
|
|
mWaveTracks[i] = std::static_pointer_cast<WaveTrack>( newTrack );
|
|
}
|
|
}
|
|
mWaveTrackMutex.Unlock();
|
|
}
|