Audacity 3.2.0
Reverse.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 Reverse.cpp
6
7 Mark Phillips
8
9*******************************************************************//********************************************************************/
15
16
17
18#include "Reverse.h"
19#include "LoadEffects.h"
20
21#include <math.h>
22
23#include "../LabelTrack.h"
24#include "../SyncLock.h"
25#include "../WaveClip.h"
26#include "../WaveTrack.h"
27
28//
29// EffectReverse
30//
31
33{ XO("Reverse") };
34
36
38{
39}
40
42{
43}
44
45// ComponentInterface implementation
46
48{
49 return Symbol;
50}
51
53{
54 return XO("Reverses the selected audio");
55}
56
57// EffectDefinitionInterface implementation
58
60{
61 return EffectTypeProcess;
62}
63
65{
66 return false;
67}
68
69// Effect implementation
70
72{
73 //all needed because Reverse should move the labels too
74 this->CopyInputTracks(true); // Set up mOutputTracks.
75 bool bGoodResult = true;
76 int count = 0;
77
78 auto trackRange =
80 trackRange.VisitWhile( bGoodResult,
81 [&](WaveTrack * track) {
82 if (mT1 > mT0) {
83 auto start = track->TimeToLongSamples(mT0);
84 auto end = track->TimeToLongSamples(mT1);
85 auto len = end - start;
86
87 if (!ProcessOneWave(count, track, start, len))
88 bGoodResult = false;
89 }
90 count++;
91 },
92 [&](LabelTrack * track) {
93 track->ChangeLabelsOnReverse(mT0, mT1);
94 count++;
95 }
96 );
97
98 this->ReplaceProcessedTracks(bGoodResult);
99 return bGoodResult;
100}
101
103{
104 bool rValue = true; // return value
105
106 auto end = start + len; // start, end, len refer to the selected reverse region
107
108 // STEP 1:
109 // If a reverse selection begins and/or ends at the inside of a clip
110 // perform a split at the start and/or end of the reverse selection
111 const auto &clips = track->GetClips();
112 // Beware, the array grows as we loop over it. Use integer subscripts, not iterators.
113 for (size_t ii = 0; ii < clips.size(); ++ii) {
114 const auto &clip = clips[ii].get();
115 auto clipStart = clip->GetPlayStartSample();
116 auto clipEnd = clip->GetPlayEndSample();
117 if (clipStart < start && clipEnd > start && clipEnd <= end) { // the reverse selection begins at the inside of a clip
118 double splitTime = track->LongSamplesToTime(start);
119 track->SplitAt(splitTime);
120 }
121 else if (clipStart >= start && clipStart < end && clipEnd > end) { // the reverse selection ends at the inside of a clip
122 double splitTime = track->LongSamplesToTime(end);
123 track->SplitAt(splitTime);
124 }
125 else if (clipStart < start && clipEnd > end) { // the selection begins AND ends at the inside of a clip
126 double splitTime = track->LongSamplesToTime(start);
127 track->SplitAt(splitTime);
128 splitTime = track->LongSamplesToTime(end);
129 track->SplitAt(splitTime);
130 }
131 }
132
133 //STEP 2:
134 // Individually reverse each clip inside the selected region
135 // and apply the appropriate offset after detaching them from the track
136
137 bool checkedFirstClip = false;
138
139 // used in calculating the offset of clips to rearrange
140 // holds the NEW end position of the current clip
141 auto currentEnd = end;
142
143 WaveClipHolders revClips; // holds the reversed clips
144 WaveClipHolders otherClips; // holds the clips that appear after the reverse selection region
145 auto clipArray = track->SortedClipArray();
146 size_t i;
147 for (i=0; i < clipArray.size(); i++) {
148
149 WaveClip *clip = clipArray[i];
150 auto clipStart = clip->GetPlayStartSample();
151 auto clipEnd = clip->GetPlayEndSample();
152
153 if (clipStart >= start && clipEnd <= end) { // if the clip is inside the selected region
154
155 // this is used to check if the selected region begins with a whitespace.
156 // if yes then clipStart (of the first clip) and start are not the same.
157 // adjust currentEnd accordingly and set endMerge to false
158 if(checkedFirstClip == false && clipStart > start) {
159 checkedFirstClip = true;
160 if(i > 0) {
161 if (clipArray[i-1]->GetPlayEndSample() <= start) {
162 currentEnd -= (clipStart - start);
163 }
164 }
165 else {
166 currentEnd -= (clipStart - start);
167 }
168 }
169
170 auto revStart = (clipStart >= start)? clipStart: start;
171 auto revEnd = (clipEnd >= end)? end: clipEnd;
172 auto revLen = revEnd - revStart;
173 if (revEnd >= revStart) {
174 if(!ProcessOneClip(count, track, revStart, revLen, start, end)) // reverse the clip
175 {
176 rValue = false;
177 break;
178 }
179
180 auto clipOffsetStart = currentEnd - (clipEnd - clipStart); // calculate the offset required
181 double offsetStartTime = track->LongSamplesToTime(clipOffsetStart);
182 if(i+1 < clipArray.size()) // update currentEnd if there is a clip to process next
183 {
184 auto nextClipStart = clipArray[i+1]->GetPlayStartSample();
185 currentEnd = currentEnd - (clipEnd - clipStart) - (nextClipStart - clipEnd);
186 }
187
188 revClips.push_back(track->RemoveAndReturnClip(clip)); // detach the clip from track
189 revClips.back()->SetPlayStartTime(track->LongSamplesToTime(track->TimeToLongSamples(offsetStartTime))); // align time to a sample and set offset
190 }
191 }
192 else if (clipStart >= end) { // clip is after the selection region
193 otherClips.push_back(track->RemoveAndReturnClip(clip)); // simply remove and append to otherClips
194 }
195 }
196
197 // STEP 3: Append the clips from
198 // revClips and otherClips back to the track
199 // the last clip of revClips is appended to the track first
200 // PRL: I don't think that matters, the sequence of storage of clips in the track
201 // is not elsewhere assumed to be by time
202 {
203 for (auto it = revClips.rbegin(), revEnd = revClips.rend(); rValue && it != revEnd; ++it)
204 rValue = track->AddClip(*it);
205 }
206
207 for (auto &clip : otherClips)
208 if (!(rValue = track->AddClip(clip)))
209 break;
210
211 return rValue;
212}
213
215 sampleCount start, sampleCount len,
216 sampleCount originalStart, sampleCount originalEnd)
217{
218 bool rc = true;
219 // keep track of two blocks whose data we will swap
220 auto first = start;
221
222 auto blockSize = track->GetMaxBlockSize();
223 float tmp;
224 Floats buffer1{ blockSize };
225 Floats buffer2{ blockSize };
226
227 auto originalLen = originalEnd - originalStart;
228
229 while (len > 1) {
230 auto block =
231 limitSampleBufferSize( track->GetBestBlockSize(first), len / 2 );
232 auto second = first + (len - block);
233
234 track->GetFloats(buffer1.get(), first, block);
235 track->GetFloats(buffer2.get(), second, block);
236 for (decltype(block) i = 0; i < block; i++) {
237 tmp = buffer1[i];
238 buffer1[i] = buffer2[block-i-1];
239 buffer2[block-i-1] = tmp;
240 }
241 // Don't dither on later rendering if only reversing samples
242 track->Set((samplePtr)buffer1.get(), floatSample, first, block,
244 track->Set((samplePtr)buffer2.get(), floatSample, second, block,
246
247 len -= 2 * block;
248 first += block;
249
250 if( TrackProgress(count, 2 * ( first - originalStart ).as_double() /
251 originalLen.as_double() ) ) {
252 rc = false;
253 break;
254 }
255 }
256
257 return rc;
258}
EffectType
@ EffectTypeProcess
XO("Cut/Copy/Paste")
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:22
@ narrowestSampleFormat
Two synonyms for previous values that might change if more values were added.
char * samplePtr
Definition: SampleFormat.h:55
std::vector< WaveClipHolder > WaveClipHolders
Definition: WaveClip.h:42
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
double mT1
Definition: EffectBase.h:109
std::shared_ptr< TrackList > mOutputTracks
Definition: EffectBase.h:107
double mT0
Definition: EffectBase.h:108
void ReplaceProcessedTracks(const bool bGoodResult)
Definition: EffectBase.cpp:241
void CopyInputTracks(bool allSyncLockSelected=false)
Definition: Effect.cpp:677
bool TrackProgress(int whichTrack, double frac, const TranslatableString &={}) const
Definition: Effect.cpp:627
Performs effect computation.
static const ComponentInterfaceSymbol Symbol
Definition: Reverse.h:21
ComponentInterfaceSymbol GetSymbol() const override
Definition: Reverse.cpp:47
EffectType GetType() const override
Type determines how it behaves.
Definition: Reverse.cpp:59
bool ProcessOneClip(int count, WaveTrack *track, sampleCount start, sampleCount len, sampleCount originalStart, sampleCount originalEnd)
Definition: Reverse.cpp:214
TranslatableString GetDescription() const override
Definition: Reverse.cpp:52
virtual ~EffectReverse()
Definition: Reverse.cpp:41
bool Process(EffectInstance &instance, EffectSettings &settings) override
Definition: Reverse.cpp:71
bool IsInteractive() const override
Whether the effect needs a dialog for entry of settings.
Definition: Reverse.cpp:64
bool ProcessOneWave(int count, WaveTrack *track, sampleCount start, sampleCount len)
Definition: Reverse.cpp:102
A LabelTrack is a Track that holds labels (LabelStruct).
Definition: LabelTrack.h:87
bool GetFloats(float *buffer, sampleCount start, size_t len, fillFormat fill=fillZero, bool mayThrow=true, sampleCount *pNumWithinClips=nullptr) const
Retrieve samples from a track in floating-point format, regardless of the storage format.
Definition: SampleTrack.h:71
double LongSamplesToTime(sampleCount pos) const
Convert correctly between a number of samples and an (absolute) time in seconds.
Definition: SampleTrack.cpp:47
sampleCount TimeToLongSamples(double t0) const
Convert correctly between an (absolute) time in seconds and a number of samples.
Definition: SampleTrack.cpp:42
static bool IsSelectedOrSyncLockSelected(const Track *pTrack)
Definition: SyncLock.cpp:73
Holds a msgid for the translation catalog; may also bind format arguments.
This allows multiple clips to be a part of one WaveTrack.
Definition: WaveClip.h:101
sampleCount GetPlayEndSample() const
Definition: WaveClip.cpp:893
sampleCount GetPlayStartSample() const
Definition: WaveClip.cpp:888
A Track that contains audio waveform data.
Definition: WaveTrack.h:57
void SplitAt(double t)
Definition: WaveTrack.cpp:2522
bool AddClip(const std::shared_ptr< WaveClip > &clip)
Append a clip to the track; which must have the same block factory as this track; return success.
Definition: WaveTrack.cpp:1214
size_t GetMaxBlockSize() const override
This returns a nonnegative number of samples meant to size a memory buffer.
Definition: WaveTrack.cpp:1800
size_t GetBestBlockSize(sampleCount t) const override
This returns a nonnegative number of samples meant to size a memory buffer.
Definition: WaveTrack.cpp:1782
WaveClipPointers SortedClipArray()
Definition: WaveTrack.cpp:2720
std::shared_ptr< WaveClip > RemoveAndReturnClip(WaveClip *clip)
Definition: WaveTrack.cpp:1201
WaveClipHolders & GetClips()
Definition: WaveTrack.h:338
void Set(constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len, sampleFormat effectiveFormat=widestSampleFormat)
Definition: WaveTrack.cpp:2191
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
BuiltinEffectsModule::Registration< EffectReverse > reg
Definition: Reverse.cpp:35
Externalized state of a plug-in.