// 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; }
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; }
///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); }
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; }
void WaveClip::RemoveAllCutLines() { while (!mCutLines.IsEmpty()) { WaveClipList::compatibility_iterator head = mCutLines.GetFirst(); delete head->GetData(); mCutLines.DeleteNode(head); } }
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); } }
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; }
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 ); }
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; }
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; }
// 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(); } }
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; } }
///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(); }
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()); }
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; }
void WaveClip::Unlock() { GetSequence()->Unlock(); for (WaveClipList::compatibility_iterator it = mCutLines.GetFirst(); it; it=it->GetNext()) it->GetData()->Unlock(); }
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(); } }