diff --git a/locale/POTFILES.in b/locale/POTFILES.in index 1b2eb568c..d899a1978 100644 --- a/locale/POTFILES.in +++ b/locale/POTFILES.in @@ -82,6 +82,8 @@ src/Dither.cpp src/Dither.h src/Envelope.cpp src/Envelope.h +src/EnvelopeEditor.cpp +src/EnvelopeEditor.h src/Experimental.h src/FFT.cpp src/FFT.h diff --git a/mac/Audacity.xcodeproj/project.pbxproj b/mac/Audacity.xcodeproj/project.pbxproj index d41aed2f9..4c88e63be 100644 --- a/mac/Audacity.xcodeproj/project.pbxproj +++ b/mac/Audacity.xcodeproj/project.pbxproj @@ -1229,6 +1229,7 @@ 5E15126F1DB0010C00702E29 /* TrackUI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E15126A1DB0010C00702E29 /* TrackUI.cpp */; }; 5E1512701DB0010C00702E29 /* TrackVRulerControls.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E15126B1DB0010C00702E29 /* TrackVRulerControls.cpp */; }; 5E16FF4D1FF9CE0B0085E1B8 /* LanguageNames.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5E16FF4C1FF9CE0B0085E1B8 /* LanguageNames.txt */; }; + 5E17EF712298372D00B47301 /* EnvelopeEditor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E17EF6F2298372D00B47301 /* EnvelopeEditor.cpp */; }; 5E18CFF02291C31000E75250 /* ProjectFileIORegistry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E18CFEE2291C31000E75250 /* ProjectFileIORegistry.cpp */; }; 5E18CFF322931D3D00E75250 /* AudacityMessageBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E18CFF222931D3D00E75250 /* AudacityMessageBox.cpp */; }; 5E19D655217D51190024D0B1 /* PluginMenus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E19D64C217D51190024D0B1 /* PluginMenus.cpp */; }; @@ -3213,6 +3214,8 @@ 5E15126B1DB0010C00702E29 /* TrackVRulerControls.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TrackVRulerControls.cpp; sourceTree = ""; }; 5E15126C1DB0010C00702E29 /* TrackVRulerControls.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackVRulerControls.h; sourceTree = ""; }; 5E16FF4C1FF9CE0B0085E1B8 /* LanguageNames.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = LanguageNames.txt; path = ../locale/LanguageNames.txt; sourceTree = ""; }; + 5E17EF6F2298372D00B47301 /* EnvelopeEditor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EnvelopeEditor.cpp; sourceTree = ""; }; + 5E17EF702298372D00B47301 /* EnvelopeEditor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EnvelopeEditor.h; sourceTree = ""; }; 5E18CFEE2291C31000E75250 /* ProjectFileIORegistry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProjectFileIORegistry.cpp; sourceTree = ""; }; 5E18CFEF2291C31000E75250 /* ProjectFileIORegistry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProjectFileIORegistry.h; sourceTree = ""; }; 5E18CFF122931CA900E75250 /* AudacityMessageBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudacityMessageBox.h; sourceTree = ""; }; @@ -4346,6 +4349,7 @@ 1790AFF709883BFD008A330A /* DirManager.cpp */, 1790AFF909883BFD008A330A /* Dither.cpp */, 1790B05F09883BFD008A330A /* Envelope.cpp */, + 5E17EF6F2298372D00B47301 /* EnvelopeEditor.cpp */, 283135FD0DFBA2E80076D551 /* FFmpeg.cpp */, 1790B07009883BFD008A330A /* FFT.cpp */, 5E07842C1DEE6B8600CA76EA /* FileException.cpp */, @@ -4463,6 +4467,7 @@ 1790AFF809883BFD008A330A /* DirManager.h */, 1790AFFA09883BFD008A330A /* Dither.h */, 1790B06009883BFD008A330A /* Envelope.h */, + 5E17EF702298372D00B47301 /* EnvelopeEditor.h */, 1790B06109883BFD008A330A /* Experimental.h */, 283135FE0DFBA2E80076D551 /* FFmpeg.h */, 1790B07109883BFD008A330A /* FFT.h */, @@ -8465,6 +8470,7 @@ 28EBA8010A78FAF800C8BB1F /* InterpolateAudio.cpp in Sources */, 28EBA8020A78FAF800C8BB1F /* Matrix.cpp in Sources */, 5EF5706B22AAAEDA00C4702C /* ProjectFileManager.cpp in Sources */, + 5E17EF712298372D00B47301 /* EnvelopeEditor.cpp in Sources */, 28E3E6E80A7C14CA00AB1361 /* ExportFLAC.cpp in Sources */, 2897F6F00AB3DB5A003C20C5 /* ControlToolBar.cpp in Sources */, 2897F6F10AB3DB5A003C20C5 /* EditToolBar.cpp in Sources */, diff --git a/src/Envelope.cpp b/src/Envelope.cpp index 72643c74c..aa324d2ae 100644 --- a/src/Envelope.cpp +++ b/src/Envelope.cpp @@ -11,12 +11,13 @@ *******************************************************************//** \class Envelope -\brief Draggable curve used in TrackPanel for varying amplification. +\brief Piecewise linear or piecewise exponential function from double to double - This class manages an envelope - i.e. a piecewise linear funtion + This class manages an envelope - i.e. a function that the user can edit by dragging control points around. The envelope is most commonly used to control the amplitude of a - waveform, but it is also used to shape the Equalization curve. + waveform, but it is also used to shape the Equalization curve, and in + TimeTrack to determine a time warp. *//****************************************************************//** @@ -30,21 +31,14 @@ a draggable point type. #include "Experimental.h" -#include "ViewInfo.h" - #include #include -#include #include -#include #include #include #include -#include "AColor.h" -#include "TrackArtist.h" - static const double VALUE_TOLERANCE = 0.001; Envelope::Envelope(bool exponential, double minValue, double maxValue, double defaultValue) @@ -307,91 +301,6 @@ static double Limit( double Lo, double Value, double Hi ) } #endif -/// TODO: This should probably move to track artist. -static void DrawPoint(wxDC & dc, const wxRect & r, int x, int y, bool top) -{ - if (y >= 0 && y <= r.height) { - wxRect circle(r.x + x, r.y + (top ? y - 1: y - 2), 4, 4); - dc.DrawEllipse(circle); - } -} - -#include "TrackPanelDrawingContext.h" - -/// TODO: This should probably move to track artist. -void Envelope::DrawPoints -(TrackPanelDrawingContext &context, const wxRect & r, - bool dB, double dBRange, - float zoomMin, float zoomMax, bool mirrored) const -{ - auto &dc = context.dc; - const auto artist = TrackArtist::Get( context ); - const auto &zoomInfo = *artist->pZoomInfo; - - bool highlight = false; -#ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING - auto target = dynamic_cast(context.target.get()); - highlight = target && target->GetEnvelope() == this; -#endif - wxPen &pen = highlight ? AColor::uglyPen : AColor::envelopePen; - dc.SetPen( pen ); - dc.SetBrush(*wxWHITE_BRUSH); - - for (int i = 0; i < (int)mEnv.size(); i++) { - const double time = mEnv[i].GetT() + mOffset; - const wxInt64 position = zoomInfo.TimeToPosition(time); - if (position >= 0 && position < r.width) { - // Change colour if this is the draggable point... - if (i == mDragPoint) { - dc.SetPen( pen ); - dc.SetBrush(AColor::envelopeBrush); - } - - double v = mEnv[i].GetVal(); - int x = (int)(position); - int y, y2; - - y = GetWaveYPos(v, zoomMin, zoomMax, r.height, dB, - true, dBRange, false); - if (!mirrored) { - DrawPoint(dc, r, x, y, true); - } - else { - y2 = GetWaveYPos(-v-.000000001, zoomMin, zoomMax, r.height, dB, - true, dBRange, false); - - // This follows the same logic as the envelop drawing in - // TrackArt::DrawEnvelope(). - // TODO: make this calculation into a reusable function. - if (y2 - y < 9) { - int value = (int)((zoomMax / (zoomMax - zoomMin)) * r.height); - y = value - 4; - y2 = value + 4; - } - - DrawPoint(dc, r, x, y, true); - DrawPoint(dc, r, x, y2, false); - - // Contour - y = GetWaveYPos(v, zoomMin, zoomMax, r.height, dB, - false, dBRange, false); - y2 = GetWaveYPos(-v-.000000001, zoomMin, zoomMax, r.height, dB, - false, dBRange, false); - if (y <= y2) { - DrawPoint(dc, r, x, y, true); - DrawPoint(dc, r, x, y2, false); - } - } - - // Change colour back again if was the draggable point. - if (i == mDragPoint) { - dc.SetPen( pen ); - dc.SetBrush(*wxWHITE_BRUSH); - } - } - } -} - bool Envelope::HandleXMLTag(const wxChar *tag, const wxChar **attrs) { // Return unless it's the envelope tag. @@ -447,208 +356,6 @@ void Envelope::WriteXML(XMLWriter &xmlFile) const xmlFile.EndTag(wxT("envelope")); } -namespace -{ -inline int SQR(int x) { return x * x; } -} - -/// ValueOfPixel() converts a y position on screen to an envelope value. -/// @param y - y position, usually of the mouse.relative to the clip. -/// @param height - height of the rectangle we are in. -/// @upper - true if we are on the upper line, false if on lower. -/// @dB - display mode either linear or log. -/// @zoomMin - vertical scale, typically -1.0 -/// @zoomMax - vertical scale, typically +1.0 -float EnvelopeEditor::ValueOfPixel( int y, int height, bool upper, - bool dB, double dBRange, - float zoomMin, float zoomMax) -{ - float v = ::ValueOfPixel(y, height, 0 != mContourOffset, dB, dBRange, zoomMin, zoomMax); - - // MB: this is mostly equivalent to what the old code did, I'm not sure - // if anything special is needed for asymmetric ranges - if(upper) - return mEnvelope.ClampValue(v); - else - return mEnvelope.ClampValue(-v); -} - -/// HandleMouseButtonDown either finds an existing control point or adds a NEW one -/// which is then recorded as the point to drag. -/// This is slightly complicated by there possibly being four control points for -/// a given time value: -/// We have an upper and lower envelope line. -/// Also we may be showing an inner envelope (at 0.5 the range). -bool EnvelopeEditor::HandleMouseButtonDown(const wxMouseEvent & event, wxRect & r, - const ZoomInfo &zoomInfo, - bool dB, double dBRange, - float zoomMin, float zoomMax) -{ - int ctr = (int)(r.height * zoomMax / (zoomMax - zoomMin)); - bool upper = !mMirrored || (zoomMin >= 0.0) || (event.m_y - r.y < ctr); - - int clip_y = event.m_y - r.y; - if(clip_y < 0) clip_y = 0; //keeps point in rect r, even if mouse isn't - if(clip_y > r.GetBottom()) clip_y = r.GetBottom(); - - int bestNum = -1; - int bestDistSqr = 100; // Must be within 10 pixel radius. - - // Member variables hold state that will be needed in dragging. - mButton = event.GetButton(); - mContourOffset = false; - - // wxLogDebug(wxT("Y:%i Height:%i Offset:%i"), y, height, mContourOffset ); - int len = mEnvelope.GetNumberOfPoints(); - - // TODO: extract this into a function FindNearestControlPoint() - // TODO: also fix it so that we can drag the last point on an envelope. - for (int i = 0; i < len; i++) { //search for control point nearest click - const double time = mEnvelope[i].GetT() + mEnvelope.GetOffset(); - const wxInt64 position = zoomInfo.TimeToPosition(time); - if (position >= 0 && position < r.width) { - - int x = (int)(position); - int y[4]; - int numControlPoints; - - // Outer control points - double value = mEnvelope[i].GetVal(); - y[0] = GetWaveYPos(value, zoomMin, zoomMax, r.height, - dB, true, dBRange, false); - y[1] = GetWaveYPos(-value, zoomMin, zoomMax, r.height, - dB, true, dBRange, false); - - // Inner control points(contour) - y[2] = GetWaveYPos(value, zoomMin, zoomMax, r.height, - dB, false, dBRange, false); - y[3] = GetWaveYPos(-value -.00000001, zoomMin, zoomMax, - r.height, dB, false, dBRange, false); - - numControlPoints = 4; - - if (y[2] > y[3]) - numControlPoints = 2; - - if (!mMirrored) - numControlPoints = 1; - - const int deltaXSquared = SQR(x - (event.m_x - r.x)); - for(int j=0; j 1); - } - } - } - } - - if (bestNum >= 0) { - mEnvelope.SetDragPoint(bestNum); - } - else { - // TODO: Extract this into a function CreateNewPoint - const double when = zoomInfo.PositionToTime(event.m_x, r.x); - - // if (when <= 0 || when >= mTrackLen) - // return false; - - const double v = mEnvelope.GetValue( when ); - - int ct = GetWaveYPos( v, zoomMin, zoomMax, r.height, dB, - false, dBRange, false) ; - int cb = GetWaveYPos( -v-.000000001, zoomMin, zoomMax, r.height, dB, - false, dBRange, false) ; - if (ct <= cb || !mMirrored) { - int t = GetWaveYPos( v, zoomMin, zoomMax, r.height, dB, - true, dBRange, false) ; - int b = GetWaveYPos( -v, zoomMin, zoomMax, r.height, dB, - true, dBRange, false) ; - - ct = (t + ct) / 2; - cb = (b + cb) / 2; - - if (mMirrored && - (event.m_y - r.y) > ct && - ((event.m_y - r.y) < cb)) - mContourOffset = true; - else - mContourOffset = false; - } - - double newVal = ValueOfPixel(clip_y, r.height, upper, dB, dBRange, - zoomMin, zoomMax); - - mEnvelope.SetDragPoint(mEnvelope.InsertOrReplace(when, newVal)); - mDirty = true; - } - - mUpper = upper; - - // const int dragPoint = mEnvelope.GetDragPoint(); - // mInitialVal = mEnvelope[dragPoint].GetVal(); - // mInitialY = event.m_y+mContourOffset; - - return true; -} - -void EnvelopeEditor::MoveDragPoint(const wxMouseEvent & event, wxRect & r, - const ZoomInfo &zoomInfo, bool dB, double dBRange, - float zoomMin, float zoomMax) -{ - int clip_y = event.m_y - r.y; - if(clip_y < 0) clip_y = 0; - if(clip_y > r.height) clip_y = r.height; - double newVal = ValueOfPixel(clip_y, r.height, mUpper, dB, dBRange, - zoomMin, zoomMax); - - // We no longer tolerate multiple envelope points at the same t. - // epsilon is less than the time offset of a single sample - // TODO: However because mTrackEpsilon assumes 200KHz this use - // of epsilon is a tad bogus. What we need to do instead is DELETE - // a duplicated point on a mouse up. - double newWhen = zoomInfo.PositionToTime(event.m_x, r.x) - mEnvelope.GetOffset(); - mEnvelope.MoveDragPoint(newWhen, newVal); -} - -bool EnvelopeEditor::HandleDragging(const wxMouseEvent & event, wxRect & r, - const ZoomInfo &zoomInfo, bool dB, double dBRange, - float zoomMin, float zoomMax, - float WXUNUSED(eMin), float WXUNUSED(eMax)) -{ - mDirty = true; - - wxRect larger = r; - larger.Inflate(10, 10); - - if (larger.Contains(event.m_x, event.m_y)) - { - // IF we're in the rect THEN we're not deleting this point (anymore). - // ...we're dragging it. - MoveDragPoint( event, r, zoomInfo, dB, dBRange, zoomMin, zoomMax); - return true; - } - - if(!mEnvelope.GetDragPointValid()) - // IF we already know we're deleting THEN no envelope point to update. - return false; - - // Invalidate the point - mEnvelope.SetDragPointValid(false); - return true; -} - -// Exit dragging mode and delete dragged point if neccessary. -bool EnvelopeEditor::HandleMouseButtonUp() -{ - mEnvelope.ClearDragPoint(); - mButton = wxMOUSE_BTN_NONE; - return true; -} - void Envelope::Delete( int point ) { mEnv.erase(mEnv.begin() + point); @@ -659,22 +366,6 @@ void Envelope::Insert(int point, const EnvPoint &p) mEnv.insert(mEnv.begin() + point, p); } -// Returns true if parent needs to be redrawn -bool EnvelopeEditor::MouseEvent(const wxMouseEvent & event, wxRect & r, - const ZoomInfo &zoomInfo, bool dB, double dBRange, - float zoomMin, float zoomMax) -{ - if (event.ButtonDown() && mButton == wxMOUSE_BTN_NONE) - return HandleMouseButtonDown( event, r, zoomInfo, dB, dBRange, - zoomMin, zoomMax); - if (event.Dragging() && mEnvelope.GetDragPoint() >= 0) - return HandleDragging( event, r, zoomInfo, dB, dBRange, - zoomMin, zoomMax); - if (event.ButtonUp() && event.GetButton() == mButton) - return HandleMouseButtonUp(); - return false; -} - void Envelope::CollapseRegion( double t0, double t1, double sampleDur ) // NOFAIL-GUARANTEE { @@ -1346,48 +1037,6 @@ void Envelope::GetValuesRelative } } -void Envelope::GetValues - ( double alignedTime, double sampleDur, - double *buffer, int bufferLen, int leftOffset, - const ZoomInfo &zoomInfo ) - const -{ - // Getting many envelope values, corresponding to pixel columns, which may - // not be uniformly spaced in time when there is a fisheye. - - double prevDiscreteTime=0.0, prevSampleVal=0.0, nextSampleVal=0.0; - for ( int xx = 0; xx < bufferLen; ++xx ) { - auto time = zoomInfo.PositionToTime( xx, -leftOffset ); - if ( sampleDur <= 0 ) - // Sample interval not defined (as for time track) - buffer[xx] = GetValue( time ); - else { - // The level of zoom-in may resolve individual samples. - // If so, then instead of evaluating the envelope directly, - // we draw a piecewise curve with knees at each sample time. - // This actually makes clearer what happens as you drag envelope - // points and make discontinuities. - auto leftDiscreteTime = alignedTime + - sampleDur * floor( ( time - alignedTime ) / sampleDur ); - if ( xx == 0 || leftDiscreteTime != prevDiscreteTime ) { - prevDiscreteTime = leftDiscreteTime; - prevSampleVal = - GetValue( prevDiscreteTime, sampleDur ); - nextSampleVal = - GetValue( prevDiscreteTime + sampleDur, sampleDur ); - } - auto ratio = ( time - leftDiscreteTime ) / sampleDur; - if ( GetExponential() ) - buffer[ xx ] = exp( - ( 1.0 - ratio ) * log( prevSampleVal ) - + ratio * log( nextSampleVal ) ); - else - buffer[ xx ] = - ( 1.0 - ratio ) * prevSampleVal + ratio * nextSampleVal; - } - } -} - // relative time int Envelope::NumberOfPointsAfter(double t) const { @@ -1806,19 +1455,3 @@ void Envelope::testMe() checkResult( 18, NextPointAfter( 0 ), 5 ); checkResult( 19, NextPointAfter( 5 ), 10 ); } - -EnvelopeEditor::EnvelopeEditor(Envelope &envelope, bool mirrored) - : mEnvelope(envelope) - , mMirrored(mirrored) - , mContourOffset(-1) - // , mInitialVal(-1.0) - // , mInitialY(-1) - , mUpper(false) - , mButton(wxMOUSE_BTN_NONE) - , mDirty(false) -{ -} - -EnvelopeEditor::~EnvelopeEditor() -{ -} diff --git a/src/Envelope.h b/src/Envelope.h index c65c4bbf4..cb0f53420 100644 --- a/src/Envelope.h +++ b/src/Envelope.h @@ -112,12 +112,6 @@ public: XMLTagHandler *HandleXMLChild(const wxChar *tag) override; void WriteXML(XMLWriter &xmlFile) const /* not override */; - void DrawPoints( - TrackPanelDrawingContext &context, - const wxRect & r, - bool dB, double dBRange, - float zoomMin, float zoomMax, bool mirrored) const; - // Handling Cut/Copy/Paste events // sampleDur determines when the endpoint of the collapse is near enough // to an endpoint of the domain, that an extra control point is not needed. @@ -148,14 +142,6 @@ public: * more than one value in a row. */ void GetValues(double *buffer, int len, double t0, double tstep) const; - /** \brief Get many envelope points for pixel columns at once, - * but don't assume uniform time per pixel. - */ - void GetValues - ( double aligned_time, double sampleDur, - double *buffer, int bufferLen, int leftOffset, - const ZoomInfo &zoomInfo) const; - // Guarantee an envelope point at the end of the domain. void Cap( double sampleDur ); @@ -279,49 +265,4 @@ inline void EnvPoint::SetVal( Envelope *pEnvelope, double val ) mVal = val; } -// A class that holds state for the duration of dragging -// of an envelope point. -class EnvelopeEditor -{ -public: - EnvelopeEditor(Envelope &envelope, bool mirrored); - ~EnvelopeEditor(); - - // Event Handlers - // Each of these returns true if the envelope needs to be redrawn - bool MouseEvent(const wxMouseEvent & event, wxRect & r, - const ZoomInfo &zoomInfo, bool dB, double dBRange, - float zoomMin = -1.0, float zoomMax = 1.0); - -private: - bool HandleMouseButtonDown(const wxMouseEvent & event, wxRect & r, - const ZoomInfo &zoomInfo, bool dB, double dBRange, - float zoomMin = -1.0, float zoomMax = 1.0); - bool HandleDragging(const wxMouseEvent & event, wxRect & r, - const ZoomInfo &zoomInfo, bool dB, double dBRange, - float zoomMin = -1.0, float zoomMax = 1.0, float eMin = 0., float eMax = 2.); - bool HandleMouseButtonUp(); - -private: - float ValueOfPixel(int y, int height, bool upper, - bool dB, double dBRange, - float zoomMin, float zoomMax); - void MoveDragPoint(const wxMouseEvent & event, wxRect & r, - const ZoomInfo &zoomInfo, bool dB, double dBRange, - float zoomMin, float zoomMax); - - Envelope &mEnvelope; - const bool mMirrored; - - /** \brief Number of pixels contour is from the true envelope. */ - int mContourOffset; - - // double mInitialVal; - - // int mInitialY; - bool mUpper; - int mButton; - bool mDirty; -}; - #endif diff --git a/src/EnvelopeEditor.cpp b/src/EnvelopeEditor.cpp new file mode 100644 index 000000000..c2c11659a --- /dev/null +++ b/src/EnvelopeEditor.cpp @@ -0,0 +1,380 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + EnvelopeEditor.cpp + + Paul Licameli split this from Envelope.cpp + +**********************************************************************/ + +#include "EnvelopeEditor.h" + +#include +#include + +#include "AColor.h" +#include "Envelope.h" +#include "TrackArtist.h" +#include "TrackPanelDrawingContext.h" +#include "ViewInfo.h" + +namespace { +void DrawPoint(wxDC & dc, const wxRect & r, int x, int y, bool top) +{ + if (y >= 0 && y <= r.height) { + wxRect circle(r.x + x, r.y + (top ? y - 1: y - 2), 4, 4); + dc.DrawEllipse(circle); + } +} +} + +void EnvelopeEditor::DrawPoints +(const Envelope &env, + TrackPanelDrawingContext &context, const wxRect & r, + bool dB, double dBRange, + float zoomMin, float zoomMax, bool mirrored) +{ + auto &dc = context.dc; + const auto artist = TrackArtist::Get( context ); + const auto &zoomInfo = *artist->pZoomInfo; + + bool highlight = false; +#ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING + auto target = dynamic_cast(context.target.get()); + highlight = target && target->GetEnvelope() == this; +#endif + wxPen &pen = highlight ? AColor::uglyPen : AColor::envelopePen; + dc.SetPen( pen ); + dc.SetBrush(*wxWHITE_BRUSH); + + for (int i = 0; i < (int)env.GetNumberOfPoints(); i++) { + const double time = env[i].GetT() + env.GetOffset(); + const wxInt64 position = zoomInfo.TimeToPosition(time); + if (position >= 0 && position < r.width) { + // Change colour if this is the draggable point... + if (i == env.GetDragPoint()) { + dc.SetPen( pen ); + dc.SetBrush(AColor::envelopeBrush); + } + + double v = env[i].GetVal(); + int x = (int)(position); + int y, y2; + + y = GetWaveYPos(v, zoomMin, zoomMax, r.height, dB, + true, dBRange, false); + if (!mirrored) { + DrawPoint(dc, r, x, y, true); + } + else { + y2 = GetWaveYPos(-v-.000000001, zoomMin, zoomMax, r.height, dB, + true, dBRange, false); + + // This follows the same logic as the envelop drawing in + // TrackArt::DrawEnvelope(). + // TODO: make this calculation into a reusable function. + if (y2 - y < 9) { + int value = (int)((zoomMax / (zoomMax - zoomMin)) * r.height); + y = value - 4; + y2 = value + 4; + } + + DrawPoint(dc, r, x, y, true); + DrawPoint(dc, r, x, y2, false); + + // Contour + y = GetWaveYPos(v, zoomMin, zoomMax, r.height, dB, + false, dBRange, false); + y2 = GetWaveYPos(-v-.000000001, zoomMin, zoomMax, r.height, dB, + false, dBRange, false); + if (y <= y2) { + DrawPoint(dc, r, x, y, true); + DrawPoint(dc, r, x, y2, false); + } + } + + // Change colour back again if was the draggable point. + if (i == env.GetDragPoint()) { + dc.SetPen( pen ); + dc.SetBrush(*wxWHITE_BRUSH); + } + } + } +} + +void EnvelopeEditor::GetValues + ( const Envelope &env, + double alignedTime, double sampleDur, + double *buffer, int bufferLen, int leftOffset, + const ZoomInfo &zoomInfo ) +{ + // Getting many envelope values, corresponding to pixel columns, which may + // not be uniformly spaced in time when there is a fisheye. + + double prevDiscreteTime=0.0, prevSampleVal=0.0, nextSampleVal=0.0; + for ( int xx = 0; xx < bufferLen; ++xx ) { + auto time = zoomInfo.PositionToTime( xx, -leftOffset ); + if ( sampleDur <= 0 ) + // Sample interval not defined (as for time track) + buffer[xx] = env.GetValue( time ); + else { + // The level of zoom-in may resolve individual samples. + // If so, then instead of evaluating the envelope directly, + // we draw a piecewise curve with knees at each sample time. + // This actually makes clearer what happens as you drag envelope + // points and make discontinuities. + auto leftDiscreteTime = alignedTime + + sampleDur * floor( ( time - alignedTime ) / sampleDur ); + if ( xx == 0 || leftDiscreteTime != prevDiscreteTime ) { + prevDiscreteTime = leftDiscreteTime; + prevSampleVal = + env.GetValue( prevDiscreteTime, sampleDur ); + nextSampleVal = + env.GetValue( prevDiscreteTime + sampleDur, sampleDur ); + } + auto ratio = ( time - leftDiscreteTime ) / sampleDur; + if ( env.GetExponential() ) + buffer[ xx ] = exp( + ( 1.0 - ratio ) * log( prevSampleVal ) + + ratio * log( nextSampleVal ) ); + else + buffer[ xx ] = + ( 1.0 - ratio ) * prevSampleVal + ratio * nextSampleVal; + } + } +} + +EnvelopeEditor::EnvelopeEditor(Envelope &envelope, bool mirrored) + : mEnvelope(envelope) + , mMirrored(mirrored) + , mContourOffset(-1) + // , mInitialVal(-1.0) + // , mInitialY(-1) + , mUpper(false) + , mButton(wxMOUSE_BTN_NONE) + , mDirty(false) +{ +} + +EnvelopeEditor::~EnvelopeEditor() +{ +} + +namespace +{ +inline int SQR(int x) { return x * x; } +} + +/// ValueOfPixel() converts a y position on screen to an envelope value. +/// @param y - y position, usually of the mouse.relative to the clip. +/// @param height - height of the rectangle we are in. +/// @upper - true if we are on the upper line, false if on lower. +/// @dB - display mode either linear or log. +/// @zoomMin - vertical scale, typically -1.0 +/// @zoomMax - vertical scale, typically +1.0 +float EnvelopeEditor::ValueOfPixel( int y, int height, bool upper, + bool dB, double dBRange, + float zoomMin, float zoomMax) +{ + float v = ::ValueOfPixel(y, height, 0 != mContourOffset, dB, dBRange, zoomMin, zoomMax); + + // MB: this is mostly equivalent to what the old code did, I'm not sure + // if anything special is needed for asymmetric ranges + if(upper) + return mEnvelope.ClampValue(v); + else + return mEnvelope.ClampValue(-v); +} + +/// HandleMouseButtonDown either finds an existing control point or adds a NEW one +/// which is then recorded as the point to drag. +/// This is slightly complicated by there possibly being four control points for +/// a given time value: +/// We have an upper and lower envelope line. +/// Also we may be showing an inner envelope (at 0.5 the range). +bool EnvelopeEditor::HandleMouseButtonDown(const wxMouseEvent & event, wxRect & r, + const ZoomInfo &zoomInfo, + bool dB, double dBRange, + float zoomMin, float zoomMax) +{ + int ctr = (int)(r.height * zoomMax / (zoomMax - zoomMin)); + bool upper = !mMirrored || (zoomMin >= 0.0) || (event.m_y - r.y < ctr); + + int clip_y = event.m_y - r.y; + if(clip_y < 0) clip_y = 0; //keeps point in rect r, even if mouse isn't + if(clip_y > r.GetBottom()) clip_y = r.GetBottom(); + + int bestNum = -1; + int bestDistSqr = 100; // Must be within 10 pixel radius. + + // Member variables hold state that will be needed in dragging. + mButton = event.GetButton(); + mContourOffset = false; + + // wxLogDebug(wxT("Y:%i Height:%i Offset:%i"), y, height, mContourOffset ); + int len = mEnvelope.GetNumberOfPoints(); + + // TODO: extract this into a function FindNearestControlPoint() + // TODO: also fix it so that we can drag the last point on an envelope. + for (int i = 0; i < len; i++) { //search for control point nearest click + const double time = mEnvelope[i].GetT() + mEnvelope.GetOffset(); + const wxInt64 position = zoomInfo.TimeToPosition(time); + if (position >= 0 && position < r.width) { + + int x = (int)(position); + int y[4]; + int numControlPoints; + + // Outer control points + double value = mEnvelope[i].GetVal(); + y[0] = GetWaveYPos(value, zoomMin, zoomMax, r.height, + dB, true, dBRange, false); + y[1] = GetWaveYPos(-value, zoomMin, zoomMax, r.height, + dB, true, dBRange, false); + + // Inner control points(contour) + y[2] = GetWaveYPos(value, zoomMin, zoomMax, r.height, + dB, false, dBRange, false); + y[3] = GetWaveYPos(-value -.00000001, zoomMin, zoomMax, + r.height, dB, false, dBRange, false); + + numControlPoints = 4; + + if (y[2] > y[3]) + numControlPoints = 2; + + if (!mMirrored) + numControlPoints = 1; + + const int deltaXSquared = SQR(x - (event.m_x - r.x)); + for(int j=0; j 1); + } + } + } + } + + if (bestNum >= 0) { + mEnvelope.SetDragPoint(bestNum); + } + else { + // TODO: Extract this into a function CreateNewPoint + const double when = zoomInfo.PositionToTime(event.m_x, r.x); + + // if (when <= 0 || when >= mTrackLen) + // return false; + + const double v = mEnvelope.GetValue( when ); + + int ct = GetWaveYPos( v, zoomMin, zoomMax, r.height, dB, + false, dBRange, false) ; + int cb = GetWaveYPos( -v-.000000001, zoomMin, zoomMax, r.height, dB, + false, dBRange, false) ; + if (ct <= cb || !mMirrored) { + int t = GetWaveYPos( v, zoomMin, zoomMax, r.height, dB, + true, dBRange, false) ; + int b = GetWaveYPos( -v, zoomMin, zoomMax, r.height, dB, + true, dBRange, false) ; + + ct = (t + ct) / 2; + cb = (b + cb) / 2; + + if (mMirrored && + (event.m_y - r.y) > ct && + ((event.m_y - r.y) < cb)) + mContourOffset = true; + else + mContourOffset = false; + } + + double newVal = ValueOfPixel(clip_y, r.height, upper, dB, dBRange, + zoomMin, zoomMax); + + mEnvelope.SetDragPoint(mEnvelope.InsertOrReplace(when, newVal)); + mDirty = true; + } + + mUpper = upper; + + // const int dragPoint = mEnvelope.GetDragPoint(); + // mInitialVal = mEnvelope[dragPoint].GetVal(); + // mInitialY = event.m_y+mContourOffset; + + return true; +} + +void EnvelopeEditor::MoveDragPoint(const wxMouseEvent & event, wxRect & r, + const ZoomInfo &zoomInfo, bool dB, double dBRange, + float zoomMin, float zoomMax) +{ + int clip_y = event.m_y - r.y; + if(clip_y < 0) clip_y = 0; + if(clip_y > r.height) clip_y = r.height; + double newVal = ValueOfPixel(clip_y, r.height, mUpper, dB, dBRange, + zoomMin, zoomMax); + + // We no longer tolerate multiple envelope points at the same t. + // epsilon is less than the time offset of a single sample + // TODO: However because mTrackEpsilon assumes 200KHz this use + // of epsilon is a tad bogus. What we need to do instead is DELETE + // a duplicated point on a mouse up. + double newWhen = zoomInfo.PositionToTime(event.m_x, r.x) - mEnvelope.GetOffset(); + mEnvelope.MoveDragPoint(newWhen, newVal); +} + +bool EnvelopeEditor::HandleDragging(const wxMouseEvent & event, wxRect & r, + const ZoomInfo &zoomInfo, bool dB, double dBRange, + float zoomMin, float zoomMax, + float WXUNUSED(eMin), float WXUNUSED(eMax)) +{ + mDirty = true; + + wxRect larger = r; + larger.Inflate(10, 10); + + if (larger.Contains(event.m_x, event.m_y)) + { + // IF we're in the rect THEN we're not deleting this point (anymore). + // ...we're dragging it. + MoveDragPoint( event, r, zoomInfo, dB, dBRange, zoomMin, zoomMax); + return true; + } + + if(!mEnvelope.GetDragPointValid()) + // IF we already know we're deleting THEN no envelope point to update. + return false; + + // Invalidate the point + mEnvelope.SetDragPointValid(false); + return true; +} + +// Exit dragging mode and delete dragged point if neccessary. +bool EnvelopeEditor::HandleMouseButtonUp() +{ + mEnvelope.ClearDragPoint(); + mButton = wxMOUSE_BTN_NONE; + return true; +} + +// Returns true if parent needs to be redrawn +bool EnvelopeEditor::MouseEvent(const wxMouseEvent & event, wxRect & r, + const ZoomInfo &zoomInfo, bool dB, double dBRange, + float zoomMin, float zoomMax) +{ + if (event.ButtonDown() && mButton == wxMOUSE_BTN_NONE) + return HandleMouseButtonDown( event, r, zoomInfo, dB, dBRange, + zoomMin, zoomMax); + if (event.Dragging() && mEnvelope.GetDragPoint() >= 0) + return HandleDragging( event, r, zoomInfo, dB, dBRange, + zoomMin, zoomMax); + if (event.ButtonUp() && event.GetButton() == mButton) + return HandleMouseButtonUp(); + return false; +} diff --git a/src/EnvelopeEditor.h b/src/EnvelopeEditor.h new file mode 100644 index 000000000..e17efe2f5 --- /dev/null +++ b/src/EnvelopeEditor.h @@ -0,0 +1,82 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + EnvelopeEditor.h + + Paul Licameli split this from Envelope.h + +**********************************************************************/ + +#ifndef __AUDACITY_ENVELOPE_EDITOR__ +#define __AUDACITY_ENVELOPE_EDITOR__ + +class wxMouseEvent; +class wxRect; +class Envelope; +class TrackPanelDrawingContext; +class ZoomInfo; + +// A class that holds state for the duration of dragging +// of an envelope point. +class EnvelopeEditor +{ +public: + static void DrawPoints( + const Envelope &env, + TrackPanelDrawingContext &context, + const wxRect & r, + bool dB, double dBRange, + float zoomMin, float zoomMax, bool mirrored); + + /** \brief Get many envelope points for pixel columns at once, + * but don't assume uniform time per pixel. + */ + static void GetValues + ( const Envelope &env, + double aligned_time, double sampleDur, + double *buffer, int bufferLen, int leftOffset, + const ZoomInfo &zoomInfo); + + EnvelopeEditor(Envelope &envelope, bool mirrored); + ~EnvelopeEditor(); + + // Event Handlers + // Each of these returns true if the envelope needs to be redrawn + bool MouseEvent(const wxMouseEvent & event, wxRect & r, + const ZoomInfo &zoomInfo, bool dB, double dBRange, + float zoomMin = -1.0, float zoomMax = 1.0); + +private: + bool HandleMouseButtonDown(const wxMouseEvent & event, wxRect & r, + const ZoomInfo &zoomInfo, bool dB, double dBRange, + float zoomMin = -1.0, float zoomMax = 1.0); + bool HandleDragging(const wxMouseEvent & event, wxRect & r, + const ZoomInfo &zoomInfo, bool dB, double dBRange, + float zoomMin = -1.0, float zoomMax = 1.0, float eMin = 0., float eMax = 2.); + bool HandleMouseButtonUp(); + +private: + float ValueOfPixel(int y, int height, bool upper, + bool dB, double dBRange, + float zoomMin, float zoomMax); + void MoveDragPoint(const wxMouseEvent & event, wxRect & r, + const ZoomInfo &zoomInfo, bool dB, double dBRange, + float zoomMin, float zoomMax); + + Envelope &mEnvelope; + const bool mMirrored; + + /** \brief Number of pixels contour is from the true envelope. */ + int mContourOffset; + + // double mInitialVal; + + // int mInitialY; + bool mUpper; + int mButton; + bool mDirty; +}; + + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index c471e5e62..8b0a1e696 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -143,6 +143,8 @@ audacity_SOURCES = \ Diags.h \ Envelope.cpp \ Envelope.h \ + EnvelopeEditor.cpp \ + EnvelopeEditor.h \ Experimental.h \ FFmpeg.cpp \ FFmpeg.h \ diff --git a/src/Makefile.in b/src/Makefile.in index 3d7ccf0c6..3cc2ea94d 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -299,38 +299,38 @@ am__audacity_SOURCES_DIST = BlockFile.cpp BlockFile.h DirManager.cpp \ CrashReport.cpp CrashReport.h Dependencies.cpp Dependencies.h \ DeviceChange.cpp DeviceChange.h DeviceManager.cpp \ DeviceManager.h Diags.cpp Diags.h Envelope.cpp Envelope.h \ - Experimental.h FFmpeg.cpp FFmpeg.h FFT.cpp FFT.h \ - FileException.cpp FileException.h FileIO.cpp FileIO.h \ - FileNames.cpp FileNames.h float_cast.h FreqWindow.cpp \ - FreqWindow.h HelpText.cpp HelpText.h HistoryWindow.cpp \ - HistoryWindow.h HitTestResult.h ImageManipulation.cpp \ - ImageManipulation.h InconsistencyException.cpp \ - InconsistencyException.h InterpolateAudio.cpp \ - InterpolateAudio.h KeyboardCapture.cpp KeyboardCapture.h \ - LabelDialog.cpp LabelDialog.h LabelTrack.cpp LabelTrack.h \ - LangChoice.cpp LangChoice.h Languages.cpp Languages.h \ - Legacy.cpp Legacy.h Lyrics.cpp Lyrics.h LyricsWindow.cpp \ - LyricsWindow.h MacroMagic.h Matrix.cpp Matrix.h MemoryX.h \ - Menus.cpp Menus.h MissingAliasFileDialog.cpp \ - MissingAliasFileDialog.h Mix.cpp Mix.h MixerBoard.cpp \ - MixerBoard.h ModuleManager.cpp ModuleManager.h NumberScale.h \ - PitchName.cpp PitchName.h PlatformCompatibility.cpp \ - PlatformCompatibility.h PluginManager.cpp PluginManager.h \ - Printing.cpp Printing.h Profiler.cpp Profiler.h Project.cpp \ - Project.h ProjectAudioIO.cpp ProjectAudioIO.h \ - ProjectAudioManager.cpp ProjectAudioManager.h \ - ProjectFileIO.cpp ProjectFileIO.h ProjectFileIORegistry.cpp \ - ProjectFileIORegistry.h ProjectFileManager.cpp \ - ProjectFileManager.h ProjectFSCK.cpp ProjectFSCK.h \ - ProjectHistory.cpp ProjectHistory.h ProjectManager.cpp \ - ProjectManager.h ProjectSelectionManager.cpp \ - ProjectSelectionManager.h ProjectSettings.cpp \ - ProjectSettings.h ProjectWindow.cpp ProjectWindow.h \ - RealFFTf.cpp RealFFTf.h RealFFTf48x.cpp RealFFTf48x.h \ - RefreshCode.h Resample.cpp Resample.h RevisionIdent.h \ - RingBuffer.cpp RingBuffer.h Screenshot.cpp Screenshot.h \ - SelectedRegion.cpp SelectedRegion.h SelectionState.cpp \ - SelectionState.h Shuttle.cpp Shuttle.h \ + EnvelopeEditor.cpp EnvelopeEditor.h Experimental.h FFmpeg.cpp \ + FFmpeg.h FFT.cpp FFT.h FileException.cpp FileException.h \ + FileIO.cpp FileIO.h FileNames.cpp FileNames.h float_cast.h \ + FreqWindow.cpp FreqWindow.h HelpText.cpp HelpText.h \ + HistoryWindow.cpp HistoryWindow.h HitTestResult.h \ + ImageManipulation.cpp ImageManipulation.h \ + InconsistencyException.cpp InconsistencyException.h \ + InterpolateAudio.cpp InterpolateAudio.h KeyboardCapture.cpp \ + KeyboardCapture.h LabelDialog.cpp LabelDialog.h LabelTrack.cpp \ + LabelTrack.h LangChoice.cpp LangChoice.h Languages.cpp \ + Languages.h Legacy.cpp Legacy.h Lyrics.cpp Lyrics.h \ + LyricsWindow.cpp LyricsWindow.h MacroMagic.h Matrix.cpp \ + Matrix.h MemoryX.h Menus.cpp Menus.h \ + MissingAliasFileDialog.cpp MissingAliasFileDialog.h Mix.cpp \ + Mix.h MixerBoard.cpp MixerBoard.h ModuleManager.cpp \ + ModuleManager.h NumberScale.h PitchName.cpp PitchName.h \ + PlatformCompatibility.cpp PlatformCompatibility.h \ + PluginManager.cpp PluginManager.h Printing.cpp Printing.h \ + Profiler.cpp Profiler.h Project.cpp Project.h \ + ProjectAudioIO.cpp ProjectAudioIO.h ProjectAudioManager.cpp \ + ProjectAudioManager.h ProjectFileIO.cpp ProjectFileIO.h \ + ProjectFileIORegistry.cpp ProjectFileIORegistry.h \ + ProjectFileManager.cpp ProjectFileManager.h ProjectFSCK.cpp \ + ProjectFSCK.h ProjectHistory.cpp ProjectHistory.h \ + ProjectManager.cpp ProjectManager.h \ + ProjectSelectionManager.cpp ProjectSelectionManager.h \ + ProjectSettings.cpp ProjectSettings.h ProjectWindow.cpp \ + ProjectWindow.h RealFFTf.cpp RealFFTf.h RealFFTf48x.cpp \ + RealFFTf48x.h RefreshCode.h Resample.cpp Resample.h \ + RevisionIdent.h RingBuffer.cpp RingBuffer.h Screenshot.cpp \ + Screenshot.h SelectedRegion.cpp SelectedRegion.h \ + SelectionState.cpp SelectionState.h Shuttle.cpp Shuttle.h \ ShuttleGetDefinition.cpp ShuttleGetDefinition.h ShuttleGui.cpp \ ShuttleGui.h ShuttlePrefs.cpp ShuttlePrefs.h Snap.cpp Snap.h \ SoundActivatedRecord.cpp SoundActivatedRecord.h Spectrum.cpp \ @@ -646,11 +646,11 @@ am_audacity_OBJECTS = $(am__objects_1) audacity-AboutDialog.$(OBJEXT) \ audacity-Dependencies.$(OBJEXT) \ audacity-DeviceChange.$(OBJEXT) \ audacity-DeviceManager.$(OBJEXT) audacity-Diags.$(OBJEXT) \ - audacity-Envelope.$(OBJEXT) audacity-FFmpeg.$(OBJEXT) \ - audacity-FFT.$(OBJEXT) audacity-FileException.$(OBJEXT) \ - audacity-FileIO.$(OBJEXT) audacity-FileNames.$(OBJEXT) \ - audacity-FreqWindow.$(OBJEXT) audacity-HelpText.$(OBJEXT) \ - audacity-HistoryWindow.$(OBJEXT) \ + audacity-Envelope.$(OBJEXT) audacity-EnvelopeEditor.$(OBJEXT) \ + audacity-FFmpeg.$(OBJEXT) audacity-FFT.$(OBJEXT) \ + audacity-FileException.$(OBJEXT) audacity-FileIO.$(OBJEXT) \ + audacity-FileNames.$(OBJEXT) audacity-FreqWindow.$(OBJEXT) \ + audacity-HelpText.$(OBJEXT) audacity-HistoryWindow.$(OBJEXT) \ audacity-ImageManipulation.$(OBJEXT) \ audacity-InconsistencyException.$(OBJEXT) \ audacity-InterpolateAudio.$(OBJEXT) \ @@ -1375,38 +1375,38 @@ audacity_SOURCES = $(libaudacity_la_SOURCES) AboutDialog.cpp \ CrashReport.cpp CrashReport.h Dependencies.cpp Dependencies.h \ DeviceChange.cpp DeviceChange.h DeviceManager.cpp \ DeviceManager.h Diags.cpp Diags.h Envelope.cpp Envelope.h \ - Experimental.h FFmpeg.cpp FFmpeg.h FFT.cpp FFT.h \ - FileException.cpp FileException.h FileIO.cpp FileIO.h \ - FileNames.cpp FileNames.h float_cast.h FreqWindow.cpp \ - FreqWindow.h HelpText.cpp HelpText.h HistoryWindow.cpp \ - HistoryWindow.h HitTestResult.h ImageManipulation.cpp \ - ImageManipulation.h InconsistencyException.cpp \ - InconsistencyException.h InterpolateAudio.cpp \ - InterpolateAudio.h KeyboardCapture.cpp KeyboardCapture.h \ - LabelDialog.cpp LabelDialog.h LabelTrack.cpp LabelTrack.h \ - LangChoice.cpp LangChoice.h Languages.cpp Languages.h \ - Legacy.cpp Legacy.h Lyrics.cpp Lyrics.h LyricsWindow.cpp \ - LyricsWindow.h MacroMagic.h Matrix.cpp Matrix.h MemoryX.h \ - Menus.cpp Menus.h MissingAliasFileDialog.cpp \ - MissingAliasFileDialog.h Mix.cpp Mix.h MixerBoard.cpp \ - MixerBoard.h ModuleManager.cpp ModuleManager.h NumberScale.h \ - PitchName.cpp PitchName.h PlatformCompatibility.cpp \ - PlatformCompatibility.h PluginManager.cpp PluginManager.h \ - Printing.cpp Printing.h Profiler.cpp Profiler.h Project.cpp \ - Project.h ProjectAudioIO.cpp ProjectAudioIO.h \ - ProjectAudioManager.cpp ProjectAudioManager.h \ - ProjectFileIO.cpp ProjectFileIO.h ProjectFileIORegistry.cpp \ - ProjectFileIORegistry.h ProjectFileManager.cpp \ - ProjectFileManager.h ProjectFSCK.cpp ProjectFSCK.h \ - ProjectHistory.cpp ProjectHistory.h ProjectManager.cpp \ - ProjectManager.h ProjectSelectionManager.cpp \ - ProjectSelectionManager.h ProjectSettings.cpp \ - ProjectSettings.h ProjectWindow.cpp ProjectWindow.h \ - RealFFTf.cpp RealFFTf.h RealFFTf48x.cpp RealFFTf48x.h \ - RefreshCode.h Resample.cpp Resample.h RevisionIdent.h \ - RingBuffer.cpp RingBuffer.h Screenshot.cpp Screenshot.h \ - SelectedRegion.cpp SelectedRegion.h SelectionState.cpp \ - SelectionState.h Shuttle.cpp Shuttle.h \ + EnvelopeEditor.cpp EnvelopeEditor.h Experimental.h FFmpeg.cpp \ + FFmpeg.h FFT.cpp FFT.h FileException.cpp FileException.h \ + FileIO.cpp FileIO.h FileNames.cpp FileNames.h float_cast.h \ + FreqWindow.cpp FreqWindow.h HelpText.cpp HelpText.h \ + HistoryWindow.cpp HistoryWindow.h HitTestResult.h \ + ImageManipulation.cpp ImageManipulation.h \ + InconsistencyException.cpp InconsistencyException.h \ + InterpolateAudio.cpp InterpolateAudio.h KeyboardCapture.cpp \ + KeyboardCapture.h LabelDialog.cpp LabelDialog.h LabelTrack.cpp \ + LabelTrack.h LangChoice.cpp LangChoice.h Languages.cpp \ + Languages.h Legacy.cpp Legacy.h Lyrics.cpp Lyrics.h \ + LyricsWindow.cpp LyricsWindow.h MacroMagic.h Matrix.cpp \ + Matrix.h MemoryX.h Menus.cpp Menus.h \ + MissingAliasFileDialog.cpp MissingAliasFileDialog.h Mix.cpp \ + Mix.h MixerBoard.cpp MixerBoard.h ModuleManager.cpp \ + ModuleManager.h NumberScale.h PitchName.cpp PitchName.h \ + PlatformCompatibility.cpp PlatformCompatibility.h \ + PluginManager.cpp PluginManager.h Printing.cpp Printing.h \ + Profiler.cpp Profiler.h Project.cpp Project.h \ + ProjectAudioIO.cpp ProjectAudioIO.h ProjectAudioManager.cpp \ + ProjectAudioManager.h ProjectFileIO.cpp ProjectFileIO.h \ + ProjectFileIORegistry.cpp ProjectFileIORegistry.h \ + ProjectFileManager.cpp ProjectFileManager.h ProjectFSCK.cpp \ + ProjectFSCK.h ProjectHistory.cpp ProjectHistory.h \ + ProjectManager.cpp ProjectManager.h \ + ProjectSelectionManager.cpp ProjectSelectionManager.h \ + ProjectSettings.cpp ProjectSettings.h ProjectWindow.cpp \ + ProjectWindow.h RealFFTf.cpp RealFFTf.h RealFFTf48x.cpp \ + RealFFTf48x.h RefreshCode.h Resample.cpp Resample.h \ + RevisionIdent.h RingBuffer.cpp RingBuffer.h Screenshot.cpp \ + Screenshot.h SelectedRegion.cpp SelectedRegion.h \ + SelectionState.cpp SelectionState.h Shuttle.cpp Shuttle.h \ ShuttleGetDefinition.cpp ShuttleGetDefinition.h ShuttleGui.cpp \ ShuttleGui.h ShuttlePrefs.cpp ShuttlePrefs.h Snap.cpp Snap.h \ SoundActivatedRecord.cpp SoundActivatedRecord.h Spectrum.cpp \ @@ -2539,6 +2539,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-DirManager.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-Dither.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-Envelope.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-EnvelopeEditor.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-FFT.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-FFmpeg.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-FileException.Po@am__quote@ @@ -3566,6 +3567,20 @@ audacity-Envelope.obj: Envelope.cpp @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -c -o audacity-Envelope.obj `if test -f 'Envelope.cpp'; then $(CYGPATH_W) 'Envelope.cpp'; else $(CYGPATH_W) '$(srcdir)/Envelope.cpp'; fi` +audacity-EnvelopeEditor.o: EnvelopeEditor.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -MT audacity-EnvelopeEditor.o -MD -MP -MF $(DEPDIR)/audacity-EnvelopeEditor.Tpo -c -o audacity-EnvelopeEditor.o `test -f 'EnvelopeEditor.cpp' || echo '$(srcdir)/'`EnvelopeEditor.cpp +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/audacity-EnvelopeEditor.Tpo $(DEPDIR)/audacity-EnvelopeEditor.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='EnvelopeEditor.cpp' object='audacity-EnvelopeEditor.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -c -o audacity-EnvelopeEditor.o `test -f 'EnvelopeEditor.cpp' || echo '$(srcdir)/'`EnvelopeEditor.cpp + +audacity-EnvelopeEditor.obj: EnvelopeEditor.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -MT audacity-EnvelopeEditor.obj -MD -MP -MF $(DEPDIR)/audacity-EnvelopeEditor.Tpo -c -o audacity-EnvelopeEditor.obj `if test -f 'EnvelopeEditor.cpp'; then $(CYGPATH_W) 'EnvelopeEditor.cpp'; else $(CYGPATH_W) '$(srcdir)/EnvelopeEditor.cpp'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/audacity-EnvelopeEditor.Tpo $(DEPDIR)/audacity-EnvelopeEditor.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='EnvelopeEditor.cpp' object='audacity-EnvelopeEditor.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -c -o audacity-EnvelopeEditor.obj `if test -f 'EnvelopeEditor.cpp'; then $(CYGPATH_W) 'EnvelopeEditor.cpp'; else $(CYGPATH_W) '$(srcdir)/EnvelopeEditor.cpp'; fi` + audacity-FFmpeg.o: FFmpeg.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -MT audacity-FFmpeg.o -MD -MP -MF $(DEPDIR)/audacity-FFmpeg.Tpo -c -o audacity-FFmpeg.o `test -f 'FFmpeg.cpp' || echo '$(srcdir)/'`FFmpeg.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/audacity-FFmpeg.Tpo $(DEPDIR)/audacity-FFmpeg.Po diff --git a/src/TimeTrack.cpp b/src/TimeTrack.cpp index 4d85b828b..c4f235c9d 100644 --- a/src/TimeTrack.cpp +++ b/src/TimeTrack.cpp @@ -25,6 +25,7 @@ #include "AColor.h" #include "widgets/Ruler.h" #include "Envelope.h" +#include "EnvelopeEditor.h" #include "Prefs.h" #include "Project.h" #include "ProjectSettings.h" @@ -317,8 +318,8 @@ void TimeTrack::Draw mRuler->Draw(dc, this); Doubles envValues{ size_t(mid.width) }; - GetEnvelope()->GetValues - ( 0, 0, envValues.get(), mid.width, 0, zoomInfo ); + EnvelopeEditor::GetValues( *GetEnvelope(), + 0, 0, envValues.get(), mid.width, 0, zoomInfo ); wxPen &pen = highlight ? AColor::uglyPen : AColor::envelopePen; dc.SetPen( pen ); diff --git a/src/TrackArtist.cpp b/src/TrackArtist.cpp index 057d0d849..456f7772d 100644 --- a/src/TrackArtist.cpp +++ b/src/TrackArtist.cpp @@ -72,6 +72,7 @@ audio tracks. #include "AColor.h" #include "BlockFile.h" #include "Envelope.h" +#include "EnvelopeEditor.h" #include "NumberScale.h" #include "WaveClip.h" #include "LabelTrack.h" @@ -1809,14 +1810,14 @@ void TrackArt::DrawClipWaveform(TrackPanelDrawingContext &context, std::vector vEnv(mid.width); double *const env = &vEnv[0]; - clip->GetEnvelope()->GetValues - ( tOffset, + EnvelopeEditor::GetValues( *clip->GetEnvelope(), + tOffset, - // PRL: change back to make envelope evaluate only at sample times - // and then interpolate the display - 0, // 1.0 / rate, + // PRL: change back to make envelope evaluate only at sample times + // and then interpolate the display + 0, // 1.0 / rate, - env, mid.width, leftOffset, zoomInfo ); + env, mid.width, leftOffset, zoomInfo ); // Draw the background of the track, outlining the shape of // the envelope and using a colored pen for the selected @@ -1949,14 +1950,14 @@ void TrackArt::DrawClipWaveform(TrackPanelDrawingContext &context, if (!showIndividualSamples) { std::vector vEnv2(rectPortion.width); double *const env2 = &vEnv2[0]; - clip->GetEnvelope()->GetValues - ( tOffset, + EnvelopeEditor::GetValues( *clip->GetEnvelope(), + tOffset, - // PRL: change back to make envelope evaluate only at sample times - // and then interpolate the display - 0, // 1.0 / rate, + // PRL: change back to make envelope evaluate only at sample times + // and then interpolate the display + 0, // 1.0 / rate, - env2, rectPortion.width, leftOffset, zoomInfo ); + env2, rectPortion.width, leftOffset, zoomInfo ); DrawMinMaxRMS( context, rectPortion, env2, zoomMin, zoomMax, dB, dBRange, @@ -1984,8 +1985,8 @@ void TrackArt::DrawClipWaveform(TrackPanelDrawingContext &context, if (drawEnvelope) { DrawEnvelope( context, mid, env, zoomMin, zoomMax, dB, dBRange, highlightEnvelope ); - clip->GetEnvelope()->DrawPoints - ( context, rect, dB, dBRange, zoomMin, zoomMax, true ); + EnvelopeEditor::DrawPoints( *clip->GetEnvelope(), + context, rect, dB, dBRange, zoomMin, zoomMax, true ); } // Draw arrows on the left side if the track extends to the left of the @@ -3262,9 +3263,9 @@ void TrackArt::DrawTimeTrack(TrackPanelDrawingContext &context, lower = LINEAR_TO_DB(std::max(1.0e-7, lower)) / dbRange + 1.0; upper = LINEAR_TO_DB(std::max(1.0e-7, upper)) / dbRange + 1.0; } - track->GetEnvelope()->DrawPoints - ( context, envRect, - track->GetDisplayLog(), dbRange, lower, upper, false ); + EnvelopeEditor::DrawPoints( *track->GetEnvelope(), + context, envRect, + track->GetDisplayLog(), dbRange, lower, upper, false ); } void TrackArtist::UpdateSelectedPrefs( int id ) diff --git a/src/effects/Equalization.cpp b/src/effects/Equalization.cpp index 3bd7379d7..3151dfb80 100644 --- a/src/effects/Equalization.cpp +++ b/src/effects/Equalization.cpp @@ -93,6 +93,7 @@ #include "../PlatformCompatibility.h" #include "../FileNames.h" #include "../Envelope.h" +#include "../EnvelopeEditor.h" #include "../widgets/LinkingHtmlWindow.h" #include "../widgets/ErrorDialog.h" #include "../FFT.h" @@ -3113,7 +3114,7 @@ void EqualizationPanel::OnPaint(wxPaintEvent & WXUNUSED(event)) artist.pZoomInfo = &zoomInfo; TrackPanelDrawingContext context{ memDC, {}, {}, &artist }; - mEffect->mEnvelope->DrawPoints( + EnvelopeEditor::DrawPoints( *mEffect->mEnvelope, context, mEnvRect, false, 0.0, mEffect->mdBMin, mEffect->mdBMax, false); } diff --git a/src/tracks/ui/EnvelopeHandle.cpp b/src/tracks/ui/EnvelopeHandle.cpp index 98e63727d..8d94a59d5 100644 --- a/src/tracks/ui/EnvelopeHandle.cpp +++ b/src/tracks/ui/EnvelopeHandle.cpp @@ -14,6 +14,7 @@ Paul Licameli split from TrackPanel.cpp #include "../../Experimental.h" #include "../../Envelope.h" +#include "../../EnvelopeEditor.h" #include "../../HitTestResult.h" #include "../../prefs/WaveformSettings.h" #include "../../ProjectAudioIO.h" diff --git a/win/Projects/Audacity/Audacity.vcxproj b/win/Projects/Audacity/Audacity.vcxproj index d07c11303..28749d3bf 100755 --- a/win/Projects/Audacity/Audacity.vcxproj +++ b/win/Projects/Audacity/Audacity.vcxproj @@ -164,6 +164,7 @@ + @@ -624,6 +625,7 @@ + diff --git a/win/Projects/Audacity/Audacity.vcxproj.filters b/win/Projects/Audacity/Audacity.vcxproj.filters index b141a7716..55f74aa69 100755 --- a/win/Projects/Audacity/Audacity.vcxproj.filters +++ b/win/Projects/Audacity/Audacity.vcxproj.filters @@ -164,6 +164,9 @@ src + + src + src @@ -1249,6 +1252,9 @@ src + + src + src