Audacity 3.2.0
WaveTrackShifter.cpp
Go to the documentation of this file.
1
6#include "../../../ui/TimeShiftHandle.h"
7#include "ViewInfo.h"
8#include "WaveClip.h"
9#include "WaveTrack.h"
10#include "WaveChannelView.h"
11
12#include <cassert>
13
14class WaveTrackShifter final : public TrackShifter {
15public:
20 : mpTrack{ track.SharedPointer<WaveTrack>() }
21 {
23 }
24 ~WaveTrackShifter() override {}
25 Track &GetTrack() const override {
26 assert(mpTrack->IsLeader()); // by construction
27 return *mpTrack;
28 }
29
31 double time, const ViewInfo &viewInfo, HitTestParams* params) override
32 {
33 const auto pClip = [&]() -> std::shared_ptr<WaveClip> {
34 for (auto clip : mpTrack->GetClips())
35 if ((
37 *clip, viewInfo, params->rect, { params->xx, params->yy })
38 ) || (
39 // WithinPlayRegion misses first sample, which breaks moving
40 // "selected" clip. Probable WithinPlayRegion should be fixed
41 // instead?
42 clip->GetPlayStartTime() <= time &&
43 time < clip->GetPlayEndTime()
44 ))
45 return clip;
46 return {};
47 }();
48
49 if (!pClip)
51
52 auto t0 = viewInfo.selectedRegion.t0();
53 auto t1 = viewInfo.selectedRegion.t1();
54 if (mpTrack->IsSelected() && time >= t0 && time < t1) {
55 // Unfix maybe many intervals (at least one because of test above)
56 SelectInterval({ t0, t1 });
58 }
59
60 // Select just one interval
61 UnfixIntervals([&](const auto &interval){
62 return static_cast<const WaveTrack::Interval&>(interval).GetClip(0)
63 == pClip;
64 });
65
67 }
68
69 void SelectInterval(const ChannelGroupInterval &interval) override
70 {
71 UnfixIntervals([&](auto &myInterval){
72 // Use a slightly different test from CommonSelectInterval, rounding times
73 // to exact samples according to the clip's rate
74 auto &data = static_cast<const WaveTrack::Interval&>(myInterval);
75 auto clip = data.GetClip(0).get();
76 const auto c0 = mpTrack->TimeToLongSamples(clip->GetPlayStartTime());
77 const auto c1 = mpTrack->TimeToLongSamples(clip->GetPlayEndTime());
78 return
79 mpTrack->TimeToLongSamples(interval.Start()) < c1 &&
80 mpTrack->TimeToLongSamples(interval.End()) > c0;
81 });
82 }
83
84 bool SyncLocks() override { return true; }
85
86 bool MayMigrateTo(Track &other) override
87 {
89 }
90
91 double HintOffsetLarger(double desiredOffset) override
92 {
93 // set it to a sample point, and minimum of 1 sample point
94 bool positive = (desiredOffset > 0);
95 if (!positive)
96 desiredOffset *= -1;
97 double nSamples = rint(mpTrack->GetRate() * desiredOffset);
98 nSamples = std::max(nSamples, 1.0);
99 desiredOffset = nSamples / mpTrack->GetRate();
100 if (!positive)
101 desiredOffset *= -1;
102 return desiredOffset;
103 }
104
105 double QuantizeOffset( double desiredOffset ) override
106 {
107 const auto rate = mpTrack->GetRate();
108 // set it to a sample point
109 return rint(desiredOffset * rate) / rate;
110 }
111
112 double AdjustOffsetSmaller(double desiredOffset) override
113 {
114 std::vector<WaveClip *> movingClips;
115 for (auto &interval : MovingIntervals()) {
116 auto &data = static_cast<WaveTrack::Interval&>(*interval);
117 movingClips.push_back(data.GetClip(0).get());
118 }
119 double newAmount = 0;
120 (void) mpTrack->CanOffsetClips(movingClips, desiredOffset, &newAmount);
121 return newAmount;
122 }
123
124 Intervals Detach() override
125 {
126 // TODO wide wave tracks -- simplify when clips are really wide
127 auto pRight = mpTrack->ChannelGroup::GetChannel<WaveTrack>(1);
128 for (auto &interval: mMoving) {
129 auto &data = static_cast<WaveTrack::Interval&>(*interval);
130 auto pClip = data.GetClip(0).get();
131 // interval will still hold the clip, so ignore the return:
132 (void) mpTrack->RemoveAndReturnClip(pClip);
133 mMigrated.erase(pClip);
134 if (const auto pClip1 = data.GetClip(1).get()) {
135 (void) pRight->RemoveAndReturnClip(pClip1);
136 mMigrated.erase(pClip1);
137 }
138 }
139 return std::move(mMoving);
140 }
141
143 const Track &otherTrack, const Intervals &intervals,
144 double &desiredOffset, double tolerance) override
145 {
146 bool ok = true;
147 auto pOtherWaveTrack = static_cast<const WaveTrack*>(&otherTrack);
148 for (auto &interval: intervals) {
149 auto &data = static_cast<WaveTrack::Interval&>(*interval);
150 auto pClip = data.GetClip(0).get();
151 ok = pClip ? pOtherWaveTrack->CanInsertClip(
152 *pClip, desiredOffset, tolerance) :
153 true;
154 if (!ok)
155 break;
156 }
157 return ok;
158 }
159
160 bool Attach(Intervals intervals, double offset) override
161 {
162 for (auto &interval : intervals) {
163 auto &data = static_cast<WaveTrack::Interval&>(*interval);
164 WaveClipHolder clips[2];
165 for (size_t ii : { 0, 1 }) {
166 // TODO wide wave tracks -- simplify when clips are really wide
167 auto pTrack = mpTrack->ChannelGroup::GetChannel<WaveTrack>(ii);
168 auto &pClip = clips[ii] = data.GetClip(ii);
169 if (pClip) {
170 // TODO wide wave tracks -- guarantee matching clip width
171 if (!pTrack->AddClip(pClip))
172 return false;
173 mMigrated.insert(pClip.get());
174 }
175 }
176 if (offset == .0)
177 mMoving.emplace_back(std::move(interval));
178 else {
179 for (auto pClip : clips)
180 if (pClip)
181 pClip->ShiftBy(offset);
182 mMoving.emplace_back(std::make_shared<WaveTrack::Interval>(
183 GetTrack(), clips[0], clips[1]));
184 }
185 }
186 return true;
187 }
188
189 bool FinishMigration() override
190 {
191 auto rate = mpTrack->GetRate();
192 for (auto pClip : mMigrated) {
193 // Now that user has dropped the clip into a different track,
194 // make sure the sample rate matches the destination track.
195 pClip->Resample(rate);
196 pClip->MarkChanged();
197 }
198 return true;
199 }
200
201 void DoHorizontalOffset(double offset) override
202 {
203 for (auto &interval : MovingIntervals()) {
204 auto &data = static_cast<WaveTrack::Interval&>(*interval);
205 data.GetClip(0)->ShiftBy(offset);
206 if (const auto pClip1 = data.GetClip(1))
207 pClip1->ShiftBy(offset);
208 }
209 }
210
211
212 // Ensure that t0 is still within the clip which it was in before the move.
213 // This corrects for any rounding errors.
214 double AdjustT0(double t0) const override
215 {
216 if (MovingIntervals().empty())
217 return t0;
218 else {
219 auto &data =
220 static_cast<WaveTrack::Interval&>(*MovingIntervals()[0]);
221 auto& clip = data.GetClip(0);
222 t0 = std::clamp(t0, clip->GetPlayStartTime(), clip->GetPlayEndTime());
223 }
224 return t0;
225 }
226
227private:
228 const std::shared_ptr<WaveTrack> mpTrack;
229
230 // Clips that may require resampling
231 std::unordered_set<WaveClip *> mMigrated;
232};
233
236 return [](WaveTrack &track, AudacityProject&) {
237 assert(track.IsLeader()); // pre of the open method
238 return std::make_unique<WaveTrackShifter>(track);
239 };
240}
EffectDistortionSettings params
Definition: Distortion.cpp:77
std::shared_ptr< WaveClip > WaveClipHolder
Definition: WaveClip.h:45
DEFINE_ATTACHED_VIRTUAL_OVERRIDE(MakeWaveTrackShifter)
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
A start and an end time, and whatever else subclasses associate with them.
Definition: Channel.h:30
double End() const
Definition: Channel.h:42
double Start() const
Definition: Channel.h:41
double t1() const
Definition: ViewInfo.h:36
double t0() const
Definition: ViewInfo.h:35
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:122
Abstract base class for policies to manipulate a track type for Time Shift.
void InitIntervals()
Derived class constructor can initialize all intervals reported by the track as fixed,...
const Intervals & MovingIntervals() const
Return special intervals of the track that may move.
void UnfixIntervals(std::function< bool(const ChannelGroupInterval &)> pred)
Change intervals satisfying a predicate from fixed to moving.
bool CommonMayMigrateTo(Track &otherTrack)
HitTestResult
Possibilities for HitTest on the clicked track.
@ Selection
Shift chosen intervals of this track; may shift other tracks' intervals.
@ Intervals
Shift intervals only of selected track and sister channels.
@ Miss
Don't shift anything.
Intervals mMoving
std::vector< std::shared_ptr< ChannelGroupInterval > > Intervals
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:215
static bool HitTest(const WaveClip &clip, const ZoomInfo &zoomInfo, const wxRect &rect, const wxPoint &pos)
std::shared_ptr< const WaveClip > GetClip(size_t iChannel) const
Definition: WaveTrack.h:1009
A Track that contains audio waveform data.
Definition: WaveTrack.h:227
bool IsLeader() const override
Definition: WaveTrack.cpp:2820
Intervals Detach() override
Remove all moving intervals from the track, if possible.
bool Attach(Intervals intervals, double offset) override
Put moving intervals into the track, which may have migrated from another.
double AdjustT0(double t0) const override
const std::shared_ptr< WaveTrack > mpTrack
bool SyncLocks() override
Whether unfixing of an interval should propagate to all overlapping intervals in the sync lock group.
bool FinishMigration() override
When dragging is done, do (once) the final steps of migration (which may be expensive)
void SelectInterval(const ChannelGroupInterval &interval) override
Notifies the shifter that a region is selected, so it may update its fixed and moving intervals.
~WaveTrackShifter() override
double AdjustOffsetSmaller(double desiredOffset) override
Given amount to shift by horizontally, maybe adjust it toward zero to meet placement constraints.
bool MayMigrateTo(Track &other) override
std::unordered_set< WaveClip * > mMigrated
bool AdjustFit(const Track &otherTrack, const Intervals &intervals, double &desiredOffset, double tolerance) override
void DoHorizontalOffset(double offset) override
Track & GetTrack() const override
There is always an associated track.
WaveTrackShifter(WaveTrack &track)
double HintOffsetLarger(double desiredOffset) override
Given amount to shift by horizontally, maybe adjust it from zero to suggest minimum distance.
double QuantizeOffset(double desiredOffset) override
Given amount to shift by horizontally, do any preferred rounding, before placement constraint checks.
HitTestResult HitTest(double time, const ViewInfo &viewInfo, HitTestParams *params) override
Decide how shift behaves, based on the track that is clicked in.
__finl float __vecc rint(float a)
For defining overrides of the method.
Optional, more complete information for hit testing.