/********************************************************************** 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 "../AudacityException.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; mHasUpdateRan=false; } movable_ptr ODComputeSummaryTask::Clone() const { auto clone = make_movable(); clone->mDemandSample = GetDemandSample(); // This std::move is needed to "upcast" the pointer type 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(); 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; } mBlockFilesMutex.Lock(); for(size_t i=0; i < mWaveTracks.size() && mBlockFiles.size();i++) { bool success = false; const auto bf = mBlockFiles[0].lock(); sampleCount blockStartSample = 0; sampleCount blockEndSample = 0; if(bf) { // WriteSummary might throw, but this is a worker thread, so stop // the exceptions here! success = GuardedCall( [&] { bf->DoWriteSummary(); return true; } ); blockStartSample = bf->GetStart(); blockEndSample = blockStartSample + bf->GetLength(); } else { success = true; // The block file disappeared. //the waveform in the wavetrack now is shorter, so we need to update mMaxBlockFiles //because now there is less work to do. mMaxBlockFiles--; } if (success) { //take it out of the array - we are done with it. mBlockFiles.erase(mBlockFiles.begin()); } else // The task does not make progress ; //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(); //update 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. if (success && bf) { 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< std::weak_ptr< ODPCMAliasBlockFile > > tempBlocks; mWaveTrackMutex.Lock(); for(size_t j=0;jGetAllClips()) { 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]; const auto &file = block.f; if(file->IsDataAvailable() && !file->IsSummaryAvailable()) { const auto odpcmaFile = std::static_pointer_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. { std::shared_ptr< ODPCMAliasBlockFile > ptr; while(insertCursor < (int)tempBlocks.size() && (!(ptr = tempBlocks[insertCursor].lock()) || ptr->GetStart() + ptr->GetClipOffset() < 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< std::weak_ptr< ODPCMAliasBlockFile > > &unorderedBlocks) { 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 auto processStartSample = GetDemandSample(); std::shared_ptr< ODPCMAliasBlockFile > firstBlock; for(auto i = unorderedBlocks.size(); i--;) { auto ptr = unorderedBlocks[i].lock(); if(ptr) { //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(firstBlock && ptr->GetGlobalEnd() >= processStartSample && ( firstBlock->GetGlobalEnd() < processStartSample || ptr->GetGlobalStart() <= firstBlock->GetGlobalStart()) ) { //insert at the front of the list if we get blockfiles that are after the demand sample firstBlock = ptr; mBlockFiles.insert(mBlockFiles.begin(), unorderedBlocks[i]); } else { //otherwise no priority if ( !firstBlock ) firstBlock = ptr; mBlockFiles.push_back(unorderedBlocks[i]); } if(mMaxBlockFiles< (int) mBlockFiles.size()) mMaxBlockFiles = mBlockFiles.size(); } else { // The block file disappeared. // Let it be deleted and forget about it. } } }