void WaveTrack::UpdateLocationsCache() { WaveClipList::Node *it, *jt; mDisplayNumLocations = 0; for (it=GetClipIterator(); it; it=it->GetNext()) { WaveClip* clip = it->GetData(); mDisplayNumLocations += clip->GetCutLines()->GetCount(); for (jt=GetClipIterator(); jt; jt=jt->GetNext()) { WaveClip* clip2 = jt->GetData(); if (clip != clip2 && fabs(clip->GetEndTime()-clip2->GetStartTime()) < WAVETRACK_MERGE_POINT_TOLERANCE) mDisplayNumLocations++; } } if (mDisplayNumLocations == 0) return; if (mDisplayNumLocations > mDisplayNumLocationsAllocated) { // Only realloc, if we need more space than before. Otherwise // just use block from before. if (mDisplayLocations) delete[] mDisplayLocations; mDisplayLocations = new Location[mDisplayNumLocations]; mDisplayNumLocationsAllocated = mDisplayNumLocations; } int curpos = 0; for (it=GetClipIterator(); it; it=it->GetNext()) { WaveClip* clip = it->GetData(); WaveClipList* cutlines = clip->GetCutLines(); for (jt = cutlines->GetFirst(); jt; jt=jt->GetNext()) { mDisplayLocations[curpos].typ = locationCutLine; mDisplayLocations[curpos].pos = jt->GetData()->GetOffset() + it->GetData()->GetOffset(); curpos++; } for (jt=GetClipIterator(); jt; jt=jt->GetNext()) { WaveClip* clip2 = jt->GetData(); if (clip != clip2 && fabs(clip->GetEndTime()-clip2->GetStartTime()) < WAVETRACK_MERGE_POINT_TOLERANCE) { mDisplayLocations[curpos].typ = locationMergePoint; mDisplayLocations[curpos].pos = clip->GetEndTime(); mDisplayLocations[curpos].clipidx1 = mClips.IndexOf(clip); mDisplayLocations[curpos].clipidx2 = mClips.IndexOf(clip2); curpos++; } } } }
void WaveTrack::GetEnvelopeValues(double *buffer, int bufferLen, double t0, double tstep) { memset(buffer, 0, sizeof(double)*bufferLen); double startTime = t0; double endTime = t0+tstep*bufferLen; for (WaveClipList::Node* it=GetClipIterator(); it; it=it->GetNext()) { WaveClip *clip = it->GetData(); if (clip->GetStartTime() < endTime && clip->GetEndTime() > startTime) { double* rbuf = buffer; int rlen = bufferLen; double rt0 = t0; if (rt0 < clip->GetStartTime()) { int dx = (int) floor((clip->GetStartTime() - rt0) / tstep + 0.5); rbuf += dx; rlen -= dx; rt0 = clip->GetStartTime(); } if (rt0+rlen*tstep > clip->GetEndTime()) { rlen = (int) ((clip->GetEndTime()-rt0) / tstep); } clip->GetEnvelope()->GetValues(rbuf, rlen, rt0, tstep); } } }
bool WaveTrack::SplitAt(double t) { for (WaveClipList::Node* it=GetClipIterator(); it; it=it->GetNext()) { WaveClip* c = it->GetData(); if (t > c->GetStartTime() && t < c->GetEndTime()) { WaveClip* newClip = new WaveClip(*c, mDirManager); if (!c->Clear(t, c->GetEndTime())) { delete newClip; return false; } if (!newClip->Clear(c->GetStartTime(), t)) { delete newClip; return false; } newClip->Offset(t - c->GetStartTime()); mClips.Append(newClip); return true; } } return true; }
//Trim trims within a clip, rather than trimming everything. //If a bound is outside a clip, it trims everything. bool WaveTrack::Trim (double t0, double t1) { bool inside0 = false; bool inside1 = false; //Keeps track of the offset of the first clip greater than // the left selection t0. double firstGreaterOffset = -1; WaveClipList::Node * it; for(it = GetClipIterator(); it; it = it->GetNext()) { WaveClip * clip = it->GetData(); //Find the first clip greater than the offset. //If we end up clipping the entire track, this is useful. if(firstGreaterOffset < 0 && clip->GetStartTime() >= t0) firstGreaterOffset = clip->GetStartTime(); if(t1 > clip->GetStartTime() && t1 < clip->GetEndTime()) { if (!clip->Clear(t1,clip->GetEndTime())) return false; inside1 = true; } if(t0 > clip->GetStartTime() && t0 < clip->GetEndTime()) { if (!clip->Clear(clip->GetStartTime(),t0)) return false; clip->SetOffset(t0); inside0 = true; } } //if inside0 is false, then the left selector was between //clips, so delete everything to its left. if(false == inside1) { if (!Clear(t1,GetEndTime())) return false; } if(false == inside0) { if (!Clear(0,t0)) return false; //Reset the track offset to be at the point of the first remaining clip. SetOffset(firstGreaterOffset ); } return true; }
bool WaveTrack::Join(double t0, double t1) { // Merge all WaveClips overlapping selection into one WaveClipList::Node* it; WaveClipList clipsToDelete; WaveClip *newClip; for (it=GetClipIterator(); it; it=it->GetNext()) { WaveClip *clip = it->GetData(); if (clip->GetStartTime() < t1-(1.0/mRate) && clip->GetEndTime()-(1.0/mRate) > t0) { // Put in sorted order int i; for(i=0; i<clipsToDelete.GetCount(); i++) if (clipsToDelete[i]->GetStartTime() > clip->GetStartTime()) break; //printf("Insert clip %.6f at position %d\n", clip->GetStartTime(), i); clipsToDelete.Insert(i, clip); } } newClip = CreateClip(); double t = clipsToDelete[0]->GetOffset(); newClip->SetOffset(t); for(it=clipsToDelete.GetFirst(); it; it=it->GetNext()) { WaveClip *clip = it->GetData(); //printf("t=%.6f adding clip (offset %.6f, %.6f ... %.6f)\n", // t, clip->GetOffset(), clip->GetStartTime(), clip->GetEndTime()); if (clip->GetOffset() - t > (1.0 / mRate)) { double addedSilence = (clip->GetOffset() - t); //printf("Adding %.6f seconds of silence\n"); newClip->InsertSilence(t, addedSilence); t += addedSilence; } //printf("Pasting at %.6f\n", t); newClip->Paste(t, clip); t = newClip->GetEndTime(); mClips.DeleteObject(clip); delete clip; } return true; }
bool SetClipCommand::ApplyInner( const CommandContext & context, Track * t ) { static_cast<void>(context); // if no 'At' is specified, then any clip in any selected track will be set. t->TypeSwitch([&](WaveTrack *waveTrack) { WaveClipPointers ptrs( waveTrack->SortedClipArray()); for(auto it = ptrs.begin(); (it != ptrs.end()); it++ ){ WaveClip * pClip = *it; bool bFound = !bHasContainsTime || ( ( pClip->GetStartTime() <= mContainsTime ) && ( pClip->GetEndTime() >= mContainsTime ) ); if( bFound ) { // Inside this IF is where we actually apply the command if( bHasColour ) pClip->SetColourIndex(mColour); // No validation of overlap yet. We assume the user is sensible! if( bHasT0 ) pClip->SetOffset(mT0); // \todo Use SetClip to move a clip between tracks too. } } } ); return true; }
bool WaveTrack::InsertSilence(double t, double len) { if (len <= 0) return false; if (mClips.IsEmpty()) { // Special case if there is no clip yet WaveClip* clip = CreateClip(); return clip->InsertSilence(0, len); } for (WaveClipList::Node* it=GetClipIterator(); it; it=it->GetNext()) { WaveClip *clip = it->GetData(); if (clip->GetStartTime() > t) clip->Offset(len); else if (clip->GetEndTime() > t) { return clip->InsertSilence(t, len); } } return true; }
bool WaveTrack::CanOffsetClip(WaveClip* clip, double amount, double *allowedAmount /* = NULL */) { if (allowedAmount) *allowedAmount = amount; for (WaveClipList::Node* it=GetClipIterator(); it; it=it->GetNext()) { WaveClip* c = it->GetData(); if (c != clip && c->GetStartTime() < clip->GetEndTime()+amount && c->GetEndTime() > clip->GetStartTime()+amount) { if (!allowedAmount) return false; // clips overlap if (amount > 0) { if (c->GetStartTime()-clip->GetEndTime() < *allowedAmount) *allowedAmount = c->GetStartTime()-clip->GetEndTime(); if (*allowedAmount < 0) *allowedAmount = 0; } else { if (c->GetEndTime()-clip->GetStartTime() > *allowedAmount) *allowedAmount = c->GetEndTime()-clip->GetStartTime(); if (*allowedAmount > 0) *allowedAmount = 0; } } } if (allowedAmount) { if (*allowedAmount == amount) return true; // Check if the new calculated amount would not violate // any other constraint if (!CanOffsetClip(clip, *allowedAmount, NULL)) { *allowedAmount = 0; // play safe and don't allow anything return false; } else return true; } else return true; }
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; }
bool WaveTrack::CanInsertClip(WaveClip* clip) { for (WaveClipList::Node* it=GetClipIterator(); it; it=it->GetNext()) { WaveClip* c = it->GetData(); if (c->GetStartTime() < clip->GetEndTime() && c->GetEndTime() > clip->GetStartTime()) return false; // clips overlap } return true; }
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; }
bool WaveTrack::IsEmpty(double t0, double t1) { WaveClipList::Node* it; //printf("Searching for overlap in %.6f...%.6f\n", t0, t1); for (it=GetClipIterator(); it; it=it->GetNext()) { WaveClip *clip = it->GetData(); if (clip->GetStartTime() < t1-(1.0/mRate) && clip->GetEndTime()-(1.0/mRate) > t0) { //printf("Overlapping clip: %.6f...%.6f\n", // clip->GetStartTime(), // clip->GetEndTime()); // We found a clip that overlaps this region return false; } } //printf("No overlap found\n"); // Otherwise, no clips overlap this region return true; }
bool WaveTrack::GetMinMax(float *min, float *max, double t0, double t1) { *min = float(0.0); *max = float(0.0); if (t0 > t1) return false; if (t0 == t1) return true; bool result = true; for (WaveClipList::Node* it=GetClipIterator(); it; it=it->GetNext()) { WaveClip* clip = it->GetData(); if (t1 >= clip->GetStartTime() && t0 <= clip->GetEndTime()) { float clipmin, clipmax; if (it->GetData()->GetMinMax(&clipmin, &clipmax, t0, t1)) { if (clipmin < *min) *min = clipmin; if (clipmax > *max) *max = clipmax; } else { result = false; } } } return result; }
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 WaveTrack::HandleClear(double t0, double t1, bool addCutLines, bool split) { if (t1 < t0) return false; WaveClipList::Node* it; WaveClipList clipsToDelete; WaveClipList clipsToAdd; for (it=GetClipIterator(); it; it=it->GetNext()) { WaveClip *clip = it->GetData(); if (t0 <= clip->GetStartTime() && t1 >= clip->GetEndTime()) { // Whole clip must be deleted - remember this clipsToDelete.Append(clip); } else if (t1 > clip->GetStartTime() && t0 < clip->GetEndTime()) { // Clip data is affected by command if (addCutLines) { if (!clip->ClearAndAddCutLine(t0,t1)) return false; } else { if (split) { // Three cases: if (t0 <= clip->GetStartTime()) { // Delete from the left edge clip->Clear(clip->GetStartTime(), t1); clip->Offset(t1-clip->GetStartTime()); } else if (t1 >= clip->GetEndTime()) { // Delete to right edge clip->Clear(t0, clip->GetEndTime()); } else { // Delete in the middle of the clip...we actually create two // new clips out of the left and right halves... WaveClip *left = new WaveClip(*clip, mDirManager); left->Clear(t0, clip->GetEndTime()); clipsToAdd.Append(left); WaveClip *right = new WaveClip(*clip, mDirManager); right->Clear(clip->GetStartTime(), t1); right->Offset(t1-clip->GetStartTime()); clipsToAdd.Append(right); clipsToDelete.Append(clip); } } else { if (!clip->Clear(t0,t1)) return false; } } } else if (clip->GetStartTime() >= t1) { // Clip is "behind" the region -- offset it unless we're splitting if (!split) clip->Offset(-(t1-t0)); } } for (it=clipsToDelete.GetFirst(); it; it=it->GetNext()) { mClips.DeleteObject(it->GetData()); delete it->GetData(); } for (it=clipsToAdd.GetFirst(); it; it=it->GetNext()) { mClips.Append(it->GetData()); } return true; }
bool WaveTrack::Paste(double t0, Track *src) { //printf("paste: entering WaveTrack::Paste\n"); if (src->GetKind() != Track::Wave) return false; //printf("paste: we have a wave track\n"); WaveTrack* other = (WaveTrack*)src; // // Pasting is a bit complicated, because with the existence of multiclip mode, // we must guess the behaviour the user wants. // // Currently, two modes are implemented: // // - If a single clip should be pasted, and it should be pasted inside another // clip, no new clips are generated. The audio is simply inserted. // This resembles the old (pre-multiclip support) behaviour. However, if // the clip is pasted outside of any clip, a new clip is generated. This is // the only behaviour which is different to what was done before, but it // shouldn't confuse users too much. // // - If multiple clips should be pasted, these are always pasted as single // clips, and the current clip is splitted, when necessary. This may seem // strange at first, but it probably is better than trying to auto-merge // anything. The user can still merge the clips by hand (which should be // a simple command reachable by a hotkey or single mouse click). // if (other->GetNumClips() == 0) return false; //printf("paste: we have at least one clip\n"); double insertDuration = other->GetEndTime(); WaveClipList::Node* it; //printf("Check if we need to make room for the pasted data\n"); // Make room for the pasted data, unless the space being pasted in is empty of // any clips if (!IsEmpty(t0, t0+insertDuration-1.0/mRate)) { for (it=GetClipIterator(); it; it=it->GetNext()) { WaveClip* clip = it->GetData(); //printf("paste: offsetting already existing clip %i by %f seconds\n", //(int)clip, insertDuration); if (clip->GetStartTime() > t0-(1.0/mRate)) clip->Offset(insertDuration); } } if (other->GetNumClips() == 1) { // Single clip mode // printf("paste: checking for single clip mode!\n"); WaveClip *insideClip = NULL; for (it=GetClipIterator(); it; it=it->GetNext()) { WaveClip *clip = it->GetData(); // The 1.0/mRate is the time for one sample - kind of a fudge factor, // because an overlap of less than a sample should not trigger // traditional behaviour. if (t0+src->GetEndTime()-1.0/mRate > clip->GetStartTime() && t0 < clip->GetEndTime() - 1.0/mRate) { //printf("t0=%.6f: inside clip is %.6f ... %.6f\n", // t0, clip->GetStartTime(), clip->GetEndTime()); insideClip = clip; break; } } if (insideClip) { // Exhibit traditional behaviour //printf("paste: traditional behaviour\n"); return insideClip->Paste(t0, other->GetClipByIndex(0)); } // Just fall through and exhibit new behaviour } // Insert new clips //printf("paste: multi clip mode!\n"); for (it=other->GetClipIterator(); it; it=it->GetNext()) { WaveClip* clip = it->GetData(); WaveClip* newClip = new WaveClip(*clip, mDirManager); newClip->Offset(t0); newClip->MarkChanged(); mClips.Append(newClip); } return true; }
bool WaveTrack::Copy(double t0, double t1, Track **dest) { *dest = NULL; if (t1 <= t0) return false; WaveTrack *newTrack = new WaveTrack(mDirManager); newTrack->Init(*this); WaveClipList::Node* it; for (it=GetClipIterator(); it; it=it->GetNext()) { WaveClip *clip = it->GetData(); if (t0 <= clip->GetStartTime() && t1 >= clip->GetEndTime()) { // Whole clip is in copy region //printf("copy: clip %i is in copy region\n", (int)clip); WaveClip *newClip = new WaveClip(*clip, mDirManager); newClip->RemoveAllCutLines(); newClip->Offset(-t0); newTrack->mClips.Append(newClip); } else if (t1 > clip->GetStartTime() && t0 < clip->GetEndTime()) { // Clip is affected by command //printf("copy: clip %i is affected by command\n", (int)clip); WaveClip *newClip = new WaveClip(*clip, mDirManager); newClip->RemoveAllCutLines(); double clip_t0 = t0; double clip_t1 = t1; if (clip_t0 < clip->GetStartTime()) clip_t0 = clip->GetStartTime(); if (clip_t1 > clip->GetEndTime()) clip_t1 = clip->GetEndTime(); //printf("copy: clip_t0=%f, clip_t1=%f\n", clip_t0, clip_t1); newClip->Offset(-t0); if (newClip->GetOffset() < 0) newClip->SetOffset(0); //printf("copy: clip offset is now %f\n", newClip->GetOffset()); if (!newClip->CreateFromCopy(clip_t0, clip_t1, clip)) { //printf("paste: CreateFromCopy(%f, %f, %i) returns false, quitting\n", // clip_t0, clip_t1, (int)clip); return false; } newTrack->mClips.Append(newClip); } } *dest = newTrack; return true; }
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(); } }