mirror of
https://github.com/cookiengineer/audacity
synced 2025-10-19 17:11:12 +02:00
Locate and position the current Audacity source code, and clear a variety of old junk out of the way into junk-branches
This commit is contained in:
303
src/ondemand/ODComputeSummaryTask.cpp
Normal file
303
src/ondemand/ODComputeSummaryTask.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
/**********************************************************************
|
||||
|
||||
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 <wx/wx.h>
|
||||
|
||||
//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;
|
||||
|
||||
}
|
||||
|
||||
ODTask* ODComputeSummaryTask::Clone()
|
||||
{
|
||||
ODComputeSummaryTask* clone = new ODComputeSummaryTask;
|
||||
clone->mDemandSample=GetDemandSample();
|
||||
return clone;
|
||||
|
||||
}
|
||||
|
||||
|
||||
///releases memory that the ODTask owns. Subclasses should override.
|
||||
void ODComputeSummaryTask::Terminate()
|
||||
{
|
||||
//The terminate cblock won't allow DoSomeInternal and this method to be run async, so this is thread-safe
|
||||
|
||||
//we are going to take things out of the array. But first deref them since we ref them when we put them in.
|
||||
|
||||
mBlockFilesMutex.Lock();
|
||||
for(unsigned int i=0;i<mBlockFiles.size();i++)
|
||||
mBlockFiles[i]->Deref();
|
||||
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;
|
||||
sampleCount blockEndSample;
|
||||
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());
|
||||
|
||||
mBlockFilesMutex.Unlock();
|
||||
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;i<mWaveTracks.size();i++)
|
||||
{
|
||||
if(success && mWaveTracks[i])
|
||||
mWaveTracks[i]->AddInvalidRegion(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();
|
||||
}
|
||||
|
||||
///by default creates the order of the wavetrack to load.
|
||||
void ODComputeSummaryTask::Update()
|
||||
{
|
||||
|
||||
std::vector<ODPCMAliasBlockFile*> tempBlocks;
|
||||
|
||||
mWaveTrackMutex.Lock();
|
||||
|
||||
for(size_t j=0;j<mWaveTracks.size();j++)
|
||||
{
|
||||
if(mWaveTracks[j])
|
||||
{
|
||||
WaveClip *clip;
|
||||
BlockArray *blocks;
|
||||
Sequence *seq;
|
||||
|
||||
//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.
|
||||
seq->LockDeleteUpdateMutex();
|
||||
|
||||
//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++)
|
||||
{
|
||||
//if there is data but no summary, this blockfile needs summarizing.
|
||||
if(blocks->Item(i)->f->IsDataAvailable() && !blocks->Item(i)->f->IsSummaryAvailable())
|
||||
{
|
||||
blocks->Item(i)->f->Ref();
|
||||
((ODPCMAliasBlockFile*)blocks->Item(i)->f)->SetStart(blocks->Item(i)->start);
|
||||
((ODPCMAliasBlockFile*)blocks->Item(i)->f)->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)(((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();
|
||||
}
|
||||
}
|
||||
}
|
||||
mWaveTrackMutex.Unlock();
|
||||
|
||||
//get the new order.
|
||||
mBlockFilesMutex.Lock();
|
||||
OrderBlockFiles(tempBlocks);
|
||||
mBlockFilesMutex.Unlock();
|
||||
|
||||
MarkUpdateRan();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///Orders the input as either On-Demand or default layered order.
|
||||
void ODComputeSummaryTask::OrderBlockFiles(std::vector<ODPCMAliasBlockFile*> &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;i<mBlockFiles.size();i++)
|
||||
mBlockFiles[i]->Deref();
|
||||
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.
|
||||
|
||||
//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)
|
||||
{
|
||||
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()))
|
||||
)
|
||||
{
|
||||
//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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ODComputeSummaryTask::ODUpdate()
|
||||
{
|
||||
//clear old blockFiles and do something smarter.
|
||||
|
||||
}
|
98
src/ondemand/ODComputeSummaryTask.h
Normal file
98
src/ondemand/ODComputeSummaryTask.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
ODComputeSummaryTask.h
|
||||
|
||||
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.
|
||||
|
||||
*//*******************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef __AUDACITY_ODComputeSummaryTask__
|
||||
#define __AUDACITY_ODComputeSummaryTask__
|
||||
|
||||
#include <vector>
|
||||
#include "ODTask.h"
|
||||
#include "ODTaskThread.h"
|
||||
class ODPCMAliasBlockFile;
|
||||
class WaveTrack;
|
||||
|
||||
/// A class representing a modular task to be used with the On-Demand structures.
|
||||
class ODComputeSummaryTask:public ODTask
|
||||
{
|
||||
public:
|
||||
|
||||
// Constructor / Destructor
|
||||
|
||||
/// Constructs an ODTask
|
||||
ODComputeSummaryTask();
|
||||
virtual ~ODComputeSummaryTask(){};
|
||||
|
||||
virtual ODTask* Clone();
|
||||
|
||||
///Subclasses should override to return respective type.
|
||||
virtual unsigned int GetODType(){return eODPCMSummary;}
|
||||
|
||||
///Return the task name
|
||||
virtual const char* GetTaskName(){return "ODComputeSummaryTask";}
|
||||
|
||||
virtual const wxChar* GetTip(){return _("Import complete. Calculating waveform");}
|
||||
|
||||
virtual bool UsesCustomWorkUntilPercentage(){return true;}
|
||||
virtual float ComputeNextWorkUntilPercentageComplete();
|
||||
|
||||
///releases memory that the ODTask owns. Subclasses should override.
|
||||
virtual void Terminate();
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
///recalculates the percentage complete.
|
||||
virtual void CalculatePercentComplete();
|
||||
|
||||
///Computes and writes the data for one BlockFile if it still has a refcount.
|
||||
virtual void DoSomeInternal();
|
||||
|
||||
///Readjusts the blockfile order in the default manner. If we have had an ODRequest
|
||||
///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);
|
||||
|
||||
///tells us whether or not Update has been run at least once.
|
||||
void MarkUpdateRan();
|
||||
bool HasUpdateRan();
|
||||
|
||||
//mBlockFiles is touched on several threads- the OD terminate thread, and the task thread, so we need to mutex it.
|
||||
ODLock mBlockFilesMutex;
|
||||
std::vector<ODPCMAliasBlockFile*> mBlockFiles;
|
||||
|
||||
|
||||
int mMaxBlockFiles;
|
||||
int mComputedBlockFiles;
|
||||
|
||||
|
||||
|
||||
ODLock mHasUpdateRanMutex;
|
||||
bool mHasUpdateRan;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
326
src/ondemand/ODDecodeFlacTask.cpp
Normal file
326
src/ondemand/ODDecodeFlacTask.cpp
Normal file
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* ODDecodeFlacTask.cpp
|
||||
* Audacity
|
||||
*
|
||||
* Created by apple on 8/10/08.
|
||||
* Copyright 2008 __MyCompanyName__. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ODDecodeFlacTask.h"
|
||||
|
||||
#include "../Prefs.h"
|
||||
#include <wx/string.h>
|
||||
#include <wx/utils.h>
|
||||
#include <wx/file.h>
|
||||
#include <wx/ffile.h>
|
||||
|
||||
#ifdef USE_LIBID3TAG
|
||||
extern "C" {
|
||||
#include <id3tag.h>
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "../Tags.h"
|
||||
|
||||
#define FLAC_HEADER "fLaC"
|
||||
|
||||
#define DESC _("FLAC files")
|
||||
|
||||
static const wxChar *exts[] =
|
||||
{
|
||||
wxT("flac"),
|
||||
wxT("flc")
|
||||
};
|
||||
|
||||
ODDecodeFlacTask::~ODDecodeFlacTask()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ODTask* ODDecodeFlacTask::Clone()
|
||||
{
|
||||
ODDecodeFlacTask* clone = new ODDecodeFlacTask;
|
||||
clone->mDemandSample=GetDemandSample();
|
||||
|
||||
//the decoders and blockfiles should not be copied. They are created as the task runs.
|
||||
return clone;
|
||||
}
|
||||
|
||||
|
||||
void ODFLACFile::metadata_callback(const FLAC__StreamMetadata *metadata)
|
||||
{
|
||||
switch (metadata->type)
|
||||
{
|
||||
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
||||
for (FLAC__uint32 i = 0; i < metadata->data.vorbis_comment.num_comments; i++) {
|
||||
mComments.Add(UTF8CTOWX((char *)metadata->data.vorbis_comment.comments[i].entry));
|
||||
}
|
||||
break;
|
||||
|
||||
case FLAC__METADATA_TYPE_STREAMINFO:
|
||||
mDecoder->mSampleRate=metadata->data.stream_info.sample_rate;
|
||||
mDecoder->mNumChannels=metadata->data.stream_info.channels;
|
||||
mDecoder->mBitsPerSample=metadata->data.stream_info.bits_per_sample;
|
||||
mDecoder->mNumSamples=metadata->data.stream_info.total_samples;
|
||||
|
||||
if (mDecoder->mBitsPerSample<=16) {
|
||||
if (mDecoder->mFormat<int16Sample) {
|
||||
mDecoder->mFormat=int16Sample;
|
||||
}
|
||||
} else if (mDecoder->mBitsPerSample<=24) {
|
||||
if (mDecoder->mFormat<int24Sample) {
|
||||
mDecoder->mFormat=int24Sample;
|
||||
}
|
||||
} else {
|
||||
mDecoder->mFormat=floatSample;
|
||||
}
|
||||
mDecoder->mStreamInfoDone=true;
|
||||
break;
|
||||
// handle the other types we do nothing with to avoid a warning
|
||||
case FLAC__METADATA_TYPE_PADDING: // do nothing with padding
|
||||
case FLAC__METADATA_TYPE_APPLICATION: // no idea what to do with this
|
||||
case FLAC__METADATA_TYPE_SEEKTABLE: // don't need a seektable here
|
||||
case FLAC__METADATA_TYPE_CUESHEET: // convert this to labels?
|
||||
case FLAC__METADATA_TYPE_PICTURE: // ignore pictures
|
||||
case FLAC__METADATA_TYPE_UNDEFINED: // do nothing with this either
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ODFLACFile::error_callback(FLAC__StreamDecoderErrorStatus status)
|
||||
{
|
||||
mWasError = true;
|
||||
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
|
||||
printf("Flac Error: Lost sync\n");
|
||||
break;
|
||||
case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
|
||||
printf("Flac Error: Crc mismatch\n");
|
||||
break;
|
||||
case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
|
||||
printf("Flac Error: Bad Header\n");
|
||||
break;
|
||||
default:
|
||||
printf("Flac Error: Unknown error code\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//the inside of the read loop.
|
||||
FLAC__StreamDecoderWriteStatus ODFLACFile::write_callback(const FLAC__Frame *frame,
|
||||
const FLAC__int32 * const buffer[])
|
||||
{
|
||||
|
||||
unsigned int bytesToCopy = frame->header.blocksize;
|
||||
if(bytesToCopy>mDecoder->mDecodeBufferLen-mDecoder->mDecodeBufferWritePosition)
|
||||
bytesToCopy=mDecoder->mDecodeBufferLen-mDecoder->mDecodeBufferWritePosition;
|
||||
|
||||
//the decodeBuffer was allocated to be the same format as the flac buffer, so we can do a straight up memcpy.
|
||||
memcpy(mDecoder->mDecodeBuffer+SAMPLE_SIZE(mDecoder->mFormat)*mDecoder->mDecodeBufferWritePosition,buffer[mDecoder->mTargetChannel],SAMPLE_SIZE(mDecoder->mFormat) * bytesToCopy);
|
||||
|
||||
mDecoder->mDecodeBufferWritePosition+=bytesToCopy;
|
||||
/*
|
||||
short *tmp=new short[frame->header.blocksize];
|
||||
|
||||
for (unsigned int chn=0; chn<mDecoder->mNumChannels; chn++) {
|
||||
if (frame->header.bits_per_sample == 16) {
|
||||
for (unsigned int s=0; s<frame->header.blocksize; s++) {
|
||||
tmp[s]=buffer[chn][s];
|
||||
}
|
||||
|
||||
mDecoder->mChannels[chn]->Append((samplePtr)tmp,
|
||||
int16Sample,
|
||||
frame->header.blocksize);
|
||||
}
|
||||
else {
|
||||
mDecoder->mChannels[chn]->Append((samplePtr)buffer[chn],
|
||||
int24Sample,
|
||||
frame->header.blocksize);
|
||||
}
|
||||
}
|
||||
|
||||
delete [] tmp;
|
||||
*/
|
||||
|
||||
mDecoder->mSamplesDone += frame->header.blocksize;
|
||||
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||
// return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
||||
|
||||
// mDecoder->mUpdateResult = mDecoder->mProgress->Update((wxULongLong_t) mDecoder->mSamplesDone, mDecoder->mNumSamples != 0 ? (wxULongLong_t)mDecoder->mNumSamples : 1);
|
||||
/*
|
||||
if (mDecoder->mUpdateResult != eProgressSuccess)
|
||||
{
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
||||
}
|
||||
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
//--Decoder stuff:
|
||||
///Decodes the samples for this blockfile from the real file into a float buffer.
|
||||
///This is file specific, so subclasses must implement this only.
|
||||
///the buffer was defined like
|
||||
///samplePtr sampleData = NewSamples(mLen, floatSample);
|
||||
///this->ReadData(sampleData, floatSample, 0, mLen);
|
||||
///This class should call ReadHeader() first, so it knows the length, and can prepare
|
||||
///the file object if it needs to.
|
||||
void ODFlacDecoder::Decode(samplePtr & data, sampleFormat & format, sampleCount start, sampleCount len, unsigned int channel)
|
||||
{
|
||||
|
||||
//we need to lock this so the target stays fixed over the seek/write callback.
|
||||
mFlacFileLock.Lock();
|
||||
|
||||
bool usingCache=mLastDecodeStartSample==start;
|
||||
if(usingCache)
|
||||
{
|
||||
//we've just decoded this, so lets use a cache. (often so for
|
||||
}
|
||||
|
||||
|
||||
mDecodeBufferWritePosition=0;
|
||||
mDecodeBufferLen = len;
|
||||
|
||||
data = NewSamples(len, mFormat);
|
||||
mDecodeBuffer=data;
|
||||
format = mFormat;
|
||||
|
||||
mTargetChannel=channel;
|
||||
|
||||
if(!mFile->seek_absolute(start))
|
||||
{
|
||||
mFlacFileLock.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
while(mDecodeBufferWritePosition<mDecodeBufferLen)
|
||||
mFile->process_single();
|
||||
|
||||
mFlacFileLock.Unlock();
|
||||
if(!usingCache)
|
||||
{
|
||||
mLastDecodeStartSample=start;
|
||||
}
|
||||
//insert into blockfile and
|
||||
//calculate summary happen in ODDecodeBlockFile::WriteODDecodeBlockFile, where this method is also called.
|
||||
}
|
||||
|
||||
///Read header. Subclasses must override. Probably should save the info somewhere.
|
||||
///Ideally called once per decoding of a file. This complicates the task because
|
||||
///returns true if the file exists and the header was read alright.
|
||||
|
||||
//Note:we are not using LEGACY_FLAC defs (see ImportFlac.cpp FlacImportFileHandle::Init()
|
||||
//this code is based on that function.
|
||||
bool ODFlacDecoder::ReadHeader()
|
||||
{
|
||||
mFormat = int16Sample;//start with the smallest and move up in the metadata_callback.
|
||||
//we want to use the native flac type for quick conversion.
|
||||
/* (sampleFormat)
|
||||
gPrefs->Read(wxT("/SamplingRate/DefaultProjectSampleFormat"), floatSample);*/
|
||||
if(mFile)
|
||||
delete mFile;
|
||||
mFile = new ODFLACFile(this);
|
||||
|
||||
|
||||
if (!mHandle.Open(mFName, wxT("rb"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Even though there is an init() method that takes a filename, use the one that
|
||||
// takes a file handle because wxWidgets can open a file with a Unicode name and
|
||||
// libflac can't (under Windows).
|
||||
//
|
||||
// Responsibility for closing the file is passed to libflac.
|
||||
// (it happens when mFile->finish() is called)
|
||||
bool result = mFile->init(mHandle.fp())?true:false;
|
||||
mHandle.Detach();
|
||||
|
||||
if (result != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//this will call the metadata_callback when it is done
|
||||
mFile->process_until_end_of_metadata();
|
||||
// not necessary to check state, error callback will catch errors, but here's how:
|
||||
if (mFile->get_state() > FLAC__STREAM_DECODER_READ_FRAME) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mFile->is_valid() || mFile->get_was_error()) {
|
||||
// This probably is not a FLAC file at all
|
||||
return false;
|
||||
}
|
||||
|
||||
MarkInitialized();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
ODFLACFile* ODFlacDecoder::GetFlacFile()
|
||||
{
|
||||
return mFile;
|
||||
}
|
||||
|
||||
ODFlacDecoder::~ODFlacDecoder(){
|
||||
if(mFile)
|
||||
{
|
||||
mFile->finish();
|
||||
delete mFile;
|
||||
}
|
||||
}
|
||||
|
||||
///Creates an ODFileDecoder that decodes a file of filetype the subclass handles.
|
||||
//
|
||||
//compare to FLACImportPlugin::Open(wxString filename)
|
||||
ODFileDecoder* ODDecodeFlacTask::CreateFileDecoder(const wxString & fileName)
|
||||
{
|
||||
// First check if it really is a FLAC file
|
||||
/*
|
||||
int cnt;
|
||||
wxFile binaryFile;
|
||||
if (!binaryFile.Open(fileName)) {
|
||||
return NULL; // File not found
|
||||
}
|
||||
|
||||
#ifdef USE_LIBID3TAG
|
||||
// Skip any ID3 tags that might be present
|
||||
id3_byte_t query[ID3_TAG_QUERYSIZE];
|
||||
cnt = binaryFile.Read(query, sizeof(query));
|
||||
cnt = id3_tag_query(query, cnt);
|
||||
binaryFile.Seek(cnt);
|
||||
#endif
|
||||
|
||||
char buf[5];
|
||||
cnt = binaryFile.Read(buf, 4);
|
||||
binaryFile.Close();
|
||||
|
||||
if (cnt == wxInvalidOffset || strncmp(buf, FLAC_HEADER, 4) != 0) {
|
||||
// File is not a FLAC file
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Open the file for import
|
||||
ODFlacDecoder *decoder = new ODFlacDecoder(fileName);
|
||||
*/
|
||||
/*
|
||||
bool success = decoder->Init();
|
||||
if (!success) {
|
||||
delete decoder;
|
||||
return NULL;
|
||||
}
|
||||
*/
|
||||
// Open the file for import
|
||||
ODFlacDecoder *decoder = new ODFlacDecoder(fileName);
|
||||
|
||||
mDecoders.push_back(decoder);
|
||||
return decoder;
|
||||
|
||||
}
|
||||
|
143
src/ondemand/ODDecodeFlacTask.h
Normal file
143
src/ondemand/ODDecodeFlacTask.h
Normal file
@@ -0,0 +1,143 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
ODDecodeFlacTask.h
|
||||
|
||||
Created by Michael Chinen (mchinen) on 8/11/08.
|
||||
Audacity(R) is copyright (c) 1999-2008 Audacity Team.
|
||||
License: GPL v2. See License.txt.
|
||||
|
||||
******************************************************************//**
|
||||
|
||||
\class ODDecodeFlacTask
|
||||
\brief Decodes a flac file into a oddecodeBlockFile, but not immediately.
|
||||
|
||||
This is an abstract class that subclasses will have to derive the types
|
||||
from. For any type there should only be one ODDecodeTask associated with
|
||||
a given track.
|
||||
There could be the ODBlockFiles of several FLACs in one track (after copy and pasting),
|
||||
so things aren't as simple as they seem - the implementation needs to be
|
||||
robust enough to allow all the user changes such as copy/paste, delete, and so on.
|
||||
|
||||
*//*******************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef __AUDACITY_ODDecodeFLACTask__
|
||||
#define __AUDACITY_ODDecodeFLACTask__
|
||||
|
||||
#include <vector>
|
||||
#include "ODDecodeTask.h"
|
||||
#include "ODTaskThread.h"
|
||||
|
||||
#include "FLAC++/decoder.h"
|
||||
|
||||
class ODDecodeBlockFile;
|
||||
class WaveTrack;
|
||||
class ODFileDecoder;
|
||||
|
||||
class ODFlacDecoder;
|
||||
class ODFLACFile;
|
||||
|
||||
/// A class representing a modular task to be used with the On-Demand structures.
|
||||
class ODDecodeFlacTask:public ODDecodeTask
|
||||
{
|
||||
public:
|
||||
|
||||
/// Constructs an ODTask
|
||||
ODDecodeFlacTask(){}
|
||||
virtual ~ODDecodeFlacTask();
|
||||
|
||||
|
||||
virtual ODTask* Clone();
|
||||
///Creates an ODFileDecoder that decodes a file of filetype the subclass handles.
|
||||
virtual ODFileDecoder* CreateFileDecoder(const wxString & fileName);
|
||||
|
||||
///Lets other classes know that this class handles flac
|
||||
///Subclasses should override to return respective type.
|
||||
virtual unsigned int GetODType(){return eODFLAC;}
|
||||
};
|
||||
|
||||
|
||||
class ODFLACFile : public FLAC::Decoder::File
|
||||
{
|
||||
public:
|
||||
ODFLACFile(ODFlacDecoder *decoder) : mDecoder(decoder)
|
||||
{
|
||||
mWasError = false;
|
||||
set_metadata_ignore_all();
|
||||
set_metadata_respond(FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
||||
set_metadata_respond(FLAC__METADATA_TYPE_STREAMINFO);
|
||||
}
|
||||
|
||||
bool get_was_error() const
|
||||
{
|
||||
return mWasError;
|
||||
}
|
||||
private:
|
||||
friend class ODFlacDecoder;
|
||||
ODFlacDecoder *mDecoder;
|
||||
bool mWasError;
|
||||
wxArrayString mComments;
|
||||
|
||||
protected:
|
||||
virtual FLAC__StreamDecoderWriteStatus write_callback(const FLAC__Frame *frame,
|
||||
const FLAC__int32 * const buffer[]);
|
||||
virtual void metadata_callback(const FLAC__StreamMetadata *metadata);
|
||||
virtual void error_callback(FLAC__StreamDecoderErrorStatus status);
|
||||
};
|
||||
|
||||
|
||||
///class to decode a particular file (one per file). Saves info such as filename and length (after the header is read.)
|
||||
class ODFlacDecoder:public ODFileDecoder
|
||||
{
|
||||
friend class ODFLACFile;
|
||||
public:
|
||||
///This should handle unicode converted to UTF-8 on mac/linux, but OD TODO:check on windows
|
||||
ODFlacDecoder(const wxString & fileName):ODFileDecoder(fileName),mSamplesDone(0){mFile=NULL;}
|
||||
virtual ~ODFlacDecoder();
|
||||
|
||||
///Decodes the samples for this blockfile from the real file into a float buffer.
|
||||
///This is file specific, so subclasses must implement this only.
|
||||
///the buffer was defined like
|
||||
///samplePtr sampleData = NewSamples(mLen, floatSample);
|
||||
///this->ReadData(sampleData, floatSample, 0, mLen);
|
||||
///This class should call ReadHeader() first, so it knows the length, and can prepare
|
||||
///the file object if it needs to.
|
||||
virtual void Decode(samplePtr & data, sampleFormat & format, sampleCount start, sampleCount len, unsigned int channel);
|
||||
|
||||
|
||||
///Read header. Subclasses must override. Probably should save the info somewhere.
|
||||
///Ideally called once per decoding of a file. This complicates the task because
|
||||
virtual bool ReadHeader();
|
||||
|
||||
///FLAC specific file (inherited from FLAC::Decoder::File)
|
||||
ODFLACFile* GetFlacFile();
|
||||
|
||||
private:
|
||||
friend class FLACImportFileHandle;
|
||||
sampleFormat mFormat;
|
||||
ODFLACFile *mFile;
|
||||
ODLock mFlacFileLock;//for mFile;
|
||||
wxFFile mHandle;
|
||||
unsigned long mSampleRate;
|
||||
unsigned long mNumChannels;
|
||||
unsigned long mBitsPerSample;
|
||||
FLAC__uint64 mNumSamples;
|
||||
FLAC__uint64 mSamplesDone;
|
||||
sampleCount mLastDecodeStartSample;
|
||||
bool mStreamInfoDone;
|
||||
int mUpdateResult;
|
||||
WaveTrack **mChannels;
|
||||
unsigned int mTargetChannel;
|
||||
unsigned int mDecodeBufferWritePosition;
|
||||
unsigned int mDecodeBufferLen;
|
||||
samplePtr mDecodeBuffer;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
291
src/ondemand/ODDecodeTask.cpp
Normal file
291
src/ondemand/ODDecodeTask.cpp
Normal file
@@ -0,0 +1,291 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
ODComputeSummaryTask.cpp
|
||||
|
||||
Created by Michael Chinen (mchinen) on 8/10/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 "ODDecodeTask.h"
|
||||
#include "../blockfile/ODDecodeBlockFile.h"
|
||||
#include <wx/wx.h>
|
||||
|
||||
///Creates a new task that computes summaries for a wavetrack that needs to be specified through SetWaveTrack()
|
||||
ODDecodeTask::ODDecodeTask()
|
||||
{
|
||||
mMaxBlockFiles = 0;
|
||||
mComputedBlockFiles = 0;
|
||||
}
|
||||
|
||||
|
||||
///Computes and writes the data for one BlockFile if it still has a refcount.
|
||||
void ODDecodeTask::DoSomeInternal()
|
||||
{
|
||||
if(mBlockFiles.size()<=0)
|
||||
{
|
||||
mPercentCompleteMutex.Lock();
|
||||
mPercentComplete = 1.0;
|
||||
mPercentCompleteMutex.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
ODDecodeBlockFile* bf;
|
||||
ODFileDecoder* decoder;
|
||||
sampleCount blockStartSample;
|
||||
sampleCount blockEndSample;
|
||||
bool success =false;
|
||||
|
||||
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)
|
||||
{
|
||||
//OD TODO: somehow pass the bf a reference to the decoder that manages it's file.
|
||||
//we need to ensure that the filename won't change or be moved. We do this by calling LockRead(),
|
||||
//which the dirmanager::EnsureSafeFilename also does.
|
||||
bf->LockRead();
|
||||
//Get the decoder. If the file was moved, we need to create another one and init it.
|
||||
decoder=GetOrCreateMatchingFileDecoder(bf);
|
||||
if(!decoder->IsInitialized())
|
||||
decoder->Init();
|
||||
bf->SetODFileDecoder(decoder);
|
||||
bf->DoWriteBlockFile();
|
||||
bf->UnlockRead();
|
||||
|
||||
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());
|
||||
|
||||
//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;i<mWaveTracks.size();i++)
|
||||
{
|
||||
if(success && mWaveTracks[i])
|
||||
mWaveTracks[i]->AddInvalidRegion(blockStartSample,blockEndSample);
|
||||
}
|
||||
mWaveTrackMutex.Unlock();
|
||||
}
|
||||
|
||||
//update percentage complete.
|
||||
CalculatePercentComplete();
|
||||
}
|
||||
|
||||
void ODDecodeTask::CalculatePercentComplete()
|
||||
{
|
||||
mPercentCompleteMutex.Lock();
|
||||
mPercentComplete = (float) 1.0 - ((float)mBlockFiles.size() / (mMaxBlockFiles+1));
|
||||
mPercentCompleteMutex.Unlock();
|
||||
}
|
||||
|
||||
///by default creates the order of the wavetrack to load.
|
||||
void ODDecodeTask::Update()
|
||||
{
|
||||
|
||||
std::vector<ODDecodeBlockFile*> tempBlocks;
|
||||
|
||||
mWaveTrackMutex.Lock();
|
||||
|
||||
for(size_t j=0;j<mWaveTracks.size();j++)
|
||||
{
|
||||
if(mWaveTracks[j])
|
||||
{
|
||||
WaveClip *clip;
|
||||
BlockArray *blocks;
|
||||
Sequence *seq;
|
||||
|
||||
//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.
|
||||
seq->LockDeleteUpdateMutex();
|
||||
|
||||
//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++)
|
||||
{
|
||||
//since we have more than one ODBlockFile, we will need type flags to cast.
|
||||
if(!blocks->Item(i)->f->IsDataAvailable() && ((ODDecodeBlockFile*)blocks->Item(i)->f)->GetDecodeType()==this->GetODType())
|
||||
{
|
||||
blocks->Item(i)->f->Ref();
|
||||
((ODDecodeBlockFile*)blocks->Item(i)->f)->SetStart(blocks->Item(i)->start);
|
||||
((ODDecodeBlockFile*)blocks->Item(i)->f)->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)(((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();
|
||||
}
|
||||
}
|
||||
}
|
||||
mWaveTrackMutex.Unlock();
|
||||
|
||||
//get the new order.
|
||||
OrderBlockFiles(tempBlocks);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///Orders the input as either On-Demand or default layered order.
|
||||
void ODDecodeTask::OrderBlockFiles(std::vector<ODDecodeBlockFile*> &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;i<mBlockFiles.size();i++)
|
||||
mBlockFiles[i]->Deref();
|
||||
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.
|
||||
|
||||
//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)
|
||||
{
|
||||
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()))
|
||||
)
|
||||
{
|
||||
//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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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.
|
||||
///Blocks that have IsDataAvailable()==false are blockfiles to be decoded. if BlockFile::GetDecodeType()==ODDecodeTask::GetODType() then
|
||||
///this decoder should handle it. Decoders are accessible with the methods below. These aren't thread-safe and should only
|
||||
///be called from the decoding thread.
|
||||
ODFileDecoder* ODDecodeTask::GetOrCreateMatchingFileDecoder(ODDecodeBlockFile* blockFile)
|
||||
{
|
||||
ODFileDecoder* ret=NULL;
|
||||
//see if the filename matches any of our decoders, if so, return it.
|
||||
for(int i=0;i<(int)mDecoders.size();i++)
|
||||
{
|
||||
if(mDecoders[i]->GetFileName()==blockFile->GetAudioFileName().GetFullPath())
|
||||
{
|
||||
ret = mDecoders[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//otherwise, create and add one, and return it.
|
||||
if(!ret)
|
||||
{
|
||||
ret=CreateFileDecoder(blockFile->GetAudioFileName().GetFullPath());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
int ODDecodeTask::GetNumFileDecoders()
|
||||
{
|
||||
return mDecoders.size();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///This should handle unicode converted to UTF-8 on mac/linux, but OD TODO:check on windows
|
||||
ODFileDecoder::ODFileDecoder(const wxString & fName)
|
||||
{
|
||||
mFName = fName;
|
||||
mInited = false;
|
||||
}
|
||||
|
||||
ODFileDecoder::~ODFileDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
bool ODFileDecoder::IsInitialized()
|
||||
{
|
||||
bool ret;
|
||||
mInitedLock.Lock();
|
||||
ret = mInited;
|
||||
mInitedLock.Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
///Derived classes should call this after they have parsed the header.
|
||||
void ODFileDecoder::MarkInitialized()
|
||||
{
|
||||
mInitedLock.Lock();
|
||||
mInited=true;
|
||||
mInitedLock.Unlock();
|
||||
}
|
||||
|
140
src/ondemand/ODDecodeTask.h
Normal file
140
src/ondemand/ODDecodeTask.h
Normal file
@@ -0,0 +1,140 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
ODDecodeTask.h
|
||||
|
||||
Created by Michael Chinen (mchinen) on 8/10/08.
|
||||
Audacity(R) is copyright (c) 1999-2008 Audacity Team.
|
||||
License: GPL v2. See License.txt.
|
||||
|
||||
******************************************************************//**
|
||||
|
||||
\class ODDecodeTask
|
||||
\brief Decodes a file into a simpleBlockFile, but not immediately.
|
||||
|
||||
This is an abstract class that subclasses will have to derive the types
|
||||
from. For any type there should only be one ODDecodeTask associated with
|
||||
a given track.
|
||||
There could be the ODBlockFiles of several FLACs in one track (after copy and pasting),
|
||||
so things aren't as simple as they seem - the implementation needs to be
|
||||
robust enough to allow all the user changes such as copy/paste, delete, and so on.
|
||||
|
||||
*//*******************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef __AUDACITY_ODDecodeTask__
|
||||
#define __AUDACITY_ODDecodeTask__
|
||||
|
||||
#include <vector>
|
||||
#include "ODTask.h"
|
||||
#include "ODTaskThread.h"
|
||||
class ODDecodeBlockFile;
|
||||
class WaveTrack;
|
||||
class ODFileDecoder;
|
||||
|
||||
|
||||
/// A class representing a modular task to be used with the On-Demand structures.
|
||||
class ODDecodeTask:public ODTask
|
||||
{
|
||||
public:
|
||||
// Constructor / Destructor
|
||||
|
||||
/// Constructs an ODTask
|
||||
ODDecodeTask();
|
||||
virtual ~ODDecodeTask(){};
|
||||
|
||||
virtual ODTask* Clone()=0;
|
||||
|
||||
///Return the task name
|
||||
virtual const char* GetTaskName(){return "ODDecodeTask";}
|
||||
|
||||
virtual const wxChar* GetTip(){return _("Decoding Waveform");}
|
||||
|
||||
///Subclasses should override to return respective type.
|
||||
virtual unsigned int GetODType(){return eODNone;}
|
||||
|
||||
///Creates an ODFileDecoder that decodes a file of filetype the subclass handles.
|
||||
virtual ODFileDecoder* CreateFileDecoder(const wxString & fileName)=0;
|
||||
|
||||
///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.
|
||||
///Blocks that have IsDataAvailable()==false are blockfiles to be decoded. if BlockFile::GetDecodeType()==ODDecodeTask::GetODType() then
|
||||
///this decoder should handle it. Decoders are accessible with the methods below. These aren't thread-safe and should only
|
||||
///be called from the decoding thread.
|
||||
virtual ODFileDecoder* GetOrCreateMatchingFileDecoder(ODDecodeBlockFile* blockFile);
|
||||
virtual int GetNumFileDecoders();
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
///recalculates the percentage complete.
|
||||
virtual void CalculatePercentComplete();
|
||||
|
||||
///Computes and writes the data for one BlockFile if it still has a refcount.
|
||||
virtual void DoSomeInternal();
|
||||
|
||||
///Readjusts the blockfile order in the default manner. If we have had an ODRequest
|
||||
///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);
|
||||
|
||||
|
||||
std::vector<ODDecodeBlockFile*> mBlockFiles;
|
||||
std::vector<ODFileDecoder*> mDecoders;
|
||||
|
||||
int mMaxBlockFiles;
|
||||
int mComputedBlockFiles;
|
||||
|
||||
};
|
||||
|
||||
///class to decode a particular file (one per file). Saves info such as filename and length (after the header is read.)
|
||||
class ODFileDecoder
|
||||
{
|
||||
public:
|
||||
///This should handle unicode converted to UTF-8 on mac/linux, but OD TODO:check on windows
|
||||
ODFileDecoder(const wxString& fName);
|
||||
virtual ~ODFileDecoder();
|
||||
|
||||
///Read header. Subclasses must override. Probably should save the info somewhere.
|
||||
///Ideally called once per decoding of a file. This complicates the task because
|
||||
virtual bool ReadHeader()=0;
|
||||
virtual bool Init(){return ReadHeader();}
|
||||
|
||||
///Decodes the samples for this blockfile from the real file into a float buffer.
|
||||
///This is file specific, so subclasses must implement this only.
|
||||
///the buffer should be created by the ODFileDecoder implementing this method.
|
||||
///It should set the format parameter so that the client code can deal with it.
|
||||
///This class should call ReadHeader() first, so it knows the length, and can prepare
|
||||
///the file object if it needs to.
|
||||
virtual void Decode(samplePtr & data, sampleFormat & format, sampleCount start, sampleCount len, unsigned int channel)=0;
|
||||
|
||||
wxString GetFileName(){return mFName;}
|
||||
|
||||
bool IsInitialized();
|
||||
|
||||
protected:
|
||||
///Derived classes should call this after they have parsed the header.
|
||||
void MarkInitialized();
|
||||
|
||||
bool mInited;
|
||||
ODLock mInitedLock;
|
||||
|
||||
wxString mFName;
|
||||
|
||||
unsigned int mSampleRate;
|
||||
unsigned int mNumSamples;//this may depend on the channel - so TODO: we should probably let the decoder create/modify the track info directly.
|
||||
unsigned int mNumChannels;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
653
src/ondemand/ODManager.cpp
Normal file
653
src/ondemand/ODManager.cpp
Normal file
@@ -0,0 +1,653 @@
|
||||
/*
|
||||
* ODManager.cpp
|
||||
* Audacity
|
||||
*
|
||||
* Created by apple on 6/8/08.
|
||||
* Copyright 2008 __MyCompanyName__. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ODManager.h"
|
||||
#include "ODTask.h"
|
||||
#include "ODTaskThread.h"
|
||||
#include "ODWaveTrackTaskQueue.h"
|
||||
#include "../Project.h"
|
||||
#include <NonGuiThread.h>
|
||||
#include <wx/utils.h>
|
||||
#include <wx/wx.h>
|
||||
#include <wx/thread.h>
|
||||
#include <wx/event.h>
|
||||
|
||||
ODLock gODInitedMutex;
|
||||
bool gManagerCreated=false;
|
||||
bool gPause=false; //to be loaded in and used with Pause/Resume before ODMan init.
|
||||
/// a flag that is set if we have loaded some OD blockfiles from PCM.
|
||||
bool sHasLoadedOD=false;
|
||||
|
||||
|
||||
|
||||
//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.
|
||||
int CompareNoCaseFileName(const wxString& first, const wxString& second)
|
||||
{
|
||||
return first.CmpNoCase(second);
|
||||
}
|
||||
|
||||
void ODManager::LockLibSndFileMutex()
|
||||
{
|
||||
sLibSndFileMutex.Lock();
|
||||
}
|
||||
|
||||
void ODManager::UnlockLibSndFileMutex()
|
||||
{
|
||||
sLibSndFileMutex.Unlock();
|
||||
}
|
||||
|
||||
|
||||
//private constructor - Singleton.
|
||||
ODManager::ODManager()
|
||||
{
|
||||
mTerminate = false;
|
||||
mTerminated = false;
|
||||
mPause= gPause;
|
||||
|
||||
//must set up the queue condition
|
||||
mQueueNotEmptyCond = new ODCondition(&mQueueNotEmptyCondLock);
|
||||
}
|
||||
|
||||
//private constructor - 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.
|
||||
//nothing else should be running on OD related threads at this point, so we don't lock.
|
||||
for(unsigned int i=0;i<mQueues.size();i++)
|
||||
delete mQueues[i];
|
||||
|
||||
delete mQueueNotEmptyCond;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///Adds a task to running queue. Thread-safe.
|
||||
void ODManager::AddTask(ODTask* task)
|
||||
{
|
||||
mTasksMutex.Lock();
|
||||
mTasks.push_back(task);
|
||||
mTasksMutex.Unlock();
|
||||
//signal the queue not empty condition.
|
||||
bool paused;
|
||||
|
||||
mPauseLock.Lock();
|
||||
paused=mPause;
|
||||
mPauseLock.Unlock();
|
||||
|
||||
mQueueNotEmptyCondLock.Lock();
|
||||
//don't signal if we are paused since if we wake up the loop it will start processing other tasks while paused
|
||||
if(!paused)
|
||||
mQueueNotEmptyCond->Signal();
|
||||
mQueueNotEmptyCondLock.Unlock();
|
||||
|
||||
}
|
||||
|
||||
void ODManager::SignalTaskQueueLoop()
|
||||
{
|
||||
bool paused;
|
||||
|
||||
mPauseLock.Lock();
|
||||
paused=mPause;
|
||||
mPauseLock.Unlock();
|
||||
mQueueNotEmptyCondLock.Lock();
|
||||
//don't signal if we are paused
|
||||
if(!paused)
|
||||
mQueueNotEmptyCond->Signal();
|
||||
mQueueNotEmptyCondLock.Unlock();
|
||||
}
|
||||
|
||||
///removes a task from the active task queue
|
||||
void ODManager::RemoveTaskIfInQueue(ODTask* task)
|
||||
{
|
||||
mTasksMutex.Lock();
|
||||
for(unsigned int i=0;i<mTasks.size();i++)
|
||||
{
|
||||
if(mTasks[i]==task)
|
||||
{
|
||||
mTasks.erase(mTasks.begin()+i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mTasksMutex.Unlock();
|
||||
|
||||
}
|
||||
|
||||
///Adds a new task to the queue. Creates a queue if the tracks associated with the task is not in the list
|
||||
///
|
||||
///@param task the task to add
|
||||
///@param lockMutex locks the mutexes if true (default). This function is used within other ODManager calls, which many need to set this to false.
|
||||
void ODManager::AddNewTask(ODTask* task, bool lockMutex)
|
||||
{
|
||||
ODWaveTrackTaskQueue* queue = NULL;
|
||||
|
||||
if(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.
|
||||
//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];
|
||||
}
|
||||
|
||||
if(queue)
|
||||
{
|
||||
//Add it to the existing queue but keep the lock since this reference can be deleted.
|
||||
queue->AddTask(task);
|
||||
if(lockMutex)
|
||||
mQueuesMutex.Unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
//Make a new one, add it to the local track queue, and to the immediate running task list,
|
||||
//since this task is definitely at the head
|
||||
queue = new ODWaveTrackTaskQueue();
|
||||
queue->AddTask(task);
|
||||
mQueues.push_back(queue);
|
||||
if(lockMutex)
|
||||
mQueuesMutex.Unlock();
|
||||
|
||||
AddTask(task);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ODManager* ODManager::Instance()
|
||||
{
|
||||
static ODManager* man=NULL;
|
||||
//this isn't 100% threadsafe but I think Okay for this purpose.
|
||||
|
||||
// wxLogDebug(wxT("Fetching Instance\n"));
|
||||
|
||||
gODInitedMutex.Lock();
|
||||
if(!man)
|
||||
{
|
||||
man = new ODManager();
|
||||
man->Init();
|
||||
gManagerCreated = true;
|
||||
}
|
||||
gODInitedMutex.Unlock();
|
||||
|
||||
return man;
|
||||
}
|
||||
|
||||
bool ODManager::IsInstanceCreated()
|
||||
{
|
||||
bool ret;
|
||||
gODInitedMutex.Lock();
|
||||
ret= gManagerCreated;
|
||||
gODInitedMutex.Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
///Launches a thread for the manager and starts accepting Tasks.
|
||||
void ODManager::Init()
|
||||
{
|
||||
mCurrentThreads = 0;
|
||||
mMaxThreads = 5;
|
||||
|
||||
// wxLogDebug(wxT("Initializing ODManager...Creating manager thread\n"));
|
||||
ODManagerHelperThread* startThread = new ODManagerHelperThread;
|
||||
|
||||
// startThread->SetPriority(0);//default of 50.
|
||||
startThread->Create();
|
||||
// printf("starting thread from init\n");
|
||||
startThread->Run();
|
||||
|
||||
// printf("started thread from init\n");
|
||||
//destruction is taken care of by wx thread code. TODO:Check if pthreads code does this.
|
||||
}
|
||||
|
||||
void ODManager::DecrementCurrentThreads()
|
||||
{
|
||||
mCurrentThreadsMutex.Lock();
|
||||
mCurrentThreads--;
|
||||
mCurrentThreadsMutex.Unlock();
|
||||
}
|
||||
|
||||
///Main loop for managing threads and tasks.
|
||||
void ODManager::Start()
|
||||
{
|
||||
ODTaskThread* thread;
|
||||
bool tasksInArray;
|
||||
bool paused;
|
||||
int numQueues=0;
|
||||
|
||||
mNeedsDraw=0;
|
||||
|
||||
//wxLog calls not threadsafe. are printfs? thread-messy for sure, but safe?
|
||||
// printf("ODManager thread strating \n");
|
||||
//TODO: Figure out why this has no effect at all.
|
||||
//wxThread::This()->SetPriority(30);
|
||||
mTerminateMutex.Lock();
|
||||
while(!mTerminate)
|
||||
{
|
||||
mTerminateMutex.Unlock();
|
||||
// printf("ODManager thread running \n");
|
||||
|
||||
//we should look at our WaveTrack queues to see if we can process a new task to the running queue.
|
||||
UpdateQueues();
|
||||
|
||||
//start some threads if necessary
|
||||
|
||||
mTasksMutex.Lock();
|
||||
tasksInArray = mTasks.size()>0;
|
||||
mTasksMutex.Unlock();
|
||||
mCurrentThreadsMutex.Lock();
|
||||
|
||||
mPauseLock.Lock();
|
||||
paused=mPause;
|
||||
mPauseLock.Unlock();
|
||||
|
||||
// keep adding tasks if there is work to do, up to the limit.
|
||||
while(!paused && tasksInArray && (mCurrentThreads < mMaxThreads))
|
||||
{
|
||||
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);
|
||||
|
||||
// thread->SetPriority(10);//default is 50.
|
||||
thread->Create();
|
||||
thread->Run();
|
||||
|
||||
mTasks.erase(mTasks.begin());
|
||||
tasksInArray = mTasks.size()>0;
|
||||
|
||||
mTasksMutex.Unlock();
|
||||
|
||||
mCurrentThreadsMutex.Lock();
|
||||
}
|
||||
|
||||
mCurrentThreadsMutex.Unlock();
|
||||
//use a conditon variable to block here instead of a sleep.
|
||||
|
||||
// JKC: If there are no tasks ready to run, or we're paused then
|
||||
// we wait for there to be tasks in the queue.
|
||||
mQueueNotEmptyCondLock.Lock();
|
||||
if( (!tasksInArray) || paused)
|
||||
mQueueNotEmptyCond->Wait();
|
||||
mQueueNotEmptyCondLock.Unlock();
|
||||
|
||||
//if there is some ODTask running, then there will be something in the queue. If so then redraw to show progress
|
||||
mQueuesMutex.Lock();
|
||||
mNeedsDraw += mQueues.size()>0?1:0;
|
||||
numQueues=mQueues.size();
|
||||
mQueuesMutex.Unlock();
|
||||
|
||||
//redraw the current project only (ODTasks will send a redraw on complete even if the projects are in the background)
|
||||
//we don't want to redraw at a faster rate when we have more queues because
|
||||
//this means the CPU is already taxed. This if statement normalizes the rate
|
||||
if((mNeedsDraw>numQueues) && numQueues)
|
||||
{
|
||||
mNeedsDraw=0;
|
||||
wxCommandEvent event( EVT_ODTASK_UPDATE );
|
||||
AudacityProject::AllProjectsDeleteLock();
|
||||
AudacityProject* proj = GetActiveProject();
|
||||
if(proj)
|
||||
proj->AddPendingEvent( event );
|
||||
AudacityProject::AllProjectsDeleteUnlock();
|
||||
}
|
||||
mTerminateMutex.Lock();
|
||||
}
|
||||
mTerminateMutex.Unlock();
|
||||
|
||||
mTerminatedMutex.Lock();
|
||||
mTerminated=true;
|
||||
mTerminatedMutex.Unlock();
|
||||
|
||||
//wxLogDebug Not thread safe.
|
||||
//printf("ODManager thread terminating\n");
|
||||
|
||||
}
|
||||
|
||||
void ODManager::Pause(bool pause)
|
||||
{
|
||||
if(IsInstanceCreated())
|
||||
{
|
||||
ODManager::Instance()->mPauseLock.Lock();
|
||||
ODManager::Instance()->mPause = pause;
|
||||
ODManager::Instance()->mPauseLock.Unlock();
|
||||
|
||||
//we should check the queue again.
|
||||
ODManager::Instance()->mQueueNotEmptyCondLock.Lock();
|
||||
ODManager::Instance()->mQueueNotEmptyCond->Signal();
|
||||
ODManager::Instance()->mQueueNotEmptyCondLock.Unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
gPause=pause;
|
||||
}
|
||||
}
|
||||
|
||||
void ODManager::Resume()
|
||||
{
|
||||
Pause(false);
|
||||
}
|
||||
|
||||
void ODManager::Quit()
|
||||
{
|
||||
if(IsInstanceCreated())
|
||||
{
|
||||
ODManager::Instance()->mTerminateMutex.Lock();
|
||||
ODManager::Instance()->mTerminate = true;
|
||||
ODManager::Instance()->mTerminateMutex.Unlock();
|
||||
|
||||
ODManager::Instance()->mTerminatedMutex.Lock();
|
||||
while(!ODManager::Instance()->mTerminated)
|
||||
{
|
||||
ODManager::Instance()->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();
|
||||
|
||||
ODManager::Instance()->mTerminatedMutex.Lock();
|
||||
}
|
||||
ODManager::Instance()->mTerminatedMutex.Unlock();
|
||||
|
||||
|
||||
delete ODManager::Instance();
|
||||
}
|
||||
}
|
||||
|
||||
///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(WaveTrack* oldTrack,WaveTrack* newTrack)
|
||||
{
|
||||
mQueuesMutex.Lock();
|
||||
for(unsigned int i=0;i<mQueues.size();i++)
|
||||
{
|
||||
mQueues[i]->ReplaceWaveTrack(oldTrack,newTrack);
|
||||
}
|
||||
mQueuesMutex.Unlock();
|
||||
}
|
||||
|
||||
///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)
|
||||
{
|
||||
ODWaveTrackTaskQueue* owner=NULL;
|
||||
mQueuesMutex.Lock();
|
||||
for(unsigned int i=0;i<mQueues.size();i++)
|
||||
{
|
||||
if(mQueues[i]->ContainsWaveTrack(track))
|
||||
{
|
||||
owner = mQueues[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(owner)
|
||||
owner->MakeWaveTrackIndependent(track);
|
||||
|
||||
mQueuesMutex.Unlock();
|
||||
|
||||
}
|
||||
|
||||
///attach the track in question to another, already existing track's queues and tasks. Remove the task/tracks.
|
||||
///only works if both tracks exist. Sets needODUpdate flag for the task. This is complicated and will probably need
|
||||
///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)
|
||||
{
|
||||
|
||||
//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.
|
||||
ODWaveTrackTaskQueue* masterQueue=NULL;
|
||||
ODWaveTrackTaskQueue* dependentQueue=NULL;
|
||||
unsigned int dependentIndex;
|
||||
bool canMerge = false;
|
||||
|
||||
mQueuesMutex.Lock();
|
||||
for(unsigned int i=0;i<mQueues.size();i++)
|
||||
{
|
||||
if(mQueues[i]->ContainsWaveTrack(masterTrack))
|
||||
{
|
||||
masterQueue = mQueues[i];
|
||||
}
|
||||
else if(mQueues[i]->ContainsWaveTrack(dependentTrack))
|
||||
{
|
||||
dependentQueue = mQueues[i];
|
||||
dependentIndex = i;
|
||||
}
|
||||
|
||||
}
|
||||
if(masterQueue&&dependentQueue)
|
||||
canMerge=masterQueue->CanMergeWith(dependentQueue);
|
||||
|
||||
|
||||
//otherwise we need to let dependentTrack's queue live on. We'll have to wait till the conflicting tasks are done.
|
||||
if(!canMerge)
|
||||
{
|
||||
mQueuesMutex.Unlock();
|
||||
return false;
|
||||
}
|
||||
//then we add dependentTrack to the masterTrack's queue - this will allow future ODScheduling to affect them together.
|
||||
//this sets the NeedODUpdateFlag since we don't want the head task to finish without haven't dealt with the depednent
|
||||
masterQueue->MergeWaveTrack(dependentTrack);
|
||||
|
||||
//finally remove the dependent track
|
||||
mQueues.erase(mQueues.begin()+dependentIndex);
|
||||
mQueuesMutex.Unlock();
|
||||
delete dependentQueue; //note this locks the ODManager's current task queue.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
///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 ODManager::DemandTrackUpdate(WaveTrack* track, double seconds)
|
||||
{
|
||||
mQueuesMutex.Lock();
|
||||
for(unsigned int i=0;i<mQueues.size();i++)
|
||||
{
|
||||
mQueues[i]->DemandTrackUpdate(track,seconds);
|
||||
}
|
||||
mQueuesMutex.Unlock();
|
||||
|
||||
}
|
||||
|
||||
///remove tasks from ODWaveTrackTaskQueues that have been done. Schedules new ones if they exist
|
||||
///Also remove queues that have become empty.
|
||||
void ODManager::UpdateQueues()
|
||||
{
|
||||
mQueuesMutex.Lock();
|
||||
for(unsigned int i=0;i<mQueues.size();i++)
|
||||
{
|
||||
|
||||
if(mQueues[i]->IsFrontTaskComplete())
|
||||
{
|
||||
//this should delete and remove the front task instance.
|
||||
mQueues[i]->RemoveFrontTask();
|
||||
//schedule next.
|
||||
if(!mQueues[i]->IsEmpty())
|
||||
{
|
||||
//we need to release the lock on the queue vector before using the task vector's lock or we deadlock
|
||||
//so get a temp.
|
||||
ODWaveTrackTaskQueue* queue;
|
||||
queue = mQueues[i];
|
||||
|
||||
mQueuesMutex.Unlock();
|
||||
AddTask(queue->GetFrontTask());
|
||||
mQueuesMutex.Lock();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//if the queue is empty delete it.
|
||||
if(mQueues[i]->IsEmpty())
|
||||
{
|
||||
delete mQueues[i];
|
||||
mQueues.erase(mQueues.begin()+i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
mQueuesMutex.Unlock();
|
||||
|
||||
}
|
||||
|
||||
//static
|
||||
///sets a flag that is set if we have loaded some OD blockfiles from PCM.
|
||||
void ODManager::MarkLoadedODFlag()
|
||||
{
|
||||
sHasLoadedOD = true;
|
||||
}
|
||||
|
||||
//static
|
||||
///resets a flag that is set if we have loaded some OD blockfiles from PCM.
|
||||
void ODManager::UnmarkLoadedODFlag()
|
||||
{
|
||||
sHasLoadedOD = false;
|
||||
}
|
||||
|
||||
//static
|
||||
///returns a flag that is set if we have loaded some OD blockfiles from PCM.
|
||||
bool ODManager::HasLoadedODFlag()
|
||||
{
|
||||
return sHasLoadedOD;
|
||||
}
|
||||
|
||||
///fills in the status bar message for a given track
|
||||
void ODManager::FillTipForWaveTrack( WaveTrack * t, const wxChar ** ppTip )
|
||||
{
|
||||
mQueuesMutex.Lock();
|
||||
for(unsigned int i=0;i<mQueues.size();i++)
|
||||
{
|
||||
mQueues[i]->FillTipForWaveTrack(t,ppTip);
|
||||
}
|
||||
mQueuesMutex.Unlock();
|
||||
}
|
||||
|
||||
///Gets the total percent complete for all tasks combined.
|
||||
float ODManager::GetOverallPercentComplete()
|
||||
{
|
||||
float total=0.0;
|
||||
mQueuesMutex.Lock();
|
||||
for(unsigned int i=0;i<mQueues.size();i++)
|
||||
{
|
||||
total+=mQueues[i]->GetFrontTask()->PercentComplete();
|
||||
}
|
||||
mQueuesMutex.Unlock();
|
||||
|
||||
//avoid div by zero and be thread smart.
|
||||
int totalTasks = GetTotalNumTasks();
|
||||
return (float) total/(totalTasks>0?totalTasks:1);
|
||||
}
|
||||
|
||||
///Get Total Number of Tasks.
|
||||
int ODManager::GetTotalNumTasks()
|
||||
{
|
||||
int ret=0;
|
||||
mQueuesMutex.Lock();
|
||||
for(unsigned int i=0;i<mQueues.size();i++)
|
||||
{
|
||||
ret+=mQueues[i]->GetNumTasks();
|
||||
}
|
||||
mQueuesMutex.Unlock();
|
||||
return ret;
|
||||
}
|
245
src/ondemand/ODManager.h
Normal file
245
src/ondemand/ODManager.h
Normal file
@@ -0,0 +1,245 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
ODManager.h
|
||||
|
||||
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 ODManager
|
||||
\brief A singleton that manages currently running Tasks on an arbitrary
|
||||
number of threads.
|
||||
|
||||
*//*******************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef __AUDACITY_ODMANAGER__
|
||||
#define __AUDACITY_ODMANAGER__
|
||||
#include <vector>
|
||||
#include "ODTask.h"
|
||||
#include "ODTaskThread.h"
|
||||
#include <wx/thread.h>
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
#ifdef __WXMAC__
|
||||
|
||||
// On Mac OS X, it's better not to use the wxThread class.
|
||||
// We use our own implementation based on pthreads instead.
|
||||
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#endif //__WXMAC__
|
||||
|
||||
|
||||
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);
|
||||
int CompareNoCaseFileName(const wxString& first, const wxString& second);
|
||||
/// A singleton that manages currently running Tasks on an arbitrary
|
||||
/// number of threads.
|
||||
class WaveTrack;
|
||||
class ODWaveTrackTaskQueue;
|
||||
class ODManager
|
||||
{
|
||||
public:
|
||||
///Gets the singleton instance
|
||||
static ODManager* Instance();
|
||||
|
||||
///Kills the ODMananger Thread.
|
||||
static void Quit();
|
||||
|
||||
///changes the tasks associated with this Waveform to process the task from a different point in the track
|
||||
void DemandTrackUpdate(WaveTrack* track, double seconds);
|
||||
|
||||
///Reduces the count of current threads running. Meant to be called when ODTaskThreads end in their own threads. Thread-safe.
|
||||
void DecrementCurrentThreads();
|
||||
|
||||
///Adds a wavetrack, creates a queue member.
|
||||
void AddNewTask(ODTask* task, bool lockMutex=true);
|
||||
|
||||
///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);
|
||||
|
||||
///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);
|
||||
|
||||
///replace the wavetrack whose wavecache the gui watches for updates
|
||||
void ReplaceWaveTrack(WaveTrack* oldTrack,WaveTrack* newTrack);
|
||||
|
||||
///Adds a task to the running queue. Threas-safe.
|
||||
void AddTask(ODTask* task);
|
||||
|
||||
void RemoveTaskIfInQueue(ODTask* task);
|
||||
|
||||
///sets a flag that is set if we have loaded some OD blockfiles from PCM.
|
||||
static void MarkLoadedODFlag();
|
||||
|
||||
///resets a flag that is set if we have loaded some OD blockfiles from PCM.
|
||||
static void UnmarkLoadedODFlag();
|
||||
|
||||
///returns a flag that is set if we have loaded some OD blockfiles from PCM.
|
||||
static bool HasLoadedODFlag();
|
||||
|
||||
///returns whether or not the singleton instance was created yet
|
||||
static bool IsInstanceCreated();
|
||||
|
||||
///fills in the status bar message for a given track
|
||||
void FillTipForWaveTrack( WaveTrack * t, const wxChar ** ppTip );
|
||||
|
||||
///Gets the total percent complete for all tasks combined.
|
||||
float GetOverallPercentComplete();
|
||||
|
||||
///Get Total Number of Tasks.
|
||||
int GetTotalNumTasks();
|
||||
|
||||
//Pause/unpause all OD Tasks. Does not occur immediately.
|
||||
static void Pause(bool pause = true);
|
||||
static void Resume();
|
||||
|
||||
static void LockLibSndFileMutex();
|
||||
static void UnlockLibSndFileMutex();
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
//private constructor - Singleton.
|
||||
ODManager();
|
||||
//private constructor - delete with static method Quit()
|
||||
virtual ~ODManager();
|
||||
///Launches a thread for the manager and starts accepting Tasks.
|
||||
void Init();
|
||||
|
||||
///Start the main loop for the manager.
|
||||
void Start();
|
||||
|
||||
///Remove references in our array to Tasks that have been completed/Schedule new ones
|
||||
void UpdateQueues();
|
||||
|
||||
|
||||
//List of tracks and their active and inactive tasks.
|
||||
std::vector<ODWaveTrackTaskQueue*> mQueues;
|
||||
ODLock mQueuesMutex;
|
||||
|
||||
//List of current Task to do.
|
||||
std::vector<ODTask*> mTasks;
|
||||
//mutex for above variable
|
||||
ODLock mTasksMutex;
|
||||
|
||||
//global pause switch for OD
|
||||
bool mPause;
|
||||
ODLock mPauseLock;
|
||||
|
||||
int mNeedsDraw;
|
||||
|
||||
///Number of threads currently running. Accessed thru multiple threads
|
||||
int mCurrentThreads;
|
||||
//mutex for above variable
|
||||
ODLock mCurrentThreadsMutex;
|
||||
|
||||
|
||||
///Maximum number of threads allowed out.
|
||||
int mMaxThreads;
|
||||
|
||||
bool mTerminate;
|
||||
ODLock mTerminateMutex;
|
||||
|
||||
bool mTerminated;
|
||||
ODLock mTerminatedMutex;
|
||||
|
||||
//for the queue not empty comdition
|
||||
ODLock mQueueNotEmptyCondLock;
|
||||
ODCondition* mQueueNotEmptyCond;
|
||||
|
||||
#ifdef __WXMAC__
|
||||
|
||||
// On Mac OS X, it's better not to use the wxThread class.
|
||||
// We use our own implementation based on pthreads instead.
|
||||
|
||||
class ODManagerHelperThread {
|
||||
public:
|
||||
typedef int ExitCode;
|
||||
ODManagerHelperThread() { mDestroy = false; mThread = NULL; }
|
||||
/* ExitCode*/ void Entry(){
|
||||
ODManager::Instance()->Start();
|
||||
}
|
||||
|
||||
void Create() {}
|
||||
void Delete() {
|
||||
mDestroy = true;
|
||||
pthread_join(mThread, NULL);
|
||||
}
|
||||
bool TestDestroy() { return mDestroy; }
|
||||
void Sleep(int ms) {
|
||||
struct timespec spec;
|
||||
spec.tv_sec = 0;
|
||||
spec.tv_nsec = ms * 1000 * 1000;
|
||||
nanosleep(&spec, NULL);
|
||||
}
|
||||
static void *callback(void *p) {
|
||||
ODManagerHelperThread *th = (ODManagerHelperThread *)p;
|
||||
/* return (void *) */th->Entry();
|
||||
}
|
||||
|
||||
///Specifies the priority the thread will run at. Currently doesn't work.
|
||||
///@param priority value from 0 (min priority) to 100 (max priority)
|
||||
void SetPriority(int priority)
|
||||
{
|
||||
mPriority=priority;
|
||||
}
|
||||
|
||||
void Run() {
|
||||
pthread_create(&mThread, NULL, callback, this);
|
||||
}
|
||||
private:
|
||||
bool mDestroy;
|
||||
pthread_t mThread;
|
||||
|
||||
int mPriority;
|
||||
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class ODManagerHelperThread : public wxThread
|
||||
{
|
||||
public:
|
||||
///Constructs a ODTaskThread
|
||||
///@param task the task to be launched as an
|
||||
ODManagerHelperThread(): wxThread(){}
|
||||
|
||||
protected:
|
||||
///Executes a part of the task
|
||||
void *Entry()
|
||||
{
|
||||
ODManager::Instance()->Start();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //__WXMAC__
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
371
src/ondemand/ODTask.cpp
Normal file
371
src/ondemand/ODTask.cpp
Normal file
@@ -0,0 +1,371 @@
|
||||
/**********************************************************************
|
||||
|
||||
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 "../WaveTrack.h"
|
||||
#include "../Project.h"
|
||||
//temporarilly commented out till it is added to all projects
|
||||
//#include "../Profiler.h"
|
||||
|
||||
|
||||
DEFINE_EVENT_TYPE(EVT_ODTASK_COMPLETE)
|
||||
|
||||
/// Constructs an ODTask
|
||||
ODTask::ODTask()
|
||||
{
|
||||
|
||||
static int sTaskNumber=0;
|
||||
mPercentComplete=0;
|
||||
mDoingTask=false;
|
||||
mTerminate = false;
|
||||
mNeedsODUpdate=false;
|
||||
mIsRunning = false;
|
||||
|
||||
mTaskNumber=sTaskNumber++;
|
||||
|
||||
mDemandSample=0;
|
||||
}
|
||||
|
||||
//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 is 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();
|
||||
|
||||
// printf("%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.
|
||||
AudacityProject::AllProjectsDeleteLock();
|
||||
for(unsigned i=0; i<gAudacityProjects.GetCount(); i++)
|
||||
{
|
||||
if(IsTaskAssociatedWithProject(gAudacityProjects[i]))
|
||||
{
|
||||
//mark the changes so that the project can be resaved.
|
||||
gAudacityProjects[i]->GetUndoManager()->SetODChangesFlag();
|
||||
break;
|
||||
}
|
||||
}
|
||||
AudacityProject::AllProjectsDeleteUnlock();
|
||||
|
||||
|
||||
// printf("%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 );
|
||||
AudacityProject::AllProjectsDeleteLock();
|
||||
|
||||
for(unsigned i=0; i<gAudacityProjects.GetCount(); i++)
|
||||
{
|
||||
if(IsTaskAssociatedWithProject(gAudacityProjects[i]))
|
||||
{
|
||||
//this assumes tasks are only associated with one project.
|
||||
gAudacityProjects[i]->AddPendingEvent( event );
|
||||
//mark the changes so that the project can be resaved.
|
||||
gAudacityProjects[i]->GetUndoManager()->SetODChangesFlag();
|
||||
break;
|
||||
}
|
||||
}
|
||||
AudacityProject::AllProjectsDeleteUnlock();
|
||||
|
||||
// printf("%s %i complete\n", GetTaskName(),GetTaskNumber());
|
||||
}
|
||||
mTerminateMutex.Unlock();
|
||||
SetIsRunning(false);
|
||||
mBlockUntilTerminateMutex.Unlock();
|
||||
}
|
||||
|
||||
bool ODTask::IsTaskAssociatedWithProject(AudacityProject* proj)
|
||||
{
|
||||
TrackList *tracks = proj->GetTracks();
|
||||
TrackListIterator iter1(tracks);
|
||||
Track *tr = iter1.First();
|
||||
|
||||
while (tr)
|
||||
{
|
||||
//go over all tracks in the project
|
||||
if (tr->GetKind() == Track::Wave)
|
||||
{
|
||||
//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]==tr)
|
||||
{
|
||||
//if we find one, then the project is associated with us;return true
|
||||
mWaveTrackMutex.Unlock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
mWaveTrackMutex.Unlock();
|
||||
}
|
||||
tr = iter1.Next();
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
WaveTrack* ODTask::GetWaveTrack(int i)
|
||||
{
|
||||
WaveTrack* track = NULL;
|
||||
mWaveTrackMutex.Lock();
|
||||
if(i<(int)mWaveTracks.size())
|
||||
track = mWaveTracks[i];
|
||||
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)
|
||||
{
|
||||
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])
|
||||
{
|
||||
sampleCount 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] == track)
|
||||
mWaveTracks[i]=NULL;
|
||||
}
|
||||
mWaveTrackMutex.Unlock();
|
||||
}
|
||||
|
||||
///Replaces all instances to a wavetrack with a new one, effectively transferring the task.
|
||||
void ODTask::ReplaceWaveTrack(WaveTrack* oldTrack,WaveTrack* newTrack)
|
||||
{
|
||||
mWaveTrackMutex.Lock();
|
||||
for(size_t i=0;i<mWaveTracks.size();i++)
|
||||
{
|
||||
if(oldTrack == mWaveTracks[i])
|
||||
{
|
||||
mWaveTracks[i] = newTrack;
|
||||
}
|
||||
}
|
||||
mWaveTrackMutex.Unlock();
|
||||
}
|
178
src/ondemand/ODTask.h
Normal file
178
src/ondemand/ODTask.h
Normal file
@@ -0,0 +1,178 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
ODTask.h
|
||||
|
||||
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.
|
||||
|
||||
*//*******************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef __AUDACITY_ODTASK__
|
||||
#define __AUDACITY_ODTASK__
|
||||
|
||||
#include "ODTaskThread.h"
|
||||
#include "../BlockFile.h"
|
||||
#include "../Project.h"
|
||||
|
||||
#include <vector>
|
||||
#include <wx/wx.h>
|
||||
class WaveTrack;
|
||||
|
||||
|
||||
DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_ODTASK_COMPLETE, -1)
|
||||
|
||||
/// A class representing a modular task to be used with the On-Demand structures.
|
||||
class ODTask
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
eODNone = 0x00000000,
|
||||
eODFLAC = 0x00000001,
|
||||
eODMP3 = 0x00000002,
|
||||
eODFFMPEG = 0x00000004,
|
||||
eODPCMSummary = 0x00001000,
|
||||
eODOTHER = 0x10000000,
|
||||
} ODTypeEnum;
|
||||
// Constructor / Destructor
|
||||
|
||||
/// Constructs an ODTask
|
||||
ODTask();
|
||||
|
||||
virtual ~ODTask(){};
|
||||
|
||||
//clones everything except information about the tracks.
|
||||
virtual ODTask* Clone()=0;
|
||||
|
||||
///Subclasses should override to return respective type.
|
||||
virtual unsigned int GetODType(){return eODNone;}
|
||||
|
||||
|
||||
///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 is 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 DoSome(float amountWork=0.0);
|
||||
|
||||
///Call DoSome until PercentComplete >= 1.0
|
||||
void DoAll();
|
||||
|
||||
virtual float PercentComplete();
|
||||
|
||||
virtual bool UsesCustomWorkUntilPercentage(){return false;}
|
||||
virtual float ComputeNextWorkUntilPercentageComplete(){return 1.0;}
|
||||
|
||||
///returns whether or not this task and another task can merge together, as when we make two mono tracks stereo.
|
||||
///for Loading/Summarizing, this is not an issue because the entire track is processed
|
||||
///Effects that affect portions of a track will need to check this.
|
||||
virtual bool CanMergeWith(ODTask* otherTask){return strcmp(GetTaskName(),otherTask->GetTaskName())==0;}
|
||||
|
||||
virtual void StopUsingWaveTrack(WaveTrack* track);
|
||||
|
||||
///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(WaveTrack* oldTrack,WaveTrack* newTrack);
|
||||
|
||||
///Adds a WaveTrack to do the task for
|
||||
void AddWaveTrack(WaveTrack* track);
|
||||
virtual int GetNumWaveTracks();
|
||||
virtual 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);
|
||||
|
||||
bool IsComplete();
|
||||
|
||||
void TerminateAndBlock();
|
||||
///releases memory that the ODTask owns. Subclasses should override.
|
||||
virtual void Terminate(){}
|
||||
|
||||
virtual const char* GetTaskName(){return "ODTask";}
|
||||
|
||||
virtual sampleCount GetDemandSample();
|
||||
|
||||
virtual void SetDemandSample(sampleCount sample);
|
||||
|
||||
///does an od update and then recalculates the data.
|
||||
virtual void RecalculatePercentComplete();
|
||||
|
||||
///returns the number of tasks created before this instance.
|
||||
int GetTaskNumber(){return mTaskNumber;}
|
||||
|
||||
void SetNeedsODUpdate();
|
||||
bool GetNeedsODUpdate();
|
||||
void ResetNeedsODUpdate();
|
||||
|
||||
virtual const wxChar* GetTip()=0;
|
||||
|
||||
///returns true if the task is associated with the project.
|
||||
virtual bool IsTaskAssociatedWithProject(AudacityProject* proj);
|
||||
|
||||
bool IsRunning();
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
///calculates the percentage complete from existing data.
|
||||
virtual void CalculatePercentComplete() = 0;
|
||||
|
||||
///pure virtual function that does some part of the task this object represents.
|
||||
///this function is meant to be called repeatedly until the IsComplete is true.
|
||||
///Does the smallest unit of work for this task.
|
||||
virtual void DoSomeInternal() = 0;
|
||||
|
||||
///virtual method called before DoSomeInternal is used from DoSome.
|
||||
virtual void Update(){}
|
||||
|
||||
///virtual method called in DoSome everytime the user has demanded some OD function so that the
|
||||
///ODTask can readjust its computation order. By default just calls Update(), but subclasses with
|
||||
///special needs can override this
|
||||
virtual void ODUpdate();
|
||||
|
||||
void SetIsRunning(bool value);
|
||||
|
||||
|
||||
|
||||
int mTaskNumber;
|
||||
float mPercentComplete;
|
||||
ODLock mPercentCompleteMutex;
|
||||
bool mDoingTask;
|
||||
bool mTaskStarted;
|
||||
bool mTerminate;
|
||||
ODLock mTerminateMutex;
|
||||
//for a function not a member var.
|
||||
ODLock mBlockUntilTerminateMutex;
|
||||
|
||||
std::vector<WaveTrack*> mWaveTracks;
|
||||
ODLock mWaveTrackMutex;
|
||||
|
||||
sampleCount mDemandSample;
|
||||
ODLock mDemandSampleMutex;
|
||||
|
||||
bool mIsRunning;
|
||||
ODLock mIsRunningMutex;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
bool mNeedsODUpdate;
|
||||
ODLock mNeedsODUpdateMutex;
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
88
src/ondemand/ODTaskThread.cpp
Normal file
88
src/ondemand/ODTaskThread.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
ODTaskThread.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 ODTaskThread
|
||||
\brief A thread that executes a part of the task specfied by an ODTask.
|
||||
|
||||
*//*******************************************************************/
|
||||
|
||||
|
||||
#include "ODTaskThread.h"
|
||||
#include "ODTask.h"
|
||||
#include "ODManager.h"
|
||||
|
||||
|
||||
ODTaskThread::ODTaskThread(ODTask* task)
|
||||
#ifndef __WXMAC__
|
||||
: wxThread()
|
||||
#endif
|
||||
{
|
||||
mTask=task;
|
||||
#ifdef __WXMAC__
|
||||
mDestroy = false;
|
||||
mThread = NULL;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#ifdef __WXMAC__
|
||||
|
||||
void ODTaskThread::Entry()
|
||||
#else
|
||||
void *ODTaskThread::Entry()
|
||||
|
||||
#endif
|
||||
{
|
||||
//TODO: Figure out why this has no effect at all.
|
||||
//wxThread::This()->SetPriority( 40);
|
||||
//Do at least 5 percent of the task
|
||||
mTask->DoSome(0.05f);
|
||||
|
||||
//release the thread count so that the ODManager knows how many active threads are alive.
|
||||
ODManager::Instance()->DecrementCurrentThreads();
|
||||
|
||||
|
||||
#ifndef __WXMAC__
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __WXMAC__
|
||||
ODCondition::ODCondition(ODLock *lock)
|
||||
{
|
||||
condition = (pthread_cond_t *) malloc (sizeof (pthread_cond_t));
|
||||
pthread_cond_init(condition, NULL);
|
||||
m_lock=lock;
|
||||
}
|
||||
ODCondition::~ODCondition()
|
||||
{
|
||||
pthread_cond_destroy (condition);
|
||||
free(condition);
|
||||
}
|
||||
|
||||
void ODCondition::Signal()
|
||||
{
|
||||
pthread_cond_signal(condition);
|
||||
}
|
||||
|
||||
void ODCondition::Broadcast()
|
||||
{
|
||||
pthread_cond_broadcast(condition);
|
||||
}
|
||||
void ODCondition::Wait()
|
||||
{
|
||||
pthread_cond_wait(condition,m_lock->mutex);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
170
src/ondemand/ODTaskThread.h
Normal file
170
src/ondemand/ODTaskThread.h
Normal file
@@ -0,0 +1,170 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
ODTaskThread.h
|
||||
|
||||
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 ODTaskThread
|
||||
\brief A thread that executes a part of the task specfied by an ODTask.
|
||||
|
||||
*//*******************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef __AUDACITY_ODTASKTHREAD__
|
||||
#define __AUDACITY_ODTASKTHREAD__
|
||||
#include <wx/thread.h>
|
||||
|
||||
#include "../Audacity.h" // contains the set-up of AUDACITY_DLL_API
|
||||
|
||||
class ODTask;
|
||||
|
||||
#ifdef __WXMAC__
|
||||
|
||||
// On Mac OS X, it's better not to use the wxThread class.
|
||||
// We use our own implementation based on pthreads instead.
|
||||
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
|
||||
class ODTaskThread {
|
||||
public:
|
||||
typedef int ExitCode;
|
||||
ODTaskThread(ODTask* task);
|
||||
/*ExitCode*/ void Entry();
|
||||
void Create() {}
|
||||
void Delete() {
|
||||
mDestroy = true;
|
||||
pthread_join(mThread, NULL);
|
||||
}
|
||||
bool TestDestroy() { return mDestroy; }
|
||||
void Sleep(int ms) {
|
||||
struct timespec spec;
|
||||
spec.tv_sec = 0;
|
||||
spec.tv_nsec = ms * 1000 * 1000;
|
||||
nanosleep(&spec, NULL);
|
||||
}
|
||||
static void *callback(void *p) {
|
||||
ODTaskThread *th = (ODTaskThread *)p;
|
||||
#if defined(__WXMAC__)
|
||||
/*return (void *)*/ th->Entry();
|
||||
return NULL;
|
||||
#else
|
||||
return (void *) th->Entry();
|
||||
#endif
|
||||
}
|
||||
void Run() {
|
||||
pthread_create(&mThread, NULL, callback, this);
|
||||
}
|
||||
|
||||
///Specifies the priority the thread will run at. Currently doesn't work.
|
||||
///@param priority value from 0 (min priority) to 100 (max priority)
|
||||
void SetPriority(int priority)
|
||||
{
|
||||
mPriority=priority;
|
||||
}
|
||||
|
||||
private:
|
||||
int mPriority;
|
||||
bool mDestroy;
|
||||
pthread_t mThread;
|
||||
|
||||
ODTask* mTask;
|
||||
};
|
||||
|
||||
class ODLock {
|
||||
public:
|
||||
ODLock(){
|
||||
mutex = (pthread_mutex_t *) malloc (sizeof (pthread_mutex_t));
|
||||
pthread_mutex_init (mutex, NULL);
|
||||
}
|
||||
|
||||
void Lock()
|
||||
{
|
||||
pthread_mutex_lock (mutex);
|
||||
}
|
||||
|
||||
void Unlock()
|
||||
{
|
||||
pthread_mutex_unlock (mutex);
|
||||
}
|
||||
|
||||
virtual ~ODLock()
|
||||
{
|
||||
pthread_mutex_destroy (mutex);
|
||||
free(mutex);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ODCondition; //needs friendship for wait()
|
||||
pthread_mutex_t *mutex ;
|
||||
};
|
||||
|
||||
class ODCondition
|
||||
{
|
||||
public:
|
||||
ODCondition(ODLock *lock);
|
||||
virtual ~ODCondition();
|
||||
void Signal();
|
||||
void Broadcast();
|
||||
void Wait();
|
||||
|
||||
protected:
|
||||
pthread_cond_t *condition;
|
||||
ODLock* m_lock;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
|
||||
class ODTaskThread : public wxThread
|
||||
{
|
||||
public:
|
||||
///Constructs a ODTaskThread
|
||||
///@param task the task to be launched as an
|
||||
ODTaskThread(ODTask* task);
|
||||
|
||||
|
||||
protected:
|
||||
///Executes a part of the task
|
||||
virtual void* Entry();
|
||||
ODTask* mTask;
|
||||
|
||||
};
|
||||
|
||||
|
||||
//a wrapper for wxMutex.
|
||||
class AUDACITY_DLL_API ODLock : public wxMutex
|
||||
{
|
||||
public:
|
||||
///Constructs a ODTaskThread
|
||||
///@param task the task to be launched as an
|
||||
ODLock(){}
|
||||
virtual ~ODLock(){}
|
||||
};
|
||||
|
||||
class ODCondition : public wxCondition
|
||||
{
|
||||
public:
|
||||
ODCondition(ODLock *lock):wxCondition(*lock){}
|
||||
virtual ~ODCondition(){}
|
||||
//these calls are implemented in wxCondition:
|
||||
//void Signal();
|
||||
//void Broadcast();
|
||||
//void Wait();
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
#endif // __WXMAC__
|
||||
|
||||
#endif //__AUDACITY_ODTASKTHREAD__
|
||||
|
334
src/ondemand/ODWaveTrackTaskQueue.cpp
Normal file
334
src/ondemand/ODWaveTrackTaskQueue.cpp
Normal file
@@ -0,0 +1,334 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
ODWaveTrackTaskQueue.h
|
||||
|
||||
Created by Michael Chinen (mchinen) on 6/11/08
|
||||
Audacity(R) is copyright (c) 1999-2008 Audacity Team.
|
||||
License: GPL v2. See License.txt.
|
||||
|
||||
******************************************************************//**
|
||||
|
||||
\class ODWaveTrackTaskQueue
|
||||
\brief watches over all to be done (not yet started and started but not finished)
|
||||
tasks associated with a WaveTrack.
|
||||
|
||||
*//*******************************************************************/
|
||||
|
||||
#include "ODWaveTrackTaskQueue.h"
|
||||
#include "ODTask.h"
|
||||
#include "ODManager.h"
|
||||
/// Constructs an ODWaveTrackTaskQueue
|
||||
ODWaveTrackTaskQueue::ODWaveTrackTaskQueue()
|
||||
{
|
||||
}
|
||||
|
||||
ODWaveTrackTaskQueue::~ODWaveTrackTaskQueue()
|
||||
{
|
||||
//we need to delete all ODTasks. We will have to block or wait until block for the active ones.
|
||||
for(unsigned int i=0;i<mTasks.size();i++)
|
||||
{
|
||||
mTasks[i]->TerminateAndBlock();//blocks if active.
|
||||
//small chance we may have rea-added the task back into the queue from a diff thread. - so remove it if we have.
|
||||
ODManager::Instance()->RemoveTaskIfInQueue(mTasks[i]);
|
||||
delete mTasks[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///returns whether or not this queue's task list and another's can merge together, as when we make two mono tracks stereo.
|
||||
bool ODWaveTrackTaskQueue::CanMergeWith(ODWaveTrackTaskQueue* otherQueue)
|
||||
{
|
||||
//have to be very careful when dealing with two lists that need to be locked.
|
||||
if(GetNumTasks()!=otherQueue->GetNumTasks())
|
||||
return false;
|
||||
|
||||
mTasksMutex.Lock();
|
||||
for(unsigned int i=0;i<mTasks.size();i++)
|
||||
{
|
||||
if(!mTasks[i]->CanMergeWith(otherQueue->GetTask(i)))
|
||||
{
|
||||
mTasksMutex.Unlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
mTasksMutex.Unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
///add track to the masterTrack's queue - this will allow future ODScheduling to affect them together.
|
||||
/// 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)
|
||||
{
|
||||
AddWaveTrack(track);
|
||||
mTasksMutex.Lock();
|
||||
for(unsigned int i=0;i<mTasks.size();i++)
|
||||
{
|
||||
mTasks[i]->AddWaveTrack(track);
|
||||
mTasks[i]->SetNeedsODUpdate();
|
||||
}
|
||||
mTasksMutex.Unlock();
|
||||
|
||||
}
|
||||
|
||||
///returns true if the argument is in the WaveTrack list.
|
||||
bool ODWaveTrackTaskQueue::ContainsWaveTrack(WaveTrack* track)
|
||||
{
|
||||
mTracksMutex.Lock();
|
||||
for(unsigned int i=0;i<mTracks.size();i++)
|
||||
{
|
||||
if(mTracks[i]==track)
|
||||
{
|
||||
mTracksMutex.Unlock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
mTracksMutex.Unlock();
|
||||
return false;
|
||||
}
|
||||
///Adds a track to the associated list.
|
||||
void ODWaveTrackTaskQueue::AddWaveTrack(WaveTrack* track)
|
||||
{
|
||||
|
||||
mTracksMutex.Lock();
|
||||
if(track)
|
||||
mTracks.push_back(track);
|
||||
|
||||
mTracksMutex.Unlock();
|
||||
}
|
||||
|
||||
void ODWaveTrackTaskQueue::AddTask(ODTask* task)
|
||||
{
|
||||
mTasksMutex.Lock();
|
||||
mTasks.push_back(task);
|
||||
mTasksMutex.Unlock();
|
||||
|
||||
//take all of the tracks in the 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?
|
||||
mTracks.push_back(task->GetWaveTrack(i));
|
||||
}
|
||||
|
||||
mTracksMutex.Unlock();
|
||||
|
||||
}
|
||||
|
||||
///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)
|
||||
{
|
||||
|
||||
mTracksMutex.Lock();
|
||||
if(mTracks.size()<2)
|
||||
{
|
||||
//if there is only one track, it is already independent.
|
||||
mTracksMutex.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
for(unsigned int i=0;i<mTracks.size();i++)
|
||||
{
|
||||
if(mTracks[i]==track)
|
||||
{
|
||||
mTracksMutex.Unlock();//release the lock, since RemoveWaveTrack is a public threadsafe method.
|
||||
RemoveWaveTrack(mTracks[i]);
|
||||
|
||||
//clone the items in order and add them to the ODManager.
|
||||
mTasksMutex.Lock();
|
||||
ODTask* task;
|
||||
for(unsigned int j=0;j<mTasks.size();j++)
|
||||
{
|
||||
task=mTasks[j]->Clone();
|
||||
task->AddWaveTrack(track);
|
||||
mTasksMutex.Unlock(); //AddNewTask assumes no locks.
|
||||
ODManager::Instance()->AddNewTask(task,false);
|
||||
mTasksMutex.Lock();
|
||||
}
|
||||
mTasksMutex.Unlock();
|
||||
mTracksMutex.Lock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
mTracksMutex.Unlock();
|
||||
}
|
||||
|
||||
///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 ODWaveTrackTaskQueue::DemandTrackUpdate(WaveTrack* track, double seconds)
|
||||
{
|
||||
if(track)
|
||||
{
|
||||
mTracksMutex.Lock();
|
||||
for(unsigned int i=0;i<mTasks.size();i++)
|
||||
{
|
||||
mTasks[i]->DemandTrackUpdate(track,seconds);
|
||||
}
|
||||
|
||||
mTracksMutex.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Replaces all instances of a wavetracck with a new one (effectively transferes the task.)
|
||||
void ODWaveTrackTaskQueue::ReplaceWaveTrack(WaveTrack* oldTrack, WaveTrack* newTrack)
|
||||
{
|
||||
if(oldTrack)
|
||||
{
|
||||
mTasksMutex.Lock();
|
||||
for(unsigned int i=0;i<mTasks.size();i++)
|
||||
mTasks[i]->ReplaceWaveTrack(oldTrack,newTrack);
|
||||
mTasksMutex.Unlock();
|
||||
|
||||
mTracksMutex.Lock();
|
||||
for(unsigned int i=0;i<mTracks.size();i++)
|
||||
if(mTracks[i]==oldTrack)
|
||||
mTracks[i]=newTrack;
|
||||
mTracksMutex.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
//returns the wavetrack at position x.
|
||||
WaveTrack* ODWaveTrackTaskQueue::GetWaveTrack(size_t x)
|
||||
{
|
||||
WaveTrack* ret = NULL;
|
||||
mTracksMutex.Lock();
|
||||
if(x>=0&&x<mTracks.size())
|
||||
ret = mTracks[x];
|
||||
mTracksMutex.Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
///returns the number of wavetracks in this queue.
|
||||
int ODWaveTrackTaskQueue::GetNumWaveTracks()
|
||||
{
|
||||
int ret = 0;
|
||||
mTracksMutex.Lock();
|
||||
ret=mTracks.size();
|
||||
mTracksMutex.Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
///returns the number of ODTasks in this queue
|
||||
int ODWaveTrackTaskQueue::GetNumTasks()
|
||||
{
|
||||
int ret = 0;
|
||||
mTasksMutex.Lock();
|
||||
ret=mTasks.size();
|
||||
mTasksMutex.Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
///returns a ODTask at position x
|
||||
ODTask* ODWaveTrackTaskQueue::GetTask(size_t x)
|
||||
{
|
||||
ODTask* ret = NULL;
|
||||
mTasksMutex.Lock();
|
||||
if(x>=0&&x<mTasks.size())
|
||||
ret = mTasks[x];
|
||||
mTasksMutex.Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//returns true if either tracks or tasks are empty
|
||||
bool ODWaveTrackTaskQueue::IsEmpty()
|
||||
{
|
||||
bool isEmpty;
|
||||
mTracksMutex.Lock();
|
||||
isEmpty = mTracks.size()<=0;
|
||||
mTracksMutex.Unlock();
|
||||
|
||||
mTasksMutex.Lock();
|
||||
isEmpty = isEmpty || mTasks.size()<=0;
|
||||
mTasksMutex.Unlock();
|
||||
|
||||
return isEmpty;
|
||||
}
|
||||
|
||||
//returns true if the foremost task exists and is empty.
|
||||
bool ODWaveTrackTaskQueue::IsFrontTaskComplete()
|
||||
{
|
||||
mTasksMutex.Lock();
|
||||
if(mTasks.size())
|
||||
{
|
||||
//there is a chance the task got updated and now has more to do, (like when it is joined with a new track)
|
||||
//check.
|
||||
mTasks[0]->RecalculatePercentComplete();
|
||||
bool ret;
|
||||
ret = mTasks[0]->IsComplete();
|
||||
mTasksMutex.Unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
mTasksMutex.Unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
///Removes and deletes the front task from the list.
|
||||
void ODWaveTrackTaskQueue::RemoveFrontTask()
|
||||
{
|
||||
mTasksMutex.Lock();
|
||||
if(mTasks.size())
|
||||
{
|
||||
//wait for the task to stop running.
|
||||
delete mTasks[0];
|
||||
mTasks.erase(mTasks.begin());
|
||||
}
|
||||
mTasksMutex.Unlock();
|
||||
}
|
||||
|
||||
///Schedules the front task for immediate execution
|
||||
ODTask* ODWaveTrackTaskQueue::GetFrontTask()
|
||||
{
|
||||
mTasksMutex.Lock();
|
||||
if(mTasks.size())
|
||||
{
|
||||
mTasksMutex.Unlock();
|
||||
return mTasks[0];
|
||||
}
|
||||
mTasksMutex.Unlock();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
///fills in the status bar message for a given track
|
||||
void ODWaveTrackTaskQueue::FillTipForWaveTrack( WaveTrack * t, const wxChar ** ppTip )
|
||||
{
|
||||
if(ContainsWaveTrack(t) && GetNumTasks())
|
||||
{
|
||||
|
||||
// if(GetNumTasks()==1)
|
||||
mTipMsg.Printf(_("%s %2.0f%% complete. Click to change task focal point."), GetFrontTask()->GetTip(), GetFrontTask()->PercentComplete()*100.0 );
|
||||
// else
|
||||
// msg.Printf(_("%s %n additional tasks remaining."), GetFrontTask()->GetTip().c_str(), GetNumTasks());
|
||||
|
||||
*ppTip = mTipMsg.c_str();
|
||||
|
||||
}
|
||||
}
|
114
src/ondemand/ODWaveTrackTaskQueue.h
Normal file
114
src/ondemand/ODWaveTrackTaskQueue.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
ODWaveTrackTaskQueue.h
|
||||
|
||||
Created by Michael Chinen (mchinen)
|
||||
Audacity(R) is copyright (c) 1999-2008 Audacity Team.
|
||||
License: GPL v2. See License.txt.
|
||||
|
||||
******************************************************************//**
|
||||
|
||||
\class ODWaveTrackTaskQueue
|
||||
\brief watches over all to be done (not yet started and started but not finished)
|
||||
tasks associated with a WaveTrack.
|
||||
|
||||
*//*******************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef __AUDACITY_ODWAVETRACKTASKQUEUE__
|
||||
#define __AUDACITY_ODWAVETRACKTASKQUEUE__
|
||||
|
||||
#include <vector>
|
||||
#include "ODTaskThread.h"
|
||||
#include <wx/wx.h>
|
||||
class WaveTrack;
|
||||
class ODTask;
|
||||
/// A class representing a modular task to be used with the On-Demand structures.
|
||||
class ODWaveTrackTaskQueue
|
||||
{
|
||||
public:
|
||||
|
||||
// Constructor / Destructor
|
||||
|
||||
/// Constructs an ODWaveTrackTaskQueue
|
||||
ODWaveTrackTaskQueue();
|
||||
|
||||
virtual ~ODWaveTrackTaskQueue();
|
||||
|
||||
|
||||
|
||||
///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);
|
||||
|
||||
///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(WaveTrack* oldTrack,WaveTrack* newTrack);
|
||||
|
||||
//if the wavetrack is in this queue, and is not the only wavetrack, clones the tasks and schedules it.
|
||||
void MakeWaveTrackIndependent(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.
|
||||
virtual bool CanMergeWith(ODWaveTrackTaskQueue* otherQueue);
|
||||
void MergeWaveTrack(WaveTrack* track);
|
||||
|
||||
|
||||
//returns true if the agrument is in the WaveTrack list.
|
||||
bool ContainsWaveTrack(WaveTrack* track);
|
||||
|
||||
//returns the wavetrack at position x.
|
||||
WaveTrack* GetWaveTrack(size_t x);
|
||||
|
||||
///returns the number of wavetracks in this queue.
|
||||
int GetNumWaveTracks();
|
||||
|
||||
///Add a task to the queue.
|
||||
void AddTask(ODTask* task);
|
||||
|
||||
//returns true if either tracks or tasks are empty
|
||||
bool IsEmpty();
|
||||
|
||||
//returns true if the foremost task exists and is empty.
|
||||
bool IsFrontTaskComplete();
|
||||
|
||||
///Removes and deletes the front task from the list.
|
||||
void RemoveFrontTask();
|
||||
|
||||
///Schedules the front task for immediate execution
|
||||
ODTask* GetFrontTask();
|
||||
|
||||
///returns the number of ODTasks in this queue
|
||||
int GetNumTasks();
|
||||
|
||||
///returns a ODTask at position x
|
||||
ODTask* GetTask(size_t x);
|
||||
|
||||
///fills in the status bar message for a given track
|
||||
void FillTipForWaveTrack( WaveTrack * t, const wxChar ** ppTip );
|
||||
|
||||
protected:
|
||||
|
||||
//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;
|
||||
ODLock mTracksMutex;
|
||||
|
||||
///the list of tasks associated with the tracks. This class owns these tasks.
|
||||
std::vector<ODTask*> mTasks;
|
||||
ODLock mTasksMutex;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user