2010-01-23 19:44:49 +00:00
|
|
|
/**********************************************************************
|
|
|
|
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
|
|
|
|
TimeTrack.cpp
|
|
|
|
|
|
|
|
Dr William Bland
|
|
|
|
|
|
|
|
*******************************************************************//*!
|
|
|
|
|
|
|
|
\class TimeTrack
|
|
|
|
\brief A kind of Track used to 'warp time'
|
|
|
|
|
|
|
|
*//*******************************************************************/
|
|
|
|
|
2021-05-09 15:16:56 +00:00
|
|
|
|
2015-06-09 14:45:14 +00:00
|
|
|
#include "TimeTrack.h"
|
2018-11-11 02:40:37 +00:00
|
|
|
|
2020-06-19 19:43:09 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2016-09-19 14:38:42 +00:00
|
|
|
#include <cfloat>
|
2019-03-28 17:23:26 +00:00
|
|
|
#include <wx/wxcrtvararg.h>
|
2018-11-14 22:05:30 +00:00
|
|
|
#include <wx/dc.h>
|
2010-01-23 19:44:49 +00:00
|
|
|
#include <wx/intl.h>
|
|
|
|
#include "widgets/Ruler.h"
|
2015-07-05 15:25:32 +00:00
|
|
|
#include "Envelope.h"
|
2017-05-03 12:48:24 +00:00
|
|
|
#include "Project.h"
|
2019-05-29 15:31:40 +00:00
|
|
|
#include "ProjectSettings.h"
|
2019-04-29 18:35:52 +00:00
|
|
|
#include "ProjectFileIORegistry.h"
|
2020-08-22 23:44:49 +00:00
|
|
|
#include "ViewInfo.h"
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2020-01-15 00:58:38 +00:00
|
|
|
#include "tracks/ui/TrackView.h"
|
|
|
|
#include "tracks/ui/TrackControls.h"
|
|
|
|
|
|
|
|
|
2013-01-06 16:55:19 +00:00
|
|
|
//TODO-MB: are these sensible values?
|
2013-12-18 22:45:14 +00:00
|
|
|
#define TIMETRACK_MIN 0.01
|
2013-01-06 16:55:19 +00:00
|
|
|
#define TIMETRACK_MAX 10.0
|
|
|
|
|
2019-04-29 18:35:52 +00:00
|
|
|
static ProjectFileIORegistry::Entry registerFactory{
|
|
|
|
wxT( "timetrack" ),
|
|
|
|
[]( AudacityProject &project ){
|
2019-05-06 23:00:10 +00:00
|
|
|
auto &tracks = TrackList::Get( project );
|
2020-08-22 23:44:49 +00:00
|
|
|
auto &viewInfo = ViewInfo::Get( project );
|
|
|
|
auto result = tracks.Add(std::make_shared<TimeTrack>(&viewInfo));
|
2020-01-15 00:58:38 +00:00
|
|
|
TrackView::Get( *result );
|
|
|
|
TrackControls::Get( *result );
|
|
|
|
return result;
|
2019-04-29 18:35:52 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-07-02 16:42:25 +00:00
|
|
|
TimeTrack::TimeTrack(const ZoomInfo *zoomInfo):
|
|
|
|
Track()
|
2016-01-26 22:37:16 +00:00
|
|
|
, mZoomInfo(zoomInfo)
|
2020-11-09 00:24:20 +00:00
|
|
|
{
|
|
|
|
CleanState();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TimeTrack::CleanState()
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2019-06-06 13:21:01 +00:00
|
|
|
mEnvelope = std::make_unique<BoundedEnvelope>(true, TIMETRACK_MIN, TIMETRACK_MAX, 1.0);
|
|
|
|
|
2019-05-24 15:23:54 +00:00
|
|
|
SetRangeLower( 0.9 );
|
|
|
|
SetRangeUpper( 1.1 );
|
2012-12-19 21:49:25 +00:00
|
|
|
mDisplayLog = false;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2016-09-19 14:38:42 +00:00
|
|
|
mEnvelope->SetTrackLen(DBL_MAX);
|
2012-12-19 21:49:25 +00:00
|
|
|
mEnvelope->SetOffset(0);
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
SetDefaultName(_("Time Track"));
|
|
|
|
SetName(GetDefaultName());
|
|
|
|
|
2016-04-08 05:56:06 +00:00
|
|
|
mRuler = std::make_unique<Ruler>();
|
2016-01-26 22:37:16 +00:00
|
|
|
mRuler->SetUseZoomInfo(0, mZoomInfo);
|
2010-01-23 19:44:49 +00:00
|
|
|
mRuler->SetLabelEdges(false);
|
|
|
|
mRuler->SetFormat(Ruler::TimeFormat);
|
|
|
|
}
|
|
|
|
|
2017-03-31 18:54:55 +00:00
|
|
|
TimeTrack::TimeTrack(const TimeTrack &orig, double *pT0, double *pT1)
|
|
|
|
: Track(orig)
|
2016-01-26 22:37:16 +00:00
|
|
|
, mZoomInfo(orig.mZoomInfo)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2012-12-31 13:13:58 +00:00
|
|
|
Init(orig); // this copies the TimeTrack metadata (name, range, etc)
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-05-27 22:56:20 +00:00
|
|
|
auto len = DBL_MAX;
|
|
|
|
if (pT0 && pT1) {
|
|
|
|
len = *pT1 - *pT0;
|
2019-05-24 15:23:54 +00:00
|
|
|
mEnvelope = std::make_unique<BoundedEnvelope>( *orig.mEnvelope, *pT0, *pT1 );
|
2017-05-27 22:56:20 +00:00
|
|
|
}
|
2017-05-03 00:55:40 +00:00
|
|
|
else
|
2019-05-24 15:23:54 +00:00
|
|
|
mEnvelope = std::make_unique<BoundedEnvelope>( *orig.mEnvelope );
|
2019-06-06 13:21:01 +00:00
|
|
|
|
|
|
|
SetRangeLower(orig.GetRangeLower());
|
|
|
|
SetRangeUpper(orig.GetRangeUpper());
|
|
|
|
|
2017-05-27 22:56:20 +00:00
|
|
|
mEnvelope->SetTrackLen( len );
|
2010-01-23 19:44:49 +00:00
|
|
|
mEnvelope->SetOffset(0);
|
|
|
|
|
2012-12-31 13:13:58 +00:00
|
|
|
///@TODO: Give Ruler:: a copy-constructor instead of this?
|
2016-04-08 05:56:06 +00:00
|
|
|
mRuler = std::make_unique<Ruler>();
|
2016-01-26 22:37:16 +00:00
|
|
|
mRuler->SetUseZoomInfo(0, mZoomInfo);
|
2010-01-23 19:44:49 +00:00
|
|
|
mRuler->SetLabelEdges(false);
|
|
|
|
mRuler->SetFormat(Ruler::TimeFormat);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy the track metadata but not the contents.
|
|
|
|
void TimeTrack::Init(const TimeTrack &orig)
|
|
|
|
{
|
|
|
|
Track::Init(orig);
|
|
|
|
SetDefaultName(orig.GetDefaultName());
|
|
|
|
SetName(orig.GetName());
|
2012-12-19 21:49:25 +00:00
|
|
|
SetDisplayLog(orig.GetDisplayLog());
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TimeTrack::~TimeTrack()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-05-24 15:23:54 +00:00
|
|
|
double TimeTrack::GetRangeLower() const
|
|
|
|
{
|
|
|
|
return mEnvelope->GetRangeLower();
|
|
|
|
}
|
|
|
|
|
|
|
|
double TimeTrack::GetRangeUpper() const
|
|
|
|
{
|
|
|
|
return mEnvelope->GetRangeUpper();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TimeTrack::SetRangeLower(double lower)
|
|
|
|
{
|
|
|
|
mEnvelope->SetRangeLower( lower );
|
|
|
|
}
|
|
|
|
|
|
|
|
void TimeTrack::SetRangeUpper(double upper)
|
|
|
|
{
|
|
|
|
mEnvelope->SetRangeUpper( upper );
|
|
|
|
}
|
|
|
|
|
2020-09-29 16:21:20 +00:00
|
|
|
bool TimeTrack::SupportsBasicEditing() const
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-01-29 05:25:33 +00:00
|
|
|
Track::Holder TimeTrack::PasteInto( AudacityProject &project ) const
|
|
|
|
{
|
|
|
|
// Maintain uniqueness of the time track!
|
|
|
|
std::shared_ptr<TimeTrack> pNewTrack;
|
|
|
|
if( auto pTrack = *TrackList::Get( project ).Any<TimeTrack>().begin() )
|
|
|
|
pNewTrack = pTrack->SharedPointer<TimeTrack>();
|
|
|
|
else
|
|
|
|
pNewTrack = std::make_shared<TimeTrack>( &ViewInfo::Get( project ) );
|
2020-11-09 00:24:20 +00:00
|
|
|
|
|
|
|
// Should come here only for .aup3 import, not for paste (because the
|
|
|
|
// track is skipped in cut/copy commands)
|
|
|
|
// And for import we agree to replace the track contents completely
|
|
|
|
pNewTrack->CleanState();
|
|
|
|
pNewTrack->Init(*this);
|
2021-01-29 05:25:33 +00:00
|
|
|
pNewTrack->Paste(0.0, this);
|
2020-11-09 00:24:20 +00:00
|
|
|
pNewTrack->SetRangeLower(this->GetRangeLower());
|
|
|
|
pNewTrack->SetRangeUpper(this->GetRangeUpper());
|
2021-01-29 05:25:33 +00:00
|
|
|
return pNewTrack;
|
|
|
|
}
|
|
|
|
|
2017-03-31 18:54:55 +00:00
|
|
|
Track::Holder TimeTrack::Cut( double t0, double t1 )
|
|
|
|
{
|
|
|
|
auto result = Copy( t0, t1, false );
|
|
|
|
Clear( t0, t1 );
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
Track::Holder TimeTrack::Copy( double t0, double t1, bool ) const
|
|
|
|
{
|
2018-11-19 04:07:05 +00:00
|
|
|
return std::make_shared<TimeTrack>( *this, &t0, &t1 );
|
2017-03-31 18:54:55 +00:00
|
|
|
}
|
|
|
|
|
2017-03-23 15:10:14 +00:00
|
|
|
void TimeTrack::Clear(double t0, double t1)
|
2017-03-31 18:54:55 +00:00
|
|
|
{
|
2019-05-27 14:17:16 +00:00
|
|
|
auto sampleTime = 1.0 / ProjectSettings::Get( *GetActiveProject() ).GetRate();
|
2017-05-03 12:48:24 +00:00
|
|
|
mEnvelope->CollapseRegion( t0, t1, sampleTime );
|
2017-03-31 18:54:55 +00:00
|
|
|
}
|
|
|
|
|
2017-03-23 15:10:14 +00:00
|
|
|
void TimeTrack::Paste(double t, const Track * src)
|
2017-03-31 18:54:55 +00:00
|
|
|
{
|
2017-04-11 22:16:03 +00:00
|
|
|
bool bOk = src && src->TypeSwitch< bool >( [&] (const TimeTrack *tt) {
|
2019-05-27 14:17:16 +00:00
|
|
|
auto sampleTime = 1.0 / ProjectSettings::Get( *GetActiveProject() ).GetRate();
|
2017-04-11 22:16:03 +00:00
|
|
|
mEnvelope->PasteEnvelope
|
|
|
|
(t, tt->mEnvelope.get(), sampleTime);
|
|
|
|
return true;
|
|
|
|
} );
|
|
|
|
|
|
|
|
if (! bOk )
|
|
|
|
// THROW_INCONSISTENCY_EXCEPTION // ?
|
2018-10-10 18:09:10 +00:00
|
|
|
(void)0;// intentionally do nothing.
|
2017-03-31 18:54:55 +00:00
|
|
|
}
|
|
|
|
|
2017-12-08 11:26:09 +00:00
|
|
|
void TimeTrack::Silence(double WXUNUSED(t0), double WXUNUSED(t1))
|
2017-03-31 18:54:55 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-03-23 15:10:14 +00:00
|
|
|
void TimeTrack::InsertSilence(double t, double len)
|
2017-03-31 18:54:55 +00:00
|
|
|
{
|
|
|
|
mEnvelope->InsertSpace(t, len);
|
|
|
|
}
|
|
|
|
|
2018-11-19 01:15:26 +00:00
|
|
|
Track::Holder TimeTrack::Clone() const
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2018-11-19 04:07:05 +00:00
|
|
|
return std::make_shared<TimeTrack>(*this);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2012-12-19 21:49:25 +00:00
|
|
|
bool TimeTrack::GetInterpolateLog() const
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2017-05-03 00:58:18 +00:00
|
|
|
return mEnvelope->GetExponential();
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2012-12-19 21:49:25 +00:00
|
|
|
void TimeTrack::SetInterpolateLog(bool interpolateLog) {
|
2017-05-03 00:58:18 +00:00
|
|
|
mEnvelope->SetExponential(interpolateLog);
|
2012-12-19 21:49:25 +00:00
|
|
|
}
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
bool TimeTrack::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
|
|
|
|
{
|
|
|
|
if (!wxStrcmp(tag, wxT("timetrack"))) {
|
2012-12-19 21:49:25 +00:00
|
|
|
mRescaleXMLValues = true; // will be set to false if upper/lower is found
|
2010-01-23 19:44:49 +00:00
|
|
|
long nValue;
|
|
|
|
while(*attrs) {
|
|
|
|
const wxChar *attr = *attrs++;
|
|
|
|
const wxChar *value = *attrs++;
|
2014-06-03 20:30:19 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
if (!value)
|
|
|
|
break;
|
2014-06-03 20:30:19 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
const wxString strValue = value;
|
2018-11-20 19:37:19 +00:00
|
|
|
if (this->Track::HandleCommonXMLAttribute(attr, strValue))
|
|
|
|
;
|
2012-12-19 21:49:25 +00:00
|
|
|
else if (!wxStrcmp(attr, wxT("rangelower")))
|
|
|
|
{
|
2019-05-24 15:23:54 +00:00
|
|
|
SetRangeLower( Internat::CompatibleToDouble(value) );
|
2012-12-19 21:49:25 +00:00
|
|
|
mRescaleXMLValues = false;
|
|
|
|
}
|
|
|
|
else if (!wxStrcmp(attr, wxT("rangeupper")))
|
|
|
|
{
|
2019-05-24 15:23:54 +00:00
|
|
|
SetRangeUpper( Internat::CompatibleToDouble(value) );
|
2013-01-06 16:55:19 +00:00
|
|
|
mRescaleXMLValues = false;
|
2012-12-19 21:49:25 +00:00
|
|
|
}
|
2014-06-03 20:30:19 +00:00
|
|
|
else if (!wxStrcmp(attr, wxT("displaylog")) &&
|
2012-12-19 21:49:25 +00:00
|
|
|
XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
|
|
|
|
{
|
|
|
|
SetDisplayLog(nValue != 0);
|
|
|
|
//TODO-MB: This causes a graphical glitch, TrackPanel should probably be Refresh()ed after loading.
|
|
|
|
// I don't know where to do this though.
|
|
|
|
}
|
2014-06-03 20:30:19 +00:00
|
|
|
else if (!wxStrcmp(attr, wxT("interpolatelog")) &&
|
2012-12-19 21:49:25 +00:00
|
|
|
XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
|
|
|
|
{
|
|
|
|
SetInterpolateLog(nValue != 0);
|
|
|
|
}
|
2014-06-03 20:30:19 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
} // while
|
2013-01-06 16:55:19 +00:00
|
|
|
if(mRescaleXMLValues)
|
|
|
|
mEnvelope->SetRange(0.0, 1.0); // this will be restored to the actual range later
|
2010-01-23 19:44:49 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-08-25 21:51:26 +00:00
|
|
|
void TimeTrack::HandleXMLEndTag(const wxChar * WXUNUSED(tag))
|
2013-01-06 16:55:19 +00:00
|
|
|
{
|
|
|
|
if(mRescaleXMLValues)
|
|
|
|
{
|
|
|
|
mRescaleXMLValues = false;
|
2019-05-24 15:23:54 +00:00
|
|
|
mEnvelope->RescaleValues(GetRangeLower(), GetRangeUpper());
|
2013-01-06 16:55:19 +00:00
|
|
|
mEnvelope->SetRange(TIMETRACK_MIN, TIMETRACK_MAX);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
XMLTagHandler *TimeTrack::HandleXMLChild(const wxChar *tag)
|
|
|
|
{
|
|
|
|
if (!wxStrcmp(tag, wxT("envelope")))
|
2016-04-08 05:56:06 +00:00
|
|
|
return mEnvelope.get();
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-02-22 19:23:35 +00:00
|
|
|
void TimeTrack::WriteXML(XMLWriter &xmlFile) const
|
2016-12-01 22:03:40 +00:00
|
|
|
// may throw
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
xmlFile.StartTag(wxT("timetrack"));
|
2018-11-20 19:37:19 +00:00
|
|
|
this->Track::WriteCommonXMLAttributes( xmlFile );
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2012-12-19 21:49:25 +00:00
|
|
|
//xmlFile.WriteAttr(wxT("channel"), mChannel);
|
|
|
|
//xmlFile.WriteAttr(wxT("offset"), mOffset, 8);
|
2019-05-24 15:23:54 +00:00
|
|
|
xmlFile.WriteAttr(wxT("rangelower"), GetRangeLower(), 12);
|
|
|
|
xmlFile.WriteAttr(wxT("rangeupper"), GetRangeUpper(), 12);
|
2012-12-19 21:49:25 +00:00
|
|
|
xmlFile.WriteAttr(wxT("displaylog"), GetDisplayLog());
|
|
|
|
xmlFile.WriteAttr(wxT("interpolatelog"), GetInterpolateLog());
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
mEnvelope->WriteXML(xmlFile);
|
|
|
|
|
|
|
|
xmlFile.EndTag(wxT("timetrack"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void TimeTrack::testMe()
|
|
|
|
{
|
|
|
|
GetEnvelope()->Flatten(0.0);
|
2017-05-03 00:58:18 +00:00
|
|
|
GetEnvelope()->InsertOrReplace(0.0, 0.2);
|
|
|
|
GetEnvelope()->InsertOrReplace(5.0 - 0.001, 0.2);
|
|
|
|
GetEnvelope()->InsertOrReplace(5.0 + 0.001, 1.3);
|
|
|
|
GetEnvelope()->InsertOrReplace(10.0, 1.3);
|
2014-06-03 20:30:19 +00:00
|
|
|
|
2012-12-19 21:49:25 +00:00
|
|
|
double value1 = GetEnvelope()->Integral(2.0, 13.0);
|
|
|
|
double expected1 = (5.0 - 2.0) * 0.2 + (13.0 - 5.0) * 1.3;
|
|
|
|
double value2 = GetEnvelope()->IntegralOfInverse(2.0, 13.0);
|
|
|
|
double expected2 = (5.0 - 2.0) / 0.2 + (13.0 - 5.0) / 1.3;
|
|
|
|
if( fabs(value1 - expected1) > 0.01 )
|
|
|
|
{
|
2017-10-09 04:37:10 +00:00
|
|
|
wxPrintf( "TimeTrack: Integral failed! expected %f got %f\n", expected1, value1);
|
2012-12-19 21:49:25 +00:00
|
|
|
}
|
|
|
|
if( fabs(value2 - expected2) > 0.01 )
|
|
|
|
{
|
2017-10-09 04:37:10 +00:00
|
|
|
wxPrintf( "TimeTrack: IntegralOfInverse failed! expected %f got %f\n", expected2, value2);
|
2012-12-19 21:49:25 +00:00
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2012-12-19 21:49:25 +00:00
|
|
|
/*double reqt0 = 10.0 - .1;
|
2010-01-23 19:44:49 +00:00
|
|
|
double reqt1 = 10.0 + .1;
|
|
|
|
double t0 = warp( reqt0 );
|
|
|
|
double t1 = warp( reqt1 );
|
|
|
|
if( t0 > t1 )
|
|
|
|
{
|
2017-10-09 04:37:10 +00:00
|
|
|
wxPrintf( "TimeTrack: Warping reverses an interval! [%.2f,%.2f] -> [%.2f,%.2f]\n",
|
2013-02-20 23:42:58 +00:00
|
|
|
reqt0, reqt1,
|
|
|
|
t0, t1 );
|
2012-12-19 21:49:25 +00:00
|
|
|
}*/
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|