1 /**********************************************************************
2 
3   Audacity: A Digital Audio Editor
4 
5   Repair.cpp
6 
7   Dominic Mazzoni
8 
9 *******************************************************************//**
10 
11 \class EffectRepair
12 \brief Use the interpolation code to fill in damaged audio.
13 Damage can include pops, clicks, or clipping.  As long as the
14 damaged section is short and surrounded by lots of good audio,
15 it is usually quite successful.
16 
17 This was formerly the PopClickRemoval effect, but it was
18 renamed and focused on the smaller subproblem of repairing
19 the audio, rather than actually finding the clicks.
20 
21 *//*******************************************************************/
22 
23 
24 
25 #include "Repair.h"
26 
27 #include <math.h>
28 
29 #include <wx/intl.h>
30 
31 #include "InterpolateAudio.h"
32 #include "../WaveTrack.h"
33 #include "../widgets/AudacityMessageBox.h"
34 
35 #include "LoadEffects.h"
36 
37 const ComponentInterfaceSymbol EffectRepair::Symbol
38 { XO("Repair") };
39 
40 namespace{ BuiltinEffectsModule::Registration< EffectRepair > reg; }
41 
EffectRepair()42 EffectRepair::EffectRepair()
43 {
44 }
45 
~EffectRepair()46 EffectRepair::~EffectRepair()
47 {
48 }
49 
50 // ComponentInterface implementation
51 
GetSymbol()52 ComponentInterfaceSymbol EffectRepair::GetSymbol()
53 {
54    return Symbol;
55 }
56 
GetDescription()57 TranslatableString EffectRepair::GetDescription()
58 {
59    return XO("Sets the peak amplitude of a one or more tracks");
60 }
61 
62 // EffectDefinitionInterface implementation
63 
GetType()64 EffectType EffectRepair::GetType()
65 {
66    return EffectTypeProcess;
67 }
68 
IsInteractive()69 bool EffectRepair::IsInteractive()
70 {
71    return false;
72 }
73 
74 // Effect implementation
75 
Process()76 bool EffectRepair::Process()
77 {
78    //v This may be too much copying for EffectRepair. To support Cancel, may be able to copy much less.
79    //  But for now, Cancel isn't supported without this.
80    this->CopyInputTracks(); // Set up mOutputTracks. //v This may be too much copying for EffectRepair.
81    bool bGoodResult = true;
82 
83    int count = 0;
84    for( auto track : mOutputTracks->Selected< WaveTrack >() ) {
85       const
86       double trackStart = track->GetStartTime();
87       const double repair_t0 = std::max(mT0, trackStart);
88       const
89       double trackEnd = track->GetEndTime();
90       const double repair_t1 = std::min(mT1, trackEnd);
91       const
92       double repair_deltat = repair_t1 - repair_t0;
93       if (repair_deltat > 0) {  // selection is within track audio
94          const auto repair0 = track->TimeToLongSamples(repair_t0);
95          const auto repair1 = track->TimeToLongSamples(repair_t1);
96          const auto repairLen = repair1 - repair0;
97          if (repairLen > 128) {
98             ::Effect::MessageBox(
99                XO(
100 "The Repair effect is intended to be used on very short sections of damaged audio (up to 128 samples).\n\nZoom in and select a tiny fraction of a second to repair.") );
101             bGoodResult = false;
102             break;
103          }
104 
105          const double rate = track->GetRate();
106          const double spacing = std::max(repair_deltat * 2, 128. / rate);
107          const double t0 = std::max(repair_t0 - spacing, trackStart);
108          const double t1 = std::min(repair_t1 + spacing, trackEnd);
109 
110          const auto s0 = track->TimeToLongSamples(t0);
111          const auto s1 = track->TimeToLongSamples(t1);
112          // The difference is at most 2 * 128:
113          const auto repairStart = (repair0 - s0).as_size_t();
114          const auto len = s1 - s0;
115 
116          if (s0 == repair0 && s1 == repair1) {
117             ::Effect::MessageBox(
118                XO(
119 "Repair works by using audio data outside the selection region.\n\nPlease select a region that has audio touching at least one side of it.\n\nThe more surrounding audio, the better it performs.") );
120    ///            The Repair effect needs some data to go on.\n\nPlease select an area to repair with some audio on at least one side (the more the better).") );
121             bGoodResult = false;
122             break;
123          }
124 
125          if (!ProcessOne(count, track, s0,
126                          // len is at most 5 * 128.
127                          len.as_size_t(),
128                          repairStart,
129                          // repairLen is at most 128.
130                          repairLen.as_size_t() )) {
131             bGoodResult = false;
132             break;
133          }
134       }
135 
136       count++;
137    }
138 
139    this->ReplaceProcessedTracks(bGoodResult);
140    return bGoodResult;
141 }
142 
ProcessOne(int count,WaveTrack * track,sampleCount start,size_t len,size_t repairStart,size_t repairLen)143 bool EffectRepair::ProcessOne(int count, WaveTrack * track,
144                               sampleCount start,
145                               size_t len,
146                               size_t repairStart, size_t repairLen)
147 {
148    Floats buffer{ len };
149    track->GetFloats(buffer.get(), start, len);
150    InterpolateAudio(buffer.get(), len, repairStart, repairLen);
151    track->Set((samplePtr)&buffer[repairStart], floatSample,
152               start + repairStart, repairLen);
153    return !TrackProgress(count, 1.0); // TrackProgress returns true on Cancel.
154 }
155