1
0
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:
ra
2010-01-23 19:44:49 +00:00
commit e74978ba77
1011 changed files with 781704 additions and 0 deletions

View 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.
}

View 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

View 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;
}

View 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

View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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__

View 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();
}
}

View 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