Audacity  2.3.1
TimeTrack.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  TimeTrack.cpp
6 
7  Dr William Bland
8 
9 *******************************************************************//*******************************************************************/
15 
16 #include "Audacity.h"
17 #include "TimeTrack.h"
18 #include "Experimental.h"
19 
20 #include <cfloat>
21 #include <wx/intl.h>
22 #include "AColor.h"
23 #include "widgets/Ruler.h"
24 #include "Envelope.h"
25 #include "Prefs.h"
26 #include "Project.h"
27 #include "Internat.h"
28 #include "ViewInfo.h"
29 #include "AllThemeResources.h"
30 
31 //TODO-MB: are these sensible values?
32 #define TIMETRACK_MIN 0.01
33 #define TIMETRACK_MAX 10.0
34 
35 std::unique_ptr<TimeTrack> TrackFactory::NewTimeTrack()
36 {
37  return std::make_unique<TimeTrack>(mDirManager, mZoomInfo);
38 }
39 
40 TimeTrack::TimeTrack(const std::shared_ptr<DirManager> &projDirManager, const ZoomInfo *zoomInfo):
41  Track(projDirManager)
42  , mZoomInfo(zoomInfo)
43 {
44  mHeight = 100;
45 
46  mRangeLower = 0.9;
47  mRangeUpper = 1.1;
48  mDisplayLog = false;
49 
50  mEnvelope = std::make_unique<Envelope>(true, TIMETRACK_MIN, TIMETRACK_MAX, 1.0);
51  mEnvelope->SetTrackLen(DBL_MAX);
52  mEnvelope->SetOffset(0);
53 
54  SetDefaultName(_("Time Track"));
56 
57  mRuler = std::make_unique<Ruler>();
58  mRuler->SetUseZoomInfo(0, mZoomInfo);
59  mRuler->SetLabelEdges(false);
60  mRuler->SetFormat(Ruler::TimeFormat);
61 }
62 
63 TimeTrack::TimeTrack(const TimeTrack &orig, double *pT0, double *pT1)
64  : Track(orig)
65  , mZoomInfo(orig.mZoomInfo)
66 {
67  Init(orig); // this copies the TimeTrack metadata (name, range, etc)
68 
69  auto len = DBL_MAX;
70  if (pT0 && pT1) {
71  len = *pT1 - *pT0;
72  mEnvelope = std::make_unique<Envelope>( *orig.mEnvelope, *pT0, *pT1 );
73  }
74  else
75  mEnvelope = std::make_unique<Envelope>( *orig.mEnvelope );
76  mEnvelope->SetTrackLen( len );
77  mEnvelope->SetOffset(0);
78 
80  mRuler = std::make_unique<Ruler>();
81  mRuler->SetUseZoomInfo(0, mZoomInfo);
82  mRuler->SetLabelEdges(false);
83  mRuler->SetFormat(Ruler::TimeFormat);
84 }
85 
86 // Copy the track metadata but not the contents.
87 void TimeTrack::Init(const TimeTrack &orig)
88 {
89  Track::Init(orig);
91  SetName(orig.GetName());
95 }
96 
98 {
99 }
100 
101 Track::Holder TimeTrack::Cut( double t0, double t1 )
102 {
103  auto result = Copy( t0, t1, false );
104  Clear( t0, t1 );
105  return result;
106 }
107 
108 Track::Holder TimeTrack::Copy( double t0, double t1, bool ) const
109 {
110  auto result = std::make_unique<TimeTrack>( *this, &t0, &t1 );
111  return Track::Holder{ std::move( result ) };
112 }
113 
114 void TimeTrack::Clear(double t0, double t1)
115 {
116  auto sampleTime = 1.0 / GetActiveProject()->GetRate();
117  mEnvelope->CollapseRegion( t0, t1, sampleTime );
118 }
119 
120 void TimeTrack::Paste(double t, const Track * src)
121 {
122  bool bOk = src && src->TypeSwitch< bool >( [&] (const TimeTrack *tt) {
123  auto sampleTime = 1.0 / GetActiveProject()->GetRate();
124  mEnvelope->PasteEnvelope
125  (t, tt->mEnvelope.get(), sampleTime);
126  return true;
127  } );
128 
129  if (! bOk )
130  // THROW_INCONSISTENCY_EXCEPTION // ?
131  (void)0;// intentionally do nothing.
132 }
133 
134 void TimeTrack::Silence(double WXUNUSED(t0), double WXUNUSED(t1))
135 {
136 }
137 
138 void TimeTrack::InsertSilence(double t, double len)
139 {
140  mEnvelope->InsertSpace(t, len);
141 }
142 
144 {
145  return std::make_unique<TimeTrack>(*this);
146 }
147 
149 {
150  return mEnvelope->GetExponential();
151 }
152 
153 void TimeTrack::SetInterpolateLog(bool interpolateLog) {
154  mEnvelope->SetExponential(interpolateLog);
155 }
156 
157 //Compute the (average) warp factor between two non-warped time points
158 double TimeTrack::ComputeWarpFactor(double t0, double t1) const
159 {
160  return GetEnvelope()->AverageOfInverse(t0, t1);
161 }
162 
163 double TimeTrack::ComputeWarpedLength(double t0, double t1) const
164 {
165  return GetEnvelope()->IntegralOfInverse(t0, t1);
166 }
167 
168 double TimeTrack::SolveWarpedLength(double t0, double length) const
169 {
170  return GetEnvelope()->SolveIntegralOfInverse(t0, length);
171 }
172 
173 bool TimeTrack::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
174 {
175  if (!wxStrcmp(tag, wxT("timetrack"))) {
176  mRescaleXMLValues = true; // will be set to false if upper/lower is found
177  long nValue;
178  while(*attrs) {
179  const wxChar *attr = *attrs++;
180  const wxChar *value = *attrs++;
181 
182  if (!value)
183  break;
184 
185  const wxString strValue = value;
186  if (!wxStrcmp(attr, wxT("name")) && XMLValueChecker::IsGoodString(strValue))
187  mName = strValue;
188  else if (!wxStrcmp(attr, wxT("height")) &&
189  XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
190  mHeight = nValue;
191  else if (!wxStrcmp(attr, wxT("minimized")) &&
192  XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
193  mMinimized = (nValue != 0);
194  else if (!wxStrcmp(attr, wxT("rangelower")))
195  {
197  mRescaleXMLValues = false;
198  }
199  else if (!wxStrcmp(attr, wxT("rangeupper")))
200  {
202  mRescaleXMLValues = false;
203  }
204  else if (!wxStrcmp(attr, wxT("displaylog")) &&
205  XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
206  {
207  SetDisplayLog(nValue != 0);
208  //TODO-MB: This causes a graphical glitch, TrackPanel should probably be Refresh()ed after loading.
209  // I don't know where to do this though.
210  }
211  else if (!wxStrcmp(attr, wxT("interpolatelog")) &&
212  XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
213  {
214  SetInterpolateLog(nValue != 0);
215  }
216 
217  } // while
219  mEnvelope->SetRange(0.0, 1.0); // this will be restored to the actual range later
220  return true;
221  }
222 
223  return false;
224 }
225 
226 void TimeTrack::HandleXMLEndTag(const wxChar * WXUNUSED(tag))
227 {
229  {
230  mRescaleXMLValues = false;
231  mEnvelope->RescaleValues(mRangeLower, mRangeUpper);
233  }
234 }
235 
237 {
238  if (!wxStrcmp(tag, wxT("envelope")))
239  return mEnvelope.get();
240 
241  return NULL;
242 }
243 
244 void TimeTrack::WriteXML(XMLWriter &xmlFile) const
245 // may throw
246 {
247  xmlFile.StartTag(wxT("timetrack"));
248 
249  xmlFile.WriteAttr(wxT("name"), mName);
250  //xmlFile.WriteAttr(wxT("channel"), mChannel);
251  //xmlFile.WriteAttr(wxT("offset"), mOffset, 8);
252  xmlFile.WriteAttr(wxT("height"), GetActualHeight());
253  xmlFile.WriteAttr(wxT("minimized"), GetMinimized());
254  xmlFile.WriteAttr(wxT("rangelower"), mRangeLower, 12);
255  xmlFile.WriteAttr(wxT("rangeupper"), mRangeUpper, 12);
256  xmlFile.WriteAttr(wxT("displaylog"), GetDisplayLog());
257  xmlFile.WriteAttr(wxT("interpolatelog"), GetInterpolateLog());
258 
259  mEnvelope->WriteXML(xmlFile);
260 
261  xmlFile.EndTag(wxT("timetrack"));
262 }
263 
266 
267 void TimeTrack::Draw
268 (TrackPanelDrawingContext &context, const wxRect & r, const ZoomInfo &zoomInfo) const
269 {
270  auto &dc = context.dc;
271  bool highlight = false;
272 #ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
273  auto target = dynamic_cast<EnvelopeHandle*>(context.target.get());
274  highlight = target && target->GetEnvelope() == this->GetEnvelope();
275 #endif
276 
277  double min = zoomInfo.PositionToTime(0);
278  double max = zoomInfo.PositionToTime(r.width);
279  if (min > max)
280  {
281  wxASSERT(false);
282  min = max;
283  }
284 
285  AColor::UseThemeColour( &dc, clrUnselected );
286  dc.DrawRectangle(r);
287 
288  //copy this rectangle away for future use.
289  wxRect mid = r;
290 
291  // Draw the Ruler
292  mRuler->SetBounds(r.x, r.y, r.x + r.width - 1, r.y + r.height - 1);
293  mRuler->SetRange(min, max);
294  mRuler->SetFlip(false); // If we don't do this, the Ruler doesn't redraw itself when the envelope is modified.
295  // I have no idea why!
296  //
297  // LL: It's because the ruler only Invalidate()s when the NEW value is different
298  // than the current value.
299  mRuler->SetFlip(GetHeight() > 75 ? true : true); // MB: so why don't we just call Invalidate()? :)
300  mRuler->SetTickColour( theTheme.Colour( clrTrackPanelText ));
301  mRuler->Draw(dc, this);
302 
303  Doubles envValues{ size_t(mid.width) };
304  GetEnvelope()->GetValues
305  ( 0, 0, envValues.get(), mid.width, 0, zoomInfo );
306 
307  wxPen &pen = highlight ? AColor::uglyPen : AColor::envelopePen;
308  dc.SetPen( pen );
309 
310  double logLower = log(std::max(1.0e-7, mRangeLower)), logUpper = log(std::max(1.0e-7, mRangeUpper));
311  for (int x = 0; x < mid.width; x++)
312  {
313  double y;
314  if(mDisplayLog)
315  y = (double)mid.height * (logUpper - log(envValues[x])) / (logUpper - logLower);
316  else
317  y = (double)mid.height * (mRangeUpper - envValues[x]) / (mRangeUpper - mRangeLower);
318  int thisy = r.y + (int)y;
319  AColor::Line(dc, mid.x + x, thisy - 1, mid.x + x, thisy+2);
320  }
321 }
322 
324 {
325  GetEnvelope()->Flatten(0.0);
326  GetEnvelope()->InsertOrReplace(0.0, 0.2);
327  GetEnvelope()->InsertOrReplace(5.0 - 0.001, 0.2);
328  GetEnvelope()->InsertOrReplace(5.0 + 0.001, 1.3);
329  GetEnvelope()->InsertOrReplace(10.0, 1.3);
330 
331  double value1 = GetEnvelope()->Integral(2.0, 13.0);
332  double expected1 = (5.0 - 2.0) * 0.2 + (13.0 - 5.0) * 1.3;
333  double value2 = GetEnvelope()->IntegralOfInverse(2.0, 13.0);
334  double expected2 = (5.0 - 2.0) / 0.2 + (13.0 - 5.0) / 1.3;
335  if( fabs(value1 - expected1) > 0.01 )
336  {
337  wxPrintf( "TimeTrack: Integral failed! expected %f got %f\n", expected1, value1);
338  }
339  if( fabs(value2 - expected2) > 0.01 )
340  {
341  wxPrintf( "TimeTrack: IntegralOfInverse failed! expected %f got %f\n", expected2, value2);
342  }
343 
344  /*double reqt0 = 10.0 - .1;
345  double reqt1 = 10.0 + .1;
346  double t0 = warp( reqt0 );
347  double t1 = warp( reqt1 );
348  if( t0 > t1 )
349  {
350  wxPrintf( "TimeTrack: Warping reverses an interval! [%.2f,%.2f] -> [%.2f,%.2f]\n",
351  reqt0, reqt1,
352  t0, t1 );
353  }*/
354 }
355 
bool GetDisplayLog() const
Definition: TimeTrack.h:130
void Init(const TimeTrack &orig)
Copy the metadata from another track but not the points.
Definition: TimeTrack.cpp:87
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:209
double ComputeWarpedLength(double t0, double t1) const
Compute the duration (in seconds at playback) of the specified region of the track.
Definition: TimeTrack.cpp:163
void SetRangeUpper(double upper)
Definition: TimeTrack.h:128
void Silence(double t0, double t1) override
Definition: TimeTrack.cpp:134
double AverageOfInverse(double t0, double t1) const
Definition: Envelope.cpp:1415
void Flatten(double value)
Definition: Envelope.cpp:137
R TypeSwitch(const Functions &...functions)
Definition: Track.h:623
#define TIMETRACK_MAX
Definition: TimeTrack.cpp:33
int mHeight
Definition: Track.h:204
void InsertSilence(double t, double len) override
Definition: TimeTrack.cpp:138
double GetRangeLower() const
Definition: TimeTrack.h:124
double PositionToTime(wxInt64 position, wxInt64 origin=0, bool ignoreFisheye=false) const
Definition: ViewInfo.cpp:49
double SolveIntegralOfInverse(double t0, double area) const
Definition: Envelope.cpp:1632
void SetDisplayLog(bool displayLog)
Definition: TimeTrack.h:131
const std::shared_ptr< DirManager > mDirManager
Definition: Track.h:1572
XMLTagHandler * HandleXMLChild(const wxChar *tag) override
Definition: TimeTrack.cpp:236
bool GetInterpolateLog() const
Definition: TimeTrack.cpp:148
bool mDisplayLog
Definition: TimeTrack.h:146
static wxPen uglyPen
Definition: AColor.h:147
static bool IsGoodInt(const wxString &strInt)
Check that the supplied string can be converted to a long (32bit) integer.
void Paste(double t, const Track *src) override
Definition: TimeTrack.cpp:120
wxString GetDefaultName() const
Definition: Track.h:371
const ZoomInfo *const mZoomInfo
Definition: TimeTrack.h:141
static wxPen envelopePen
Definition: AColor.h:122
double mRangeUpper
Definition: TimeTrack.h:145
void Draw(TrackPanelDrawingContext &context, const wxRect &r, const ZoomInfo &zoomInfo) const
Definition: TimeTrack.cpp:268
A kind of Track used to 'warp time'.
Definition: TimeTrack.h:29
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override
Definition: TimeTrack.cpp:173
std::unique_ptr< TimeTrack > NewTimeTrack()
Definition: TimeTrack.cpp:35
void Clear(double t0, double t1) override
Definition: TimeTrack.cpp:114
double GetRangeUpper() const
Definition: TimeTrack.h:125
void testMe()
Definition: TimeTrack.cpp:323
static bool IsGoodString(const wxString &str)
static bool CompatibleToDouble(const wxString &stringToConvert, double *result)
Convert a string to a number.
Definition: Internat.cpp:122
int InsertOrReplace(double when, double value)
Add a point at a particular absolute time coordinate.
Definition: Envelope.h:196
double IntegralOfInverse(double t0, double t1) const
Definition: Envelope.cpp:1569
bool mMinimized
Definition: Track.h:211
#define TIMETRACK_MIN
Definition: TimeTrack.cpp:32
Fundamental data object of Audacity, placed in the TrackPanel. Classes derived form it include the Wa...
Definition: Track.h:190
This class is an interface which should be implemented by classes which wish to be able to load and s...
Definition: XMLTagHandler.h:72
double mRangeLower
Definition: TimeTrack.h:144
int min(int a, int b)
wxString GetName() const
Definition: Track.h:369
TimeTrack(const std::shared_ptr< DirManager > &projDirManager, const ZoomInfo *zoomInfo)
Definition: TimeTrack.cpp:40
static void UseThemeColour(wxDC *dc, int iBrush, int iPen=-1)
Definition: AColor.cpp:289
std::unique_ptr< Envelope > mEnvelope
Definition: TimeTrack.h:142
Holder Cut(double t0, double t1) override
Definition: TimeTrack.cpp:101
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:122
void SetDefaultName(const wxString &n)
Definition: Track.h:372
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom")).Raw()), OnMoveTrack)#define SET_TRACK_NAME_PLUGIN_SYMBOLclass SetTrackNameCommand:public AudacityCommand
Holder Copy(double t0, double t1, bool forClipboard) const override
Definition: TimeTrack.cpp:108
void SetInterpolateLog(bool interpolateLog)
Definition: TimeTrack.cpp:153
void SetName(const wxString &n)
Definition: Track.h:370
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:312
Envelope * GetEnvelope() const
virtual ~TimeTrack()
Definition: TimeTrack.cpp:97
void Init(const Track &orig)
Definition: Track.cpp:78
std::unique_ptr< Ruler > mRuler
Definition: TimeTrack.h:143
Track::Holder Duplicate() const override
Definition: TimeTrack.cpp:143
const ZoomInfo *const mZoomInfo
Definition: Track.h:1573
double SolveWarpedLength(double t0, double length) const
Compute how much unwarped time must have elapsed if length seconds of warped time has elapsed...
Definition: TimeTrack.cpp:168
bool mRescaleXMLValues
Definition: TimeTrack.h:147
wxColour & Colour(int iIndex)
Definition: Theme.cpp:1225
double GetRate() const
Definition: Project.h:199
Envelope * GetEnvelope()
Definition: TimeTrack.h:87
std::unique_ptr< Track > Holder
Definition: Track.h:362
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:22
void SetRangeLower(double lower)
Definition: TimeTrack.h:127
double ComputeWarpFactor(double t0, double t1) const
Compute the integral warp factor between two non-warped time points.
Definition: TimeTrack.cpp:158
void HandleXMLEndTag(const wxChar *tag) override
Definition: TimeTrack.cpp:226
void WriteXML(XMLWriter &xmlFile) const override
Definition: TimeTrack.cpp:244
double Integral(double t0, double t1) const
Definition: Envelope.cpp:1506
wxString mName
Definition: Track.h:205