Example #1
0
// get the sum of the sizes of all blocks this track list
// references.  However, if a block is referred to multiple
// times it is only counted once.  Return value is in bytes.
wxLongLong UndoManager::CalculateSpaceUsage(int index)
{
   TrackListOfKindIterator iter(Track::Wave);
   WaveTrack *wt;
   WaveClipList::compatibility_iterator it;
   BlockArray *blocks;
   unsigned int i;

   // get a map of all blocks referenced in this TrackList
   std::map<BlockFile*, wxLongLong> cur;

   wt = (WaveTrack *) iter.First(stack[index]->tracks);
   while (wt) {
      for (it = wt->GetClipIterator(); it; it = it->GetNext()) {
         blocks = it->GetData()->GetSequenceBlockArray();
         for (i = 0; i < blocks->GetCount(); i++) 
         {
            BlockFile* pBlockFile = blocks->Item(i)->f;
            if (pBlockFile->GetFileName().FileExists())
               cur[pBlockFile] = pBlockFile->GetSpaceUsage();
         }
      }
      wt = (WaveTrack *) iter.Next();
   }

   if (index > 0) {
      // get a set of all blocks referenced in all prev TrackList
      std::set<BlockFile*> prev;
      while (--index) {
         wt = (WaveTrack *) iter.First(stack[index]->tracks);
         while (wt) {
            for (it = wt->GetClipIterator(); it; it = it->GetNext()) {
               blocks = it->GetData()->GetSequenceBlockArray();
               for (i = 0; i < blocks->GetCount(); i++) {
                  prev.insert(blocks->Item(i)->f);
               }
            }
            wt = (WaveTrack *) iter.Next();
         }
      }

      // remove all blocks in prevBlockFiles from curBlockFiles
      std::set<BlockFile*>::const_iterator prevIter;
      for (prevIter = prev.begin(); prevIter != prev.end(); prevIter++) {
         cur.erase(*prevIter);
      }
   }

   // sum the sizes of the blocks remaining in curBlockFiles;
   wxLongLong bytes = 0;
   std::map<BlockFile*, wxLongLong>::const_iterator curIter;
   for (curIter = cur.begin(); curIter != cur.end(); curIter++) {
      bytes += curIter->second;
   }

   return bytes;
}
Example #2
0
///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();

         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 insertCursor;

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

            seq->UnlockDeleteUpdateMutex();
            node = node->GetNext();
         }
      }
   }
   mWaveTrackMutex.Unlock();

   //get the new order.
   OrderBlockFiles(tempBlocks);
}
Example #3
0
bool WaveClip::Clear(double t0, double t1)
{
   sampleCount s0, s1;

   TimeToSamplesClip(t0, &s0);
   TimeToSamplesClip(t1, &s1);

   if (GetSequence()->Delete(s0, s1-s0))
   {
      // msmeyer
      //
      // Delete all cutlines that are within the given area, if any.
      //
      // Note that when cutlines are active, two functions are used:
      // Clear() and ClearAndAddCutLine(). ClearAndAddCutLine() is called
      // whenever the user directly calls a command that removes some audio, e.g.
      // "Cut" or "Clear" from the menu. This command takes care about recursive
      // preserving of cutlines within clips. Clear() is called when internal
      // operations want to remove audio. In the latter case, it is the right
      // thing to just remove all cutlines within the area.
      //
      double clip_t0 = t0;
      double clip_t1 = t1;
      if (clip_t0 < GetStartTime())
         clip_t0 = GetStartTime();
      if (clip_t1 > GetEndTime())
         clip_t1 = GetEndTime();

      WaveClipList::compatibility_iterator nextIt;

      for (WaveClipList::compatibility_iterator it = mCutLines.GetFirst(); it; it=nextIt)
      {
         nextIt = it->GetNext();
         WaveClip* clip = it->GetData();
         double cutlinePosition = mOffset + clip->GetOffset();
         if (cutlinePosition >= t0 && cutlinePosition <= t1)
         {
            // This cutline is within the area, delete it
            delete clip;
            mCutLines.DeleteNode(it);
         } else
         if (cutlinePosition >= t1)
         {
            clip->Offset(clip_t0-clip_t1);
         }
      }

      // Collapse envelope
      GetEnvelope()->CollapseRegion(t0, t1);
      if (t0 < GetStartTime())
         Offset(-(GetStartTime() - t0));

      MarkChanged();
      return true;
   }

   return false;
}
Example #4
0
void WaveClip::OffsetCutLines(double t0, double len)
{
   for (WaveClipList::compatibility_iterator it = mCutLines.GetFirst(); it; it=it->GetNext())
   {
      WaveClip* cutLine = it->GetData();
      if (mOffset + cutLine->GetOffset() >= t0)
         cutLine->Offset(len);
   }
}
Example #5
0
void UndoManager::CalculateSpaceUsage()
{
   TIMER_START( "CalculateSpaceUsage", space_calc );
   TrackListOfKindIterator iter(Track::Wave);

   space.Clear();
   space.Add(0, stack.GetCount());

   Set s1, s2;
   Set *prev = &s1;
   Set *cur = &s2;

   for (size_t i = 0, cnt = stack.GetCount(); i < cnt; i++)
   {
      // Swap map pointers
      std::swap(cur, prev);

      // And clean out the NEW current map
      cur->clear();

      // Scan all tracks at current level
      WaveTrack *wt = (WaveTrack *) iter.First(stack[i]->tracks);
      while (wt)
      {
         // Scan all clips within current track
         WaveClipList::compatibility_iterator it = wt->GetClipIterator();
         while (it)
         {
            // Scan all blockfiles within current clip
            BlockArray *blocks = it->GetData()->GetSequenceBlockArray();
            for (size_t b = 0, cnt = blocks->size(); b < cnt; b++)
            {
               BlockFile *file = (*blocks)[b].f;

               // Accumulate space used by the file if the file didn't exist
               // in the previous level
               if (prev->count(file) == 0 && cur->count(file) == 0)
               {
                  space[i] += file->GetSpaceUsage().GetValue();
               }
               
               // Add file to current set
               cur->insert(file);
            }
            
            it = it->GetNext();
         }

         wt = (WaveTrack *) iter.Next();
      }
   }

   TIMER_STOP( space_calc );
}
Example #6
0
bool WaveClip::RemoveCutLine(double cutLinePosition)
{
   for (WaveClipList::compatibility_iterator it = mCutLines.GetFirst(); it; it=it->GetNext())
   {
      if (fabs(mOffset + it->GetData()->GetOffset() - cutLinePosition) < 0.0001)
      {
         delete it->GetData();
         mCutLines.DeleteNode(it);
         return true;
      }
   }

   return false;
}
Example #7
0
bool WaveClip::ExpandCutLine(double cutLinePosition)
{
   for (WaveClipList::compatibility_iterator it = mCutLines.GetFirst(); it; it=it->GetNext())
   {
      WaveClip* cutline = it->GetData();
      if (fabs(mOffset + cutline->GetOffset() - cutLinePosition) < 0.0001)
      {
         if (!Paste(mOffset+cutline->GetOffset(), cutline))
            return false;
         delete cutline;
         mCutLines.DeleteNode(it);
         return true;
      }
   }

   return false;
}
Example #8
0
bool WaveClip::FindCutLine(double cutLinePosition,
                           double* cutlineStart /* = NULL */,
                           double* cutlineEnd /* = NULL */)
{
   for (WaveClipList::compatibility_iterator it = mCutLines.GetFirst(); it; it=it->GetNext())
   {
      WaveClip* cutline = it->GetData();
      if (fabs(mOffset + cutline->GetOffset() - cutLinePosition) < 0.0001)
      {
         if (cutlineStart)
            *cutlineStart = mOffset+cutline->GetStartTime();
         if (cutlineEnd)
            *cutlineEnd = mOffset+cutline->GetEndTime();
         return true;
      }
   }
   
   return false;
}
Example #9
0
// Given a project, returns a single array of all SeqBlocks
// in the current set of tracks.  Enumerating that array allows
// you to process all block files in the current set.
void GetAllSeqBlocks(AudacityProject *project,
                     BlockArray *outBlocks)
{
   TrackList *tracks = project->GetTracks();
   TrackListIterator iter(tracks);
   Track *t = iter.First();
   while (t) {
      if (t->GetKind() == Track::Wave) {
         WaveTrack *waveTrack = (WaveTrack *)t;
         WaveClipList::compatibility_iterator node = waveTrack->GetClipIterator();
         while(node) {
            WaveClip *clip = node->GetData();
            Sequence *sequence = clip->GetSequence();
            BlockArray *blocks = sequence->GetBlockArray();
            int i;
            for (i = 0; i < (int)blocks->GetCount(); i++)
               outBlocks->Add(blocks->Item(i));
            node = node->GetNext();
         }
      }
      t = iter.Next();
   }
}
Example #10
0
void WaveClip::Unlock()
{
   GetSequence()->Unlock();
   for (WaveClipList::compatibility_iterator it = mCutLines.GetFirst(); it; it=it->GetNext())
      it->GetData()->Unlock();
}
Example #11
0
bool WaveClip::ClearAndAddCutLine(double t0, double t1)
{
   if (t0 > GetEndTime() || t1 < GetStartTime())
      return true; // time out of bounds
      
   WaveClip *newClip = new WaveClip(mSequence->GetDirManager(),
                                    mSequence->GetSampleFormat(),
                                    mRate);
   double clip_t0 = t0;
   double clip_t1 = t1;
   if (clip_t0 < GetStartTime())
      clip_t0 = GetStartTime();
   if (clip_t1 > GetEndTime())
      clip_t1 = GetEndTime();

   if (!newClip->CreateFromCopy(clip_t0, clip_t1, this))
      return false;
   newClip->SetOffset(clip_t0-mOffset);

   // Sort out cutlines that belong to the new cutline
   WaveClipList::compatibility_iterator nextIt;

   for (WaveClipList::compatibility_iterator it = mCutLines.GetFirst(); it; it=nextIt)
   {
      nextIt = it->GetNext();
      WaveClip* clip = it->GetData();
      double cutlinePosition = mOffset + clip->GetOffset();
      if (cutlinePosition >= t0 && cutlinePosition <= t1)
      {
         clip->SetOffset(cutlinePosition - newClip->GetOffset() - mOffset);
         newClip->mCutLines.Append(clip);
         mCutLines.DeleteNode(it);
      } else
      if (cutlinePosition >= t1)
      {
         clip->Offset(clip_t0-clip_t1);
      }
   }
   
   // Clear actual audio data
   sampleCount s0, s1;

   TimeToSamplesClip(t0, &s0);
   TimeToSamplesClip(t1, &s1);
   
   if (GetSequence()->Delete(s0, s1-s0))
   {
      // Collapse envelope
      GetEnvelope()->CollapseRegion(t0, t1);
      if (t0 < GetStartTime())
         Offset(-(GetStartTime() - t0));

      MarkChanged();

      mCutLines.Append(newClip);
      return true;
   } else
   {
      delete newClip;
      return false;
   }
}
Example #12
0
bool WaveClip::Paste(double t0, WaveClip* other)
{
   WaveClip* pastedClip;

   bool clipNeedsResampling = other->mRate != mRate;

   if (clipNeedsResampling)
   {
      // The other clip's rate is different to our's, so resample
      pastedClip = new WaveClip(*other, mSequence->GetDirManager());
      if (!pastedClip->Resample(mRate))
      {
         delete pastedClip;
         return false;
      }
   } else
   {
      // No resampling needed, just use original clip without making a copy
      pastedClip = other;
   }

   sampleCount s0;
   TimeToSamplesClip(t0, &s0);

   // Force sample formats to match.
   if (pastedClip->mSequence->GetSampleFormat() != mSequence->GetSampleFormat())
      pastedClip->ConvertToSampleFormat(mSequence->GetSampleFormat());

   bool result = false;
   if (mSequence->Paste(s0, pastedClip->mSequence))
   {
      MarkChanged();
      mEnvelope->Paste((double)s0/mRate + mOffset, pastedClip->mEnvelope);
      mEnvelope->RemoveUnneededPoints();
      OffsetCutLines(t0, pastedClip->GetEndTime() - pastedClip->GetStartTime());
      
      // Paste cut lines contained in pasted clip
      for (WaveClipList::compatibility_iterator it = pastedClip->mCutLines.GetFirst(); it; it=it->GetNext())
      {
         WaveClip* cutline = it->GetData();
         WaveClip* newCutLine = new WaveClip(*cutline,
                                             mSequence->GetDirManager());
         newCutLine->Offset(t0 - mOffset);
         mCutLines.Append(newCutLine);
      }
      
      result = true;
   }
   
   if (clipNeedsResampling)
   {
      // Clip was constructed as a copy, so delete it
      delete pastedClip;
   }

   return result;
}
///creates the order of the wavetrack to load.
///by default left to right, or frome the point the user has clicked.
void ODComputeSummaryTask::Update()
{
   std::vector<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();

         while(node) {
            clip = node->GetData();
            seq = clip->GetSequence();
            //This lock may be way too big since the whole file is one sequence.
            //TODO: test for large files and find a way to break it down.
            Sequence::DeleteUpdateMutexLocker locker(*seq);

            //See Sequence::Delete() for why need this for now..
            //We don't need the mBlockFilesMutex here because it is only for the vector list.
            //These are existing blocks, and its wavetrack or blockfiles won't be deleted because
            //of the respective mWaveTrackMutex lock and LockDeleteUpdateMutex() call.
            blocks = clip->GetSequenceBlockArray();
            int i;
            int insertCursor;

            insertCursor =0;//OD TODO:see if this works, removed from inner loop (bfore was n*n)

            for(i=0; i<(int)blocks->size(); i++)
            {
               //if there is data but no summary, this blockfile needs summarizing.
               SeqBlock &block = (*blocks)[i];
               BlockFile *const file = block.f;
               if(file->IsDataAvailable() && !file->IsSummaryAvailable())
               {
                  file->Ref();
                  ODPCMAliasBlockFile *const odpcmaFile = static_cast<ODPCMAliasBlockFile*>(file);
                  odpcmaFile->SetStart(block.start);
                  odpcmaFile->SetClipOffset((sampleCount)(clip->GetStartTime()*clip->GetRate()));

                  //these will always be linear within a sequence-lets take advantage of this by keeping a cursor.
                  while(insertCursor<(int)tempBlocks.size()&&
                     (sampleCount)(tempBlocks[insertCursor]->GetStart()+tempBlocks[insertCursor]->GetClipOffset()) <
                        (sampleCount)(odpcmaFile->GetStart()+odpcmaFile->GetClipOffset()))
                     insertCursor++;

                  tempBlocks.insert(tempBlocks.begin() + insertCursor++, odpcmaFile);
               }
            }
            node = node->GetNext();
         }
      }
   }
   mWaveTrackMutex.Unlock();

   //get the NEW order.
   mBlockFilesMutex.Lock();
   OrderBlockFiles(tempBlocks);
   mBlockFilesMutex.Unlock();

   MarkUpdateRan();
}
Example #14
0
void SnapManager::Reinit()
{
   int snapTo = mProject->GetSnapTo();
   double rate = mProject->GetRate();
   wxString format = mProject->GetSelectionFormat();

   // No need to reinit if these are still the same
   if (snapTo == mSnapTo && rate == mRate && format == mFormat)
   {
      return;
   }

   // Save NEW settings
   mSnapTo = snapTo;
   mRate = rate;
   mFormat = format;

   mSnapPoints.clear();

   // Grab time-snapping prefs (unless otherwise requested)
   mSnapToTime = false;

   // Look up the format string
   if (mSnapTo != SNAP_OFF && !mNoTimeSnap)
   {
      mSnapToTime = true;
      mConverter.SetSampleRate(mRate);
      mConverter.SetFormatName(mFormat);
   }

   // Add a SnapPoint at t=0
   mSnapPoints.push_back(SnapPoint{});

   TrackListIterator iter(mTracks);
   for (Track *track = iter.First();  track; track = iter.Next())
   {
      if (mTrackExclusions && mTrackExclusions->Index(track) != wxNOT_FOUND)
      {
         continue;
      }

      if (track->GetKind() == Track::Label)
      {
         LabelTrack *labelTrack = (LabelTrack *)track;
         for (int i = 0, cnt = labelTrack->GetNumLabels(); i < cnt; ++i)
         {
            const LabelStruct *label = labelTrack->GetLabel(i);
            const double t0 = label->getT0();
            const double t1 = label->getT1();
            CondListAdd(t0, labelTrack);
            if (t1 != t0)
            {
               CondListAdd(t1, labelTrack);
            }
         }
      }
      else if (track->GetKind() == Track::Wave)
      {
         WaveTrack *waveTrack = (WaveTrack *)track;
         WaveClipList::compatibility_iterator it;
         for (it = waveTrack->GetClipIterator(); it; it = it->GetNext())
         {
            WaveClip *clip = it->GetData();
            if (mClipExclusions)
            {
               bool skip = false;
               for (size_t j = 0, cnt = mClipExclusions->GetCount(); j < cnt; ++j)
               {
                  if (mClipExclusions->Item(j).track == waveTrack &&
                      mClipExclusions->Item(j).clip == clip)
                  {
                     skip = true;
                     break;
                  }
               }

               if (skip)
               {
                  continue;
               }
            }

            CondListAdd(clip->GetStartTime(), waveTrack);
            CondListAdd(clip->GetEndTime(), waveTrack);
         }
      }
#ifdef USE_MIDI
      else if (track->GetKind() == Track::Note)
      {
         CondListAdd(track->GetStartTime(), track);
         CondListAdd(track->GetEndTime(), track);
      }
#endif
   }

   // Sort all by time
   std::sort(mSnapPoints.begin(), mSnapPoints.end());
}
Example #15
0
bool EffectReverse::ProcessOneWave(int count, WaveTrack * track, sampleCount start, sampleCount len)
{
   bool rValue = true; // return value

   sampleCount end = (sampleCount) start + len; // start, end, len refer to the selected reverse region

   // STEP 1:
   // If a reverse selection begins and/or ends at the inside of a clip
   // perform a split at the start and/or end of the reverse selection
   WaveClipList::compatibility_iterator node = track->GetClipIterator();
   while (node) {
      WaveClip *clip = node->GetData();
      sampleCount clipStart = clip->GetStartSample();
      sampleCount clipEnd = clip->GetEndSample();
      if (clipStart < start && clipEnd > start && clipEnd <= end) { // the reverse selection begins at the inside of a clip
         double splitTime = track->LongSamplesToTime(start);
         track->SplitAt(splitTime);
      }
      else if (clipStart >= start && clipStart < end && clipEnd > end) { // the reverse selection ends at the inside of a clip
         double splitTime = track->LongSamplesToTime(end);
         track->SplitAt(splitTime);
      }
      else if (clipStart < start && clipEnd > end) { // the selection begins AND ends at the inside of a clip
         double splitTime = track->LongSamplesToTime(start);
         track->SplitAt(splitTime);
         splitTime = track->LongSamplesToTime(end);
         track->SplitAt(splitTime);
      }
      node = node->GetNext();
   }

   //STEP 2:
   // Individually reverse each clip inside the selected region
   // and apply the appropriate offset after detaching them from the track

   bool checkedFirstClip = false;
   
   // used in calculating the offset of clips to rearrange
   // holds the new end position of the current clip
   sampleCount currentEnd = (sampleCount)end;
   
   WaveClipList revClips; // holds the reversed clips
   WaveClipList otherClips; // holds the clips that appear after the reverse selection region
   WaveClipArray clipArray;
   track->FillSortedClipArray(clipArray);
   size_t i;
   for (i=0; i < clipArray.Count(); i++) {
      
      WaveClip *clip = clipArray.Item(i);
      sampleCount clipStart = clip->GetStartSample();
      sampleCount clipEnd = clip->GetEndSample();
            
      if (clipStart >= start && clipEnd <= end) { // if the clip is inside the selected region
         
         // this is used to check if the selected region begins with a whitespace.
         // if yes then clipStart (of the first clip) and start are not the same.
         // adjust currentEnd accordingly and set endMerge to false
         if(checkedFirstClip == false && clipStart > start) { 
            checkedFirstClip = true;
            if(i > 0) {
               if (clipArray.Item(i-1)->GetEndSample() <= start) {
                  currentEnd -= (clipStart - start);
               }
            }
            else {
               currentEnd -= (clipStart - start);
            }
         }
         
         sampleCount revStart = (clipStart >= start)? clipStart: start;
         sampleCount revEnd = (clipEnd >= end)? end: clipEnd;
         sampleCount revLen = (sampleCount)revEnd-revStart;
         if (revEnd >= revStart) {
            if(!ProcessOneClip(count, track, revStart, revLen, start, end)) // reverse the clip
            {
               rValue = false;
               break;
            }

            sampleCount clipOffsetStart = (sampleCount)(currentEnd - (clipEnd-clipStart)); // calculate the offset required
            double offsetStartTime = track->LongSamplesToTime(clipOffsetStart);
            if(i+1 < clipArray.Count()) // update currentEnd if there is a clip to process next
            {
               sampleCount nextClipStart = clipArray.Item(i+1)->GetStartSample();
               currentEnd = (sampleCount)(currentEnd - (clipEnd - clipStart) - (nextClipStart - clipEnd));
            }

            clip = track->RemoveAndReturnClip(clip); // detach the clip from track
            clip->SetOffset(track->LongSamplesToTime(track->TimeToLongSamples(offsetStartTime))); // align time to a sample and set offset
            revClips.Append(clip);
            
         }
      }
      else if (clipStart >= end) { // clip is after the selection region
         clip = track->RemoveAndReturnClip(clip); // simply remove and append to otherClips
         otherClips.Append(clip);
      }
   }
   
   // STEP 3: Append the clips from
   // revClips and otherClips back to the track
   size_t revClipsCount = revClips.GetCount();
   for (i = 0; i < revClipsCount; i++) {
      WaveClipList::compatibility_iterator node = revClips.Item(revClipsCount - 1 - i); // the last clip of revClips is appended to the track first
      WaveClip *clip = node->GetData();
      track->AddClip(clip);
   }

   for (i = 0; i < otherClips.GetCount(); i++) {
      WaveClipList::compatibility_iterator node = otherClips.Item(i);
      track->AddClip(node->GetData());
   }

   return rValue;
}
Example #16
0
SnapManager::SnapManager(TrackList *tracks, TrackClipArray *exclusions,
                         double zoom, int pixelTolerance, bool noTimeSnap)
{
   int i;

   // Grab time-snapping prefs (unless otherwise requested)
   mSnapToTime = false;

   AudacityProject *p = GetActiveProject();
   wxASSERT(p);
   if (p)
   {
      // Look up the format string
      if (p->GetSnapTo() && !noTimeSnap) {
         mSnapToTime = true;
         mConverter.SetSampleRate(p->GetRate());
         mConverter.SetFormatName(p->GetSelectionFormat());
      }
   }

   mSnapPoints = new SnapPointArray(CompareSnapPoints);
   if (zoom > 0 && pixelTolerance > 0)
      mTolerance = pixelTolerance / zoom;
   else {
      // This shouldn't happen, but we don't want to crash if we get
      // illegal values.  The net effect of this is to never snap.
      mTolerance = 0.0;
   }
   // Two time points closer than this are considered the same
   mEpsilon = 1 / 44100.0;

   // Add a SnapPoint at t=0
   mSnapPoints->Add(new SnapPoint(0.0, NULL));

   TrackListIterator iter(tracks);
   Track *track = iter.First();
   while (track) {
      if (track->GetKind() == Track::Label) {
         LabelTrack *labelTrack = (LabelTrack *)track;
         for(i = 0; i < labelTrack->GetNumLabels(); i++) {
            const LabelStruct *label = labelTrack->GetLabel(i);
            CondListAdd(label->t, labelTrack);
            if (label->t1 != label->t) {
               CondListAdd(label->t1, labelTrack);
            }
         }
      }
      else if (track->GetKind() == Track::Wave) {
         WaveTrack *waveTrack = (WaveTrack *)track;
         WaveClipList::compatibility_iterator it;
         for (it=waveTrack->GetClipIterator(); it; it=it->GetNext()) {
            WaveClip *clip = it->GetData();
            if (exclusions) {
               bool skip = false;
               for(int j=0; j<(int)exclusions->GetCount(); j++) {
                  if ((*exclusions)[j].track == waveTrack &&
                      (*exclusions)[j].clip == clip)
                     skip = true;
               }
               if (skip)
                  continue;
            }
            CondListAdd(clip->GetStartTime(), waveTrack);
            CondListAdd(clip->GetEndTime(), waveTrack);
         }
      }
#ifdef USE_MIDI
      else if (track->GetKind() == Track::Note) {
         CondListAdd(track->GetStartTime(), track);
         CondListAdd(track->GetEndTime(), track);
      }
#endif
      track = iter.Next();
   }
}