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