1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-29 06:38:38 +02:00
audacity/src/TimeTrack.cpp
Paul Licameli cbe4a46af2 Bug2265: Should save and restore track heights...
... Regression happened at 3f1fd8ced0fb5fe617ad2dd7808cd1ae2ca2c0f3

The heights were written to file, but not read back again.

This fix was done carefully to avoid making dependency cycles.  We don't want
Track to depend on TrackView or TrackControls.

It's not great that LabelTrack, NoteTrack, TimeTrack, and WaveTrack now have
those dependencies, but at least they don't make cycles.

It would be better to figure out how to attach the view and controls to the
track with ClientData, then just invoke BuildAll to repopulate the view and
controls, so that they are non-null when you reach
Track::HandleCommonXMLAttribute.
2020-01-14 20:04:32 -05:00

309 lines
8.1 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
TimeTrack.cpp
Dr William Bland
*******************************************************************//*!
\class TimeTrack
\brief A kind of Track used to 'warp time'
*//*******************************************************************/
#include "Audacity.h"
#include "TimeTrack.h"
#include "Experimental.h"
#include <cfloat>
#include <wx/wxcrtvararg.h>
#include <wx/dc.h>
#include <wx/intl.h>
#include "widgets/Ruler.h"
#include "Envelope.h"
#include "Project.h"
#include "ProjectSettings.h"
#include "ProjectFileIORegistry.h"
#include "tracks/ui/TrackView.h"
#include "tracks/ui/TrackControls.h"
//TODO-MB: are these sensible values?
#define TIMETRACK_MIN 0.01
#define TIMETRACK_MAX 10.0
std::shared_ptr<TimeTrack> TrackFactory::NewTimeTrack()
{
return std::make_shared<TimeTrack>(mDirManager, mZoomInfo);
}
static ProjectFileIORegistry::Entry registerFactory{
wxT( "timetrack" ),
[]( AudacityProject &project ){
auto &trackFactory = TrackFactory::Get( project );
auto &tracks = TrackList::Get( project );
auto result = tracks.Add(trackFactory.NewTimeTrack());
TrackView::Get( *result );
TrackControls::Get( *result );
return result;
}
};
TimeTrack::TimeTrack(const std::shared_ptr<DirManager> &projDirManager, const ZoomInfo *zoomInfo):
Track(projDirManager)
, mZoomInfo(zoomInfo)
{
mEnvelope = std::make_unique<BoundedEnvelope>(true, TIMETRACK_MIN, TIMETRACK_MAX, 1.0);
SetRangeLower( 0.9 );
SetRangeUpper( 1.1 );
mDisplayLog = false;
mEnvelope->SetTrackLen(DBL_MAX);
mEnvelope->SetOffset(0);
SetDefaultName(_("Time Track"));
SetName(GetDefaultName());
mRuler = std::make_unique<Ruler>();
mRuler->SetUseZoomInfo(0, mZoomInfo);
mRuler->SetLabelEdges(false);
mRuler->SetFormat(Ruler::TimeFormat);
}
TimeTrack::TimeTrack(const TimeTrack &orig, double *pT0, double *pT1)
: Track(orig)
, mZoomInfo(orig.mZoomInfo)
{
Init(orig); // this copies the TimeTrack metadata (name, range, etc)
auto len = DBL_MAX;
if (pT0 && pT1) {
len = *pT1 - *pT0;
mEnvelope = std::make_unique<BoundedEnvelope>( *orig.mEnvelope, *pT0, *pT1 );
}
else
mEnvelope = std::make_unique<BoundedEnvelope>( *orig.mEnvelope );
SetRangeLower(orig.GetRangeLower());
SetRangeUpper(orig.GetRangeUpper());
mEnvelope->SetTrackLen( len );
mEnvelope->SetOffset(0);
///@TODO: Give Ruler:: a copy-constructor instead of this?
mRuler = std::make_unique<Ruler>();
mRuler->SetUseZoomInfo(0, mZoomInfo);
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());
SetDisplayLog(orig.GetDisplayLog());
}
TimeTrack::~TimeTrack()
{
}
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 );
}
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
{
return std::make_shared<TimeTrack>( *this, &t0, &t1 );
}
void TimeTrack::Clear(double t0, double t1)
{
auto sampleTime = 1.0 / ProjectSettings::Get( *GetActiveProject() ).GetRate();
mEnvelope->CollapseRegion( t0, t1, sampleTime );
}
void TimeTrack::Paste(double t, const Track * src)
{
bool bOk = src && src->TypeSwitch< bool >( [&] (const TimeTrack *tt) {
auto sampleTime = 1.0 / ProjectSettings::Get( *GetActiveProject() ).GetRate();
mEnvelope->PasteEnvelope
(t, tt->mEnvelope.get(), sampleTime);
return true;
} );
if (! bOk )
// THROW_INCONSISTENCY_EXCEPTION // ?
(void)0;// intentionally do nothing.
}
void TimeTrack::Silence(double WXUNUSED(t0), double WXUNUSED(t1))
{
}
void TimeTrack::InsertSilence(double t, double len)
{
mEnvelope->InsertSpace(t, len);
}
Track::Holder TimeTrack::Clone() const
{
return std::make_shared<TimeTrack>(*this);
}
bool TimeTrack::GetInterpolateLog() const
{
return mEnvelope->GetExponential();
}
void TimeTrack::SetInterpolateLog(bool interpolateLog) {
mEnvelope->SetExponential(interpolateLog);
}
bool TimeTrack::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
{
if (!wxStrcmp(tag, wxT("timetrack"))) {
mRescaleXMLValues = true; // will be set to false if upper/lower is found
long nValue;
while(*attrs) {
const wxChar *attr = *attrs++;
const wxChar *value = *attrs++;
if (!value)
break;
const wxString strValue = value;
if (this->Track::HandleCommonXMLAttribute(attr, strValue))
;
else if (!wxStrcmp(attr, wxT("rangelower")))
{
SetRangeLower( Internat::CompatibleToDouble(value) );
mRescaleXMLValues = false;
}
else if (!wxStrcmp(attr, wxT("rangeupper")))
{
SetRangeUpper( Internat::CompatibleToDouble(value) );
mRescaleXMLValues = false;
}
else if (!wxStrcmp(attr, wxT("displaylog")) &&
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.
}
else if (!wxStrcmp(attr, wxT("interpolatelog")) &&
XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
{
SetInterpolateLog(nValue != 0);
}
} // while
if(mRescaleXMLValues)
mEnvelope->SetRange(0.0, 1.0); // this will be restored to the actual range later
return true;
}
return false;
}
void TimeTrack::HandleXMLEndTag(const wxChar * WXUNUSED(tag))
{
if(mRescaleXMLValues)
{
mRescaleXMLValues = false;
mEnvelope->RescaleValues(GetRangeLower(), GetRangeUpper());
mEnvelope->SetRange(TIMETRACK_MIN, TIMETRACK_MAX);
}
}
XMLTagHandler *TimeTrack::HandleXMLChild(const wxChar *tag)
{
if (!wxStrcmp(tag, wxT("envelope")))
return mEnvelope.get();
return NULL;
}
void TimeTrack::WriteXML(XMLWriter &xmlFile) const
// may throw
{
xmlFile.StartTag(wxT("timetrack"));
this->Track::WriteCommonXMLAttributes( xmlFile );
//xmlFile.WriteAttr(wxT("channel"), mChannel);
//xmlFile.WriteAttr(wxT("offset"), mOffset, 8);
xmlFile.WriteAttr(wxT("rangelower"), GetRangeLower(), 12);
xmlFile.WriteAttr(wxT("rangeupper"), GetRangeUpper(), 12);
xmlFile.WriteAttr(wxT("displaylog"), GetDisplayLog());
xmlFile.WriteAttr(wxT("interpolatelog"), GetInterpolateLog());
mEnvelope->WriteXML(xmlFile);
xmlFile.EndTag(wxT("timetrack"));
}
void TimeTrack::testMe()
{
GetEnvelope()->Flatten(0.0);
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);
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 )
{
wxPrintf( "TimeTrack: Integral failed! expected %f got %f\n", expected1, value1);
}
if( fabs(value2 - expected2) > 0.01 )
{
wxPrintf( "TimeTrack: IntegralOfInverse failed! expected %f got %f\n", expected2, value2);
}
/*double reqt0 = 10.0 - .1;
double reqt1 = 10.0 + .1;
double t0 = warp( reqt0 );
double t1 = warp( reqt1 );
if( t0 > t1 )
{
wxPrintf( "TimeTrack: Warping reverses an interval! [%.2f,%.2f] -> [%.2f,%.2f]\n",
reqt0, reqt1,
t0, t1 );
}*/
}