/********************************************************************** Audacity: A Digital Audio Editor ODComputeSummaryTask.cpp Created by Michael Chinen (mchinen) on 6/8/08. Audacity(R) is copyright (c) 1999-2008 Audacity Team. License: GPL v2. See License.txt. ******************************************************************//** \class ODComputeSummaryTask \brief Computes the summary data for a PCM (WAV) file and writes it to disk, updating the ODPCMAliasBlockFile and the GUI of the newly available data. *//*******************************************************************/ #include "ODComputeSummaryTask.h" #include "../blockfile/ODPCMAliasBlockFile.h" #include "../Sequence.h" #include "../WaveTrack.h" #include //36 blockfiles > 3 minutes stereo 44.1kHz per ODTask::DoSome #define kNumBlockFilesPerDoSome 36 ///Creates a NEW task that computes summaries for a wavetrack that needs to be specified through SetWaveTrack() ODComputeSummaryTask::ODComputeSummaryTask() { mMaxBlockFiles = 0; mComputedBlockFiles = 0; mHasUpdateRan=false; } movable_ptr ODComputeSummaryTask::Clone() const { auto clone = make_movable(); clone->mDemandSample = GetDemandSample(); return std::move(clone); } ///releases memory that the ODTask owns. Subclasses should override. void ODComputeSummaryTask::Terminate() { //The terminate block won't allow DoSomeInternal and this method to be run async, so this is thread-safe. //Deref the block files since they are ref'ed when put into the array. mBlockFilesMutex.Lock(); for(unsigned int i=0;iDeref(); mBlockFiles.clear(); mBlockFilesMutex.Unlock(); } ///Computes and writes the data for one BlockFile if it still has a refcount. void ODComputeSummaryTask::DoSomeInternal() { if(mBlockFiles.size()<=0) { mPercentCompleteMutex.Lock(); mPercentComplete = 1.0; mPercentCompleteMutex.Unlock(); return; } ODPCMAliasBlockFile* bf; sampleCount blockStartSample = 0; sampleCount blockEndSample = 0; bool success =false; mBlockFilesMutex.Lock(); for(size_t i=0; i < mWaveTracks.size() && mBlockFiles.size();i++) { bf = mBlockFiles[0]; //first check to see if the ref count is at least 2. It should have one //from when we added it to this instance's mBlockFiles array, and one from //the Wavetrack/sequence. If it doesn't it has been deleted and we should forget it. if(bf->RefCount()>=2) { bf->DoWriteSummary(); success = true; blockStartSample = bf->GetStart(); blockEndSample = blockStartSample + bf->GetLength(); mComputedBlockFiles++; } else { //the waveform in the wavetrack now is shorter, so we need to update mMaxBlockFiles //because now there is less work to do. mMaxBlockFiles--; } //Release the refcount we placed on it. bf->Deref(); //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(); //upddate the gui for all associated blocks. It doesn't matter that we're hitting more wavetracks then we should //because this loop runs a number of times equal to the number of tracks, they probably are getting processed in //the next iteration at the same sample window. mWaveTrackMutex.Lock(); for(size_t i=0;iAddInvalidRegion(blockStartSample,blockEndSample); } mWaveTrackMutex.Unlock(); } mBlockFilesMutex.Unlock(); //update percentage complete. CalculatePercentComplete(); } ///compute the next time we should take a break in terms of overall percentage. ///We want to do a constant number of blockfiles. float ODComputeSummaryTask::ComputeNextWorkUntilPercentageComplete() { if(mMaxBlockFiles==0) return 1.0; float nextPercent; mPercentCompleteMutex.Lock(); nextPercent=mPercentComplete + ((float)kNumBlockFilesPerDoSome/(mMaxBlockFiles+1)); mPercentCompleteMutex.Unlock(); return nextPercent; } void ODComputeSummaryTask::MarkUpdateRan() { mHasUpdateRanMutex.Lock(); mHasUpdateRan=true; mHasUpdateRanMutex.Unlock(); } bool ODComputeSummaryTask::HasUpdateRan() { bool ret; mHasUpdateRanMutex.Lock(); ret = mHasUpdateRan; mHasUpdateRanMutex.Unlock(); return ret; } void ODComputeSummaryTask::CalculatePercentComplete() { bool hasUpdateRan; hasUpdateRan = HasUpdateRan(); mPercentCompleteMutex.Lock(); if(hasUpdateRan) mPercentComplete = (float) 1.0 - ((float)mBlockFiles.size() / (mMaxBlockFiles+1)); else mPercentComplete =0.0; mPercentCompleteMutex.Unlock(); } ///creates the order of the wavetrack to load. ///by default left to right, or frome the point the user has clicked. void ODComputeSummaryTask::Update() { std::vector tempBlocks; mWaveTrackMutex.Lock(); for(size_t j=0;jGetClips()) { 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. Sequence::DeleteUpdateMutexLocker locker(*seq); //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 insertCursor; insertCursor =0;//OD TODO:see if this works, removed from inner loop (bfore was n*n) for(i=0; i<(int)blocks->size(); i++) { //if there is data but no summary, this blockfile needs summarizing. SeqBlock &block = (*blocks)[i]; BlockFile *const file = block.f; if(file->IsDataAvailable() && !file->IsSummaryAvailable()) { file->Ref(); ODPCMAliasBlockFile *const odpcmaFile = static_cast(file); odpcmaFile->SetStart(block.start); odpcmaFile->SetClipOffset((sampleCount)(clip->GetStartTime()*clip->GetRate())); //these will always be linear within a sequence-lets take advantage of this by keeping a cursor. while(insertCursor<(int)tempBlocks.size()&& (sampleCount)(tempBlocks[insertCursor]->GetStart()+tempBlocks[insertCursor]->GetClipOffset()) < (sampleCount)(odpcmaFile->GetStart()+odpcmaFile->GetClipOffset())) insertCursor++; tempBlocks.insert(tempBlocks.begin() + insertCursor++, odpcmaFile); } } } } } mWaveTrackMutex.Unlock(); //get the NEW order. mBlockFilesMutex.Lock(); OrderBlockFiles(tempBlocks); mBlockFilesMutex.Unlock(); MarkUpdateRan(); } ///Computes the summary calculation queue order of the blockfiles void ODComputeSummaryTask::OrderBlockFiles(std::vector &unorderedBlocks) { //we are going to take things out of the array. But first deref them since we ref them when we put them in. for(unsigned int i=0;iDeref(); mBlockFiles.clear(); //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 assumes that the array is sorted in time. //find the startpoint sampleCount processStartSample = GetDemandSample(); for(int i= ((int)unorderedBlocks.size())-1;i>= 0;i--) { //check to see if the refcount is at least two before we add it to the list. //There should be one Ref() from the one added by this ODTask, and one from the track. //If there isn't, then the block was deleted for some reason and we should ignore it. if(unorderedBlocks[i]->RefCount()>=2) { //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 mBlockFiles.insert(mBlockFiles.begin()+0,unorderedBlocks[i]); } else { //otherwise no priority mBlockFiles.push_back(unorderedBlocks[i]); } if(mMaxBlockFiles< (int) mBlockFiles.size()) mMaxBlockFiles = mBlockFiles.size(); } else { //Otherwise, let it be deleted and forget about it. unorderedBlocks[i]->Deref(); } } }