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