1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-15 23:59:37 +02:00

checking in cleanup of on-demand files following james' review. also speeds up od cursor updating when user clicks for on-demand

This commit is contained in:
mchinen 2010-03-08 01:52:38 +00:00
parent 016c6ce796
commit 5ccce1ed4b
9 changed files with 123 additions and 181 deletions

View File

@ -99,6 +99,12 @@ class ODDecodeBlockFile : public SimpleBlockFile
///Gets the value that indicates where the first sample in this block corresponds to the global sequence/clip. Only for display use.
sampleCount GetStart(){return mStart;}
//returns the number of samples from the beginning of the track that this blockfile starts at
sampleCount GetGlobalStart(){return mClipOffset+mStart;}
//returns the number of samples from the beginning of the track that this blockfile ends at
sampleCount GetGlobalEnd(){return mClipOffset+mStart+GetLength();}
//Below calls are overrided just so we can take wxlog calls out, which are not threadsafe.
/// Reads the specified data from the aliased file using libsndfile

View File

@ -110,6 +110,13 @@ class ODPCMAliasBlockFile : public PCMAliasBlockFile
///Gets the number of samples the clip associated with this blockfile is offset by.
sampleCount GetClipOffset(){return mClipOffset;}
//returns the number of samples from the beginning of the track that this blockfile starts at
sampleCount GetGlobalStart(){return mClipOffset+mStart;}
//returns the number of samples from the beginning of the track that this blockfile ends at
sampleCount GetGlobalEnd(){return mClipOffset+mStart+GetLength();}
//Below calls are overrided just so we can take wxlog calls out, which are not threadsafe.
/// Reads the specified data from the aliased file using libsndfile

View File

@ -102,7 +102,10 @@ void ODComputeSummaryTask::DoSomeInternal()
//take it out of the array - we are done with it.
mBlockFiles.erase(mBlockFiles.begin());
//This is a bit of a convenience in case someone tries to terminate the task by closing the trackpanel or window.
//ODComputeSummaryTask::Terminate() uses this lock to remove everything, and we don't want it to wait since the UI is being blocked.
mBlockFilesMutex.Unlock();
wxThread::This()->Yield();
mBlockFilesMutex.Lock();
@ -187,21 +190,22 @@ void ODComputeSummaryTask::Update()
//gather all the blockfiles that we should process in the wavetrack.
WaveClipList::compatibility_iterator node = mWaveTracks[j]->GetClipIterator();
int numBlocksDone;
while(node) {
clip = node->GetData();
seq = clip->GetSequence();
//TODO:this lock is way to big since the whole file is one sequence. find a way to break it down.
//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.
seq->LockDeleteUpdateMutex();
//See Sequence::Delete() for why need this for now..
//We don't need the mBlockFilesMutex here because it is only for the vector list.
//These are existing blocks, and its wavetrack or blockfiles won't be deleted because
//of the respective mWaveTrackMutex lock and LockDeleteUpdateMutex() call.
blocks = clip->GetSequenceBlockArray();
int i;
int numBlocksIn;
int insertCursor;
numBlocksIn=0;
insertCursor =0;//OD TODO:see if this works, removed from inner loop (bfore was n*n)
for(i=0; i<(int)blocks->GetCount(); i++)
@ -219,21 +223,9 @@ void ODComputeSummaryTask::Update()
(sampleCount)(((ODPCMAliasBlockFile*)blocks->Item(i)->f)->GetStart()+((ODPCMAliasBlockFile*)blocks->Item(i)->f)->GetClipOffset()))
insertCursor++;
tempBlocks.insert(tempBlocks.begin()+insertCursor++,(ODPCMAliasBlockFile*)blocks->Item(i)->f);
//this next bit ensures that we are spacing the blockfiles over multiple wavetracks evenly.
//if((j+1)*numBlocksIn>tempBlocks.size())
// tempBlocks.push_back((ODPCMAliasBlockFile*)blocks->Item(i)->f);
// else
// tempBlocks.insert(tempBlocks.begin()+(j+1)*numBlocksIn, (ODPCMAliasBlockFile*)blocks->Item(i)->f);
//
numBlocksIn++;
}
}
numBlocksDone = numBlocksIn;
seq->UnlockDeleteUpdateMutex();
node = node->GetNext();
}
@ -260,7 +252,8 @@ void ODComputeSummaryTask::OrderBlockFiles(std::vector<ODPCMAliasBlockFile*> &un
mBlockFiles.clear();
//TODO:order the blockfiles into our queue in a fancy convenient way. (this could be user-prefs)
//for now just put them in linear. We start the order from the first block that includes the ondemand sample
//(which the user sets by clicking.) note that this code is pretty hacky - it assumes that the array is sorted in time.
//(which the user sets by clicking.)
//Note that this code assumes that the array is sorted in time.
//find the startpoint
sampleCount processStartSample = GetDemandSample();
@ -271,9 +264,12 @@ void ODComputeSummaryTask::OrderBlockFiles(std::vector<ODPCMAliasBlockFile*> &un
//If there isn't, then the block was deleted for some reason and we should ignore it.
if(unorderedBlocks[i]->RefCount()>=2)
{
if(mBlockFiles.size() && (unorderedBlocks[i]->GetStart()+unorderedBlocks[i]->GetClipOffset()) + unorderedBlocks[i]->GetLength() >=processStartSample &&
( (mBlockFiles[0]->GetStart()+mBlockFiles[0]->GetClipOffset()) + mBlockFiles[0]->GetLength() < processStartSample ||
(unorderedBlocks[i]->GetStart()+unorderedBlocks[i]->GetClipOffset()) <= (mBlockFiles[0]->GetStart() +mBlockFiles[0]->GetClipOffset()))
//test if the blockfiles are near the task cursor. we use the last mBlockFiles[0] as our point of reference
//and add ones that are closer.
if(mBlockFiles.size() &&
unorderedBlocks[i]->GetGlobalEnd() >= processStartSample &&
( mBlockFiles[0]->GetGlobalEnd() < processStartSample ||
unorderedBlocks[i]->GetGlobalStart() <= mBlockFiles[0]->GetGlobalStart())
)
{
//insert at the front of the list if we get blockfiles that are after the demand sample
@ -294,10 +290,4 @@ void ODComputeSummaryTask::OrderBlockFiles(std::vector<ODPCMAliasBlockFile*> &un
}
}
}
void ODComputeSummaryTask::ODUpdate()
{
//clear old blockFiles and do something smarter.
}
}

View File

@ -68,9 +68,6 @@ protected:
///Then it updates in the OD manner.
virtual void Update();
///Readjusts the blockfile order to start at the new cursor.
virtual void ODUpdate();
///Orders the input as either On-Demand or default layered order.
void OrderBlockFiles(std::vector<ODPCMAliasBlockFile*> &unorderedBlocks);

View File

@ -124,8 +124,7 @@ void ODDecodeTask::Update()
//gather all the blockfiles that we should process in the wavetrack.
WaveClipList::compatibility_iterator node = mWaveTracks[j]->GetClipIterator();
int numBlocksDone;
while(node) {
clip = node->GetData();
seq = clip->GetSequence();
@ -135,11 +134,8 @@ void ODDecodeTask::Update()
//See Sequence::Delete() for why need this for now..
blocks = clip->GetSequenceBlockArray();
int i;
int numBlocksIn;
int insertCursor;
numBlocksIn=0;
insertCursor =0;//OD TODO:see if this works, removed from inner loop (bfore was n*n)
for(i=0; i<(int)blocks->GetCount(); i++)
{
@ -156,12 +152,9 @@ void ODDecodeTask::Update()
(sampleCount)(((ODDecodeBlockFile*)blocks->Item(i)->f)->GetStart()+((ODDecodeBlockFile*)blocks->Item(i)->f)->GetClipOffset()))
insertCursor++;
tempBlocks.insert(tempBlocks.begin()+insertCursor++,(ODDecodeBlockFile*)blocks->Item(i)->f);
numBlocksIn++;
}
}
numBlocksDone = numBlocksIn;
seq->UnlockDeleteUpdateMutex();
node = node->GetNext();
@ -196,10 +189,14 @@ void ODDecodeTask::OrderBlockFiles(std::vector<ODDecodeBlockFile*> &unorderedBlo
//If there isn't, then the block was deleted for some reason and we should ignore it.
if(unorderedBlocks[i]->RefCount()>=2)
{
if(mBlockFiles.size() && (unorderedBlocks[i]->GetStart()+unorderedBlocks[i]->GetClipOffset()) + unorderedBlocks[i]->GetLength() >=processStartSample &&
( (mBlockFiles[0]->GetStart()+mBlockFiles[0]->GetClipOffset()) + mBlockFiles[0]->GetLength() < processStartSample ||
(unorderedBlocks[i]->GetStart()+unorderedBlocks[i]->GetClipOffset()) <= (mBlockFiles[0]->GetStart() +mBlockFiles[0]->GetClipOffset()))
)
//test if the blockfiles are near the task cursor. we use the last mBlockFiles[0] as our point of reference
//and add ones that are closer.
//since the order is linear right to left, this will add blocks so that the ones on the right side of the target
//are processed first, with the ones closer being processed earlier. Then the ones on the left side get processed.
if(mBlockFiles.size() &&
unorderedBlocks[i]->GetGlobalEnd() >= processStartSample &&
( mBlockFiles[0]->GetGlobalEnd() < processStartSample ||
unorderedBlocks[i]->GetGlobalStart() <= mBlockFiles[0]->GetGlobalStart()) )
{
//insert at the front of the list if we get blockfiles that are after the demand sample
mBlockFiles.insert(mBlockFiles.begin()+0,unorderedBlocks[i]);
@ -221,13 +218,6 @@ void ODDecodeTask::OrderBlockFiles(std::vector<ODDecodeBlockFile*> &unorderedBlo
}
void ODDecodeTask::ODUpdate()
{
//clear old blockFiles and do something smarter.
}
///there could be the ODBlockFiles of several FLACs in one track (after copy and pasting)
///so we keep a list of decoders that keep track of the file names, etc, and check the blocks against them.

View File

@ -80,9 +80,6 @@ protected:
///Then it updates in the OD manner.
virtual void Update();
///Readjusts the blockfile order to start at the new cursor.
virtual void ODUpdate();
///Orders the input as either On-Demand or default layered order.
void OrderBlockFiles(std::vector<ODDecodeBlockFile*> &unorderedBlocks);

View File

@ -1,11 +1,18 @@
/*
* ODManager.cpp
* Audacity
*
* Created by apple on 6/8/08.
* Copyright 2008 __MyCompanyName__. All rights reserved.
*
*/
/**********************************************************************
Audacity - A Digital Audio Editor
Copyright 1999-2010 Audacity Team
File License: wxWidgets
Michael Chinen
******************************************************************//**
\file ODManager.cpp
\brief Singleton ODManager class. Is the bridge between client side
ODTask requests and internals.
*//*******************************************************************/
#include "ODManager.h"
#include "ODTask.h"
@ -24,82 +31,17 @@ bool gPause=false; //to be loaded in and used with Pause/Resume before ODMan ini
/// a flag that is set if we have loaded some OD blockfiles from PCM.
bool sHasLoadedOD=false;
ODManager* ODManager::pMan=NULL;
//init the accessor function pointer - use the first time version of the interface fetcher
//first we need to typedef the function pointer type because the compiler doesn't support it in the raw
typedef ODManager* (*pfodman)();
pfodman ODManager::Instance = &(ODManager::InstanceFirstTime);
//libsndfile is not threadsafe - this deals with it
ODLock sLibSndFileMutex;
DEFINE_EVENT_TYPE(EVT_ODTASK_UPDATE)
//OD files are "greater than" non OD files, to produce a sort that has
//OD Files at the end
int CompareODFileName(const wxString& first, const wxString& second)
{
bool firstIsOD = false;
bool secondIsOD = false;
if(first.EndsWith(wxT("wav"))||first.EndsWith(wxT("WAV"))||
first.EndsWith(wxT("wave"))||first.EndsWith(wxT("WAVE"))||
first.EndsWith(wxT("Wav"))||first.EndsWith(wxT("Wave"))||
first.EndsWith(wxT("aif"))||first.EndsWith(wxT("AIF"))||
first.EndsWith(wxT("aiff"))||first.EndsWith(wxT("AIFF"))||
first.EndsWith(wxT("aiff"))||first.EndsWith(wxT("Aif")) )
{
firstIsOD=true;
}
if(second.EndsWith(wxT("wav"))||second.EndsWith(wxT("WAV"))||
second.EndsWith(wxT("wave"))||second.EndsWith(wxT("WAVE"))||
second.EndsWith(wxT("Wav"))||second.EndsWith(wxT("Wave"))||
second.EndsWith(wxT("aif"))||second.EndsWith(wxT("AIF"))||
second.EndsWith(wxT("aiff"))||second.EndsWith(wxT("AIFF"))||
second.EndsWith(wxT("aiff"))||second.EndsWith(wxT("Aif")) )
{
secondIsOD=true;
}
if(firstIsOD && !secondIsOD)
return 1;
else if(secondIsOD&&!firstIsOD)
return -1;
return first.Cmp(second);
}
//same as above but OD is less than non-OD
int CompareODFirstFileName(const wxString& first, const wxString& second)
{
bool firstIsOD = false;
bool secondIsOD = false;
if(first.EndsWith(wxT("wav"))||first.EndsWith(wxT("WAV"))||
first.EndsWith(wxT("wave"))||first.EndsWith(wxT("WAVE"))||
first.EndsWith(wxT("Wav"))||first.EndsWith(wxT("Wave"))||
first.EndsWith(wxT("aif"))||first.EndsWith(wxT("AIF"))||
first.EndsWith(wxT("aiff"))||first.EndsWith(wxT("AIFF"))||
first.EndsWith(wxT("aiff"))||first.EndsWith(wxT("Aif")) )
{
firstIsOD=true;
}
if(second.EndsWith(wxT("wav"))||second.EndsWith(wxT("WAV"))||
second.EndsWith(wxT("wave"))||second.EndsWith(wxT("WAVE"))||
second.EndsWith(wxT("Wav"))||second.EndsWith(wxT("Wave"))||
second.EndsWith(wxT("aif"))||second.EndsWith(wxT("AIF"))||
second.EndsWith(wxT("aiff"))||second.EndsWith(wxT("AIFF"))||
second.EndsWith(wxT("aiff"))||second.EndsWith(wxT("Aif")) )
{
secondIsOD=true;
}
if(firstIsOD && !secondIsOD)
return -1;
else if(secondIsOD&&!firstIsOD)
return 1;
//if they are both OD-files, or both non-OD-files, use a normal string comparison
//to get alphabetical sorting
return first.Cmp(second);
}
//using this with wxStringArray::Sort will give you a list that
//is alphabetical, without depending on case. If you use the
//default sort, you will get strings with 'R' before 'a', because it is in caps.
@ -130,7 +72,7 @@ ODManager::ODManager()
mQueueNotEmptyCond = new ODCondition(&mQueueNotEmptyCondLock);
}
//private constructor - delete with static method Quit()
//private destructor - delete with static method Quit()
ODManager::~ODManager()
{
//get rid of all the queues. The queues get rid of the tasks, so we don't worry abut them.
@ -182,6 +124,7 @@ void ODManager::SignalTaskQueueLoop()
void ODManager::RemoveTaskIfInQueue(ODTask* task)
{
mTasksMutex.Lock();
//linear search okay for now, (probably only 1-5 tasks exist at a time.)
for(unsigned int i=0;i<mTasks.size();i++)
{
if(mTasks[i]==task)
@ -206,8 +149,7 @@ void ODManager::AddNewTask(ODTask* task, bool lockMutex)
mQueuesMutex.Lock();
for(unsigned int i=0;i<mQueues.size();i++)
{
//search for a task containing the lead track.
//This may be buggy when adding tracks. We may have to do an exhaustive search instead.
//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)))
queue=mQueues[i];
@ -234,24 +176,29 @@ void ODManager::AddNewTask(ODTask* task, bool lockMutex)
}
}
ODManager* ODManager::Instance()
{
static ODManager* man=NULL;
//this isn't 100% threadsafe but I think Okay for this purpose.
// wxLogDebug(wxT("Fetching Instance\n"));
//that switches out the mutex/null check.
ODManager* ODManager::InstanceFirstTime()
{
gODInitedMutex.Lock();
if(!man)
if(!pMan)
{
man = new ODManager();
man->Init();
pMan = new ODManager();
pMan->Init();
gManagerCreated = true;
}
gODInitedMutex.Unlock();
return man;
//change the accessor function to use the quicker method.
Instance = &(ODManager::InstanceNormal);
return pMan;
}
//faster method of instance fetching once init is done
ODManager* ODManager::InstanceNormal()
{
return pMan;
}
bool ODManager::IsInstanceCreated()
@ -278,7 +225,7 @@ void ODManager::Init()
startThread->Run();
// printf("started thread from init\n");
//destruction is taken care of by wx thread code. TODO:Check if pthreads code does this.
//destruction of thread is taken care of by thread library
}
void ODManager::DecrementCurrentThreads()
@ -328,13 +275,7 @@ void ODManager::Start()
mCurrentThreads++;
mCurrentThreadsMutex.Unlock();
//remove the head
mTasksMutex.Lock();
//task = mTasks[0];
//the thread will add it back to the array if the job is not yet done at the end of the thread's run.
//mTasks.erase(mTasks.begin());
mTasksMutex.Unlock();
//detach a new thread.
thread = new ODTaskThread(mTasks[0]);//task);
@ -392,18 +333,21 @@ void ODManager::Start()
}
//static function that prevents ODTasks from being scheduled
//does not stop currently running tasks from completing their immediate subtask,
//but presumably they will finish within a second
void ODManager::Pause(bool pause)
{
if(IsInstanceCreated())
{
ODManager::Instance()->mPauseLock.Lock();
ODManager::Instance()->mPause = pause;
ODManager::Instance()->mPauseLock.Unlock();
pMan->mPauseLock.Lock();
pMan->mPause = pause;
pMan->mPauseLock.Unlock();
//we should check the queue again.
ODManager::Instance()->mQueueNotEmptyCondLock.Lock();
ODManager::Instance()->mQueueNotEmptyCond->Signal();
ODManager::Instance()->mQueueNotEmptyCondLock.Unlock();
pMan->mQueueNotEmptyCondLock.Lock();
pMan->mQueueNotEmptyCond->Signal();
pMan->mQueueNotEmptyCondLock.Unlock();
}
else
{
@ -420,27 +364,29 @@ void ODManager::Quit()
{
if(IsInstanceCreated())
{
ODManager::Instance()->mTerminateMutex.Lock();
ODManager::Instance()->mTerminate = true;
ODManager::Instance()->mTerminateMutex.Unlock();
pMan->mTerminateMutex.Lock();
pMan->mTerminate = true;
pMan->mTerminateMutex.Unlock();
ODManager::Instance()->mTerminatedMutex.Lock();
while(!ODManager::Instance()->mTerminated)
pMan->mTerminatedMutex.Lock();
while(!pMan->mTerminated)
{
ODManager::Instance()->mTerminatedMutex.Unlock();
pMan->mTerminatedMutex.Unlock();
wxThread::Sleep(200);
//signal the queue not empty condition since the ODMan thread will wait on the queue condition
ODManager::Instance()->mQueueNotEmptyCondLock.Lock();
ODManager::Instance()->mQueueNotEmptyCond->Signal();
ODManager::Instance()->mQueueNotEmptyCondLock.Unlock();
pMan->mQueueNotEmptyCondLock.Lock();
pMan->mQueueNotEmptyCond->Signal();
pMan->mQueueNotEmptyCondLock.Unlock();
ODManager::Instance()->mTerminatedMutex.Lock();
pMan->mTerminatedMutex.Lock();
}
ODManager::Instance()->mTerminatedMutex.Unlock();
pMan->mTerminatedMutex.Unlock();
delete ODManager::Instance();
delete pMan;
//The above while loop waits for ODTasks to finish and the delete removes all tasks from the Queue.
//This function is called from the main audacity event thread, so there should not be more requests for pMan
}
}
@ -572,10 +518,7 @@ void ODManager::UpdateQueues()
ODWaveTrackTaskQueue* queue;
queue = mQueues[i];
mQueuesMutex.Unlock();
AddTask(queue->GetFrontTask());
mQueuesMutex.Lock();
}
}

View File

@ -41,9 +41,7 @@ number of threads.
DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_ODTASK_UPDATE, -1)
///wxstring compare function for sorting filenames with od
int CompareODFileName(const wxString& first, const wxString& second);
int CompareODFirstFileName(const wxString& first, const wxString& second);
///wxstring compare function for sorting case, which is needed to load correctly.
int CompareNoCaseFileName(const wxString& first, const wxString& second);
/// A singleton that manages currently running Tasks on an arbitrary
/// number of threads.
@ -52,8 +50,13 @@ class ODWaveTrackTaskQueue;
class ODManager
{
public:
///Gets the singleton instance - this is a function pointer that points to one of the below two instance calls.
///Note that it is not a member function pointer since it is a static function.
static ODManager* (*Instance)();
///Gets the singleton instance
static ODManager* Instance();
static ODManager* InstanceFirstTime();
///Gets the singleton instance
static ODManager* InstanceNormal();
///Kills the ODMananger Thread.
static void Quit();
@ -132,6 +135,8 @@ class ODManager
///Remove references in our array to Tasks that have been completed/Schedule new ones
void UpdateQueues();
//instance
static ODManager* pMan;
//List of tracks and their active and inactive tasks.
std::vector<ODWaveTrackTaskQueue*> mQueues;

View File

@ -110,7 +110,10 @@ void ODWaveTrackTaskQueue::AddTask(ODTask* task)
mTracksMutex.Lock();
for(int i=0;i<task->GetNumWaveTracks();i++)
{
//ODTask::GetWaveTrack is not threadsafe, but I think we are safe anyway, because the task isn't being run yet?
//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.
mTracks.push_back(task->GetWaveTrack(i));
}
@ -165,7 +168,11 @@ void ODWaveTrackTaskQueue::MakeWaveTrackIndependent(WaveTrack* track)
{
task=mTasks[j]->Clone();
task->AddWaveTrack(track);
mTasksMutex.Unlock(); //AddNewTask assumes no locks.
//AddNewTask requires us to relinquish this lock. However, it is safe because ODManager::MakeWaveTrackIndependent
//has already locked the m_queuesMutex.
mTasksMutex.Unlock();
//AddNewTask locks the m_queuesMutex which is already locked by ODManager::MakeWaveTrackIndependent,
//so we pass a boolean flag telling it not to lock again.
ODManager::Instance()->AddNewTask(task,false);
mTasksMutex.Lock();
}
@ -304,7 +311,7 @@ void ODWaveTrackTaskQueue::RemoveFrontTask()
mTasksMutex.Unlock();
}
///Schedules the front task for immediate execution
///gets the front task for immediate execution
ODTask* ODWaveTrackTaskQueue::GetFrontTask()
{
mTasksMutex.Lock();