1
0
mirror of https://github.com/cookiengineer/audacity synced 2026-02-05 03:03:10 +01:00

commit a large patch by Maarten Baert maarten-baert<at>hotmail<dot>com to fix and improve time track support. Several fix-me issues remain but none are new with this patch.

This commit is contained in:
richardash1981
2012-12-19 21:49:25 +00:00
parent 97d967501a
commit 55be06c9e4
16 changed files with 617 additions and 406 deletions

View File

@@ -31,16 +31,19 @@ TimeTrack *TrackFactory::NewTimeTrack()
TimeTrack::TimeTrack(DirManager *projDirManager):
Track(projDirManager)
{
mHeight = 50;
mHeight = 100;
mRangeLower = 90;
mRangeUpper = 110;
mRangeLower = 0.9;
mRangeUpper = 1.1;
mDisplayLog = false;
mEnvelope = new Envelope();
mEnvelope->SetTrackLen(1000000000.0);
mEnvelope->SetInterpolateDB(false);
mEnvelope->Flatten(0.5);
mEnvelope->SetInterpolateDB(true);
mEnvelope->Flatten(1.0);
mEnvelope->Mirror(false);
mEnvelope->SetOffset(0);
SetDefaultName(_("Time Track"));
SetName(GetDefaultName());
@@ -57,15 +60,10 @@ TimeTrack::TimeTrack(TimeTrack &orig):
{
Init(orig);
mHeight = 50;
mRangeLower = 90;
mRangeUpper = 110;
mEnvelope = new Envelope();
mEnvelope->SetTrackLen(1000000000.0);
mEnvelope->SetInterpolateDB(false);
mEnvelope->Flatten(0.5);
SetInterpolateLog(orig.GetInterpolateLog()); // this calls Envelope::SetInterpolateDB
mEnvelope->Flatten(1.0);
mEnvelope->Mirror(false);
mEnvelope->Paste(0.0, orig.mEnvelope);
mEnvelope->SetOffset(0);
@@ -84,6 +82,9 @@ void TimeTrack::Init(const TimeTrack &orig)
Track::Init(orig);
SetDefaultName(orig.GetDefaultName());
SetName(orig.GetName());
SetRangeLower(orig.GetRangeLower());
SetRangeUpper(orig.GetRangeUpper());
SetDisplayLog(orig.GetDisplayLog());
}
TimeTrack::~TimeTrack()
@@ -99,35 +100,35 @@ Track *TimeTrack::Duplicate()
return new TimeTrack(*this);
}
// Our envelope represents the playback speed, which is the rate of change of
// playback position. We want to find the playback position at time t, so
// we have to integrate the playback speed.
double TimeTrack::warp( double t )
bool TimeTrack::GetInterpolateLog() const
{
double result = GetEnvelope()->Integral( 0.0, t,
GetRangeLower()/100.0,
GetRangeUpper()/100.0 );
//printf( "Warping %.2f to %.2f\n", t, result );
return result;
return mEnvelope->GetInterpolateDB();
}
//Compute the integral warp factor between two non-warped time points
void TimeTrack::SetInterpolateLog(bool interpolateLog) {
mEnvelope->SetInterpolateDB(interpolateLog);
}
//Compute the (average) warp factor between two non-warped time points
double TimeTrack::ComputeWarpFactor(double t0, double t1)
{
double factor;
factor = GetEnvelope()->Average(t0, t1);
factor = (GetRangeLower() *
(1 - factor) +
factor *
GetRangeUpper()) /
100.0;
return factor;
return GetEnvelope()->AverageOfInverse(t0, t1);
}
double TimeTrack::ComputeWarpedLength(double t0, double t1)
{
return GetEnvelope()->IntegralOfInverse(t0, t1);
}
double TimeTrack::SolveWarpedLength(double t0, double length)
{
return GetEnvelope()->SolveIntegralOfInverse(t0, length);
}
bool TimeTrack::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
{
if (!wxStrcmp(tag, wxT("timetrack"))) {
double dblValue;
mRescaleXMLValues = true; // will be set to false if upper/lower is found
long nValue;
while(*attrs) {
const wxChar *attr = *attrs++;
@@ -137,29 +138,36 @@ bool TimeTrack::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
break;
const wxString strValue = value;
if (!wxStrcmp(attr, wxT("offset")))
{
if (!XMLValueChecker::IsGoodString(strValue) ||
!Internat::CompatibleToDouble(strValue, &dblValue))
return false;
mOffset = dblValue;
mEnvelope->SetOffset(mOffset);
}
else if (!wxStrcmp(attr, wxT("name")) && XMLValueChecker::IsGoodString(strValue))
if (!wxStrcmp(attr, wxT("name")) && XMLValueChecker::IsGoodString(strValue))
mName = strValue;
else if (!wxStrcmp(attr, wxT("channel")))
{
if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&nValue) ||
!XMLValueChecker::IsValidChannel(nValue))
return false;
mChannel = nValue;
}
else if (!wxStrcmp(attr, wxT("height")) &&
XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
mHeight = nValue;
else if (!wxStrcmp(attr, wxT("minimized")) &&
XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
mMinimized = (nValue != 0);
else if (!wxStrcmp(attr, wxT("rangelower")))
{
mRangeLower = Internat::CompatibleToDouble(value);
mRescaleXMLValues = false;
}
else if (!wxStrcmp(attr, wxT("rangeupper")))
{
mRangeUpper = Internat::CompatibleToDouble(value);
mRescaleXMLValues = false; //TODO-MB: figure out how to rescale after loading
}
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
return true;
@@ -181,10 +189,14 @@ void TimeTrack::WriteXML(XMLWriter &xmlFile)
xmlFile.StartTag(wxT("timetrack"));
xmlFile.WriteAttr(wxT("name"), mName);
xmlFile.WriteAttr(wxT("channel"), mChannel);
xmlFile.WriteAttr(wxT("offset"), mOffset, 8);
xmlFile.WriteAttr(wxT("height"), this->GetActualHeight());
xmlFile.WriteAttr(wxT("minimized"), this->GetMinimized());
//xmlFile.WriteAttr(wxT("channel"), mChannel);
//xmlFile.WriteAttr(wxT("offset"), mOffset, 8);
xmlFile.WriteAttr(wxT("height"), GetActualHeight());
xmlFile.WriteAttr(wxT("minimized"), GetMinimized());
xmlFile.WriteAttr(wxT("rangelower"), mRangeLower, 12);
xmlFile.WriteAttr(wxT("rangeupper"), mRangeUpper, 12);
xmlFile.WriteAttr(wxT("displaylog"), GetDisplayLog());
xmlFile.WriteAttr(wxT("interpolatelog"), GetInterpolateLog());
mEnvelope->WriteXML(xmlFile);
@@ -222,31 +234,26 @@ void TimeTrack::Draw(wxDC & dc, const wxRect & r, double h, double pps)
//
// LL: It's because the ruler only Invalidate()s when the new value is different
// than the current value.
mRuler->SetFlip(GetHeight() > 75 ? true : true);
mRuler->SetFlip(GetHeight() > 75 ? true : true); // MB: so why don't we just call Invalidate()? :)
mRuler->Draw(dc, this);
int *heights = new int[mid.width];
double *envValues = new double[mid.width];
GetEnvelope()->GetValues(envValues, mid.width, t0, tstep);
double t = t0;
int x;
for (x = 0; x < mid.width; x++)
{
heights[x] = (int)(mid.height * (1 - envValues[x]));
t += tstep;
}
dc.SetPen(AColor::envelopePen);
for (x = 0; x < mid.width; x++)
double logLower = log(std::max(1.0e-7, mRangeLower)), logUpper = log(std::max(1.0e-7, mRangeUpper));
for (int x = 0; x < mid.width; x++)
{
int thisy = r.y + heights[x];
AColor::Line(dc, mid.x + x, thisy, mid.x + x, thisy+3);
double y;
if(mDisplayLog)
y = (double)mid.height * (logUpper - log(envValues[x])) / (logUpper - logLower);
else
y = (double)mid.height * (mRangeUpper - envValues[x]) / (mRangeUpper - mRangeLower);
int thisy = r.y + (int)y;
AColor::Line(dc, mid.x + x, thisy - 1, mid.x + x, thisy+2);
}
if (heights)
delete[]heights;
if (envValues)
delete[]envValues;
}
@@ -255,11 +262,25 @@ void TimeTrack::testMe()
{
GetEnvelope()->SetDefaultValue(0.5);
GetEnvelope()->Flatten(0.0);
GetEnvelope()->Insert( 0.0, 0.0 );
GetEnvelope()->Insert( 5.0, 1.0 );
GetEnvelope()->Insert( 10.0, 0.0 );
GetEnvelope()->Insert( 0.0, 0.2 );
GetEnvelope()->Insert( 5.0 - 0.001, 0.2 );
GetEnvelope()->Insert( 5.0 + 0.001, 1.3 );
GetEnvelope()->Insert( 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 )
{
printf( "TimeTrack: Integral failed! expected %f got %f\n", expected1, value1);
}
if( fabs(value2 - expected2) > 0.01 )
{
printf( "TimeTrack: IntegralOfInverse failed! expected %f got %f\n", expected2, value2);
}
double reqt0 = 10.0 - .1;
/*double reqt0 = 10.0 - .1;
double reqt1 = 10.0 + .1;
double t0 = warp( reqt0 );
double t1 = warp( reqt1 );
@@ -268,17 +289,6 @@ void TimeTrack::testMe()
printf( "TimeTrack: Warping reverses an interval! [%.2f,%.2f] -> [%.2f,%.2f]\n",
reqt0, reqt1,
t0, t1 );
}
}*/
}
// Indentation settings for Vim and Emacs and unique identifier for Arch, a
// version control system. Please do not modify past this point.
//
// Local Variables:
// c-basic-offset: 3
// indent-tabs-mode: nil
// End:
//
// vim: et sts=3 sw=3
// arch-tag: 8622daf1-c09a-4dcd-8b71-615d194343c7