Audacity "connects the dots" when you zoom in

Effects, Recipes, Interfacing with other software, etc.
Forum rules
If you require help using Audacity, please post on the forum board relevant to your operating system:
Windows
Mac OS X
GNU/Linux and Unix-like
steve
Site Admin
Posts: 81627
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: Audacity "connects the dots" when you zoom in

Post by steve » Mon Feb 20, 2017 12:23 am

brian-armstrong wrote:I believe these are often called Stem plots.
That's a nice descriptive name.

I think my favourite would be "sinc interpolation", which has the benefit of giving an indication of the "true peak level", though I've not yet attempted to code it.

(this is a mock up / simulation)
Image
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)

steve
Site Admin
Posts: 81627
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: Audacity "connects the dots" when you zoom in

Post by steve » Mon Feb 20, 2017 1:26 pm

brian-armstrong wrote:Hm, I think that's a good improvement. I do like the stem version at one zoom level up from there, though.
Do you have code for that?
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)

brian-armstrong
Posts: 18
Joined: Sat Feb 18, 2017 12:30 am
Operating System: macOS 10.15 Catalina or later

Re: Audacity "connects the dots" when you zoom in

Post by brian-armstrong » Tue Feb 21, 2017 9:19 am

steve wrote: Do you have code for that?
I changed where the zoom engages, fixed a bug with the stems not going to proper zero, and I changed zooming to lock in to a multiple of the project's sample rate. This gets rid of that aliasing.

Code: Select all

diff --git a/src/TrackArtist.cpp b/src/TrackArtist.cpp
index 7d4385d..2c116ad 100644
--- a/src/TrackArtist.cpp
+++ b/src/TrackArtist.cpp
@@ -1308,6 +1308,7 @@ void TrackArtist::DrawMinMaxRMS(wxDC &dc, const wxRect & rect, const double env[
 
 void TrackArtist::DrawIndividualSamples(wxDC &dc, int leftOffset, const wxRect &rect,
                                         float zoomMin, float zoomMax,
+                                        int zeroLevelYCoordinate,
                                         bool dB, float dBRange,
                                         const WaveClip *clip,
                                         const ZoomInfo &zoomInfo,
@@ -1363,12 +1364,13 @@ void TrackArtist::DrawIndividualSamples(wxDC &dc, int leftOffset, const wxRect &
    }
 
    // Draw lines
-   for (decltype(slen) s = 0; s < slen - 1; s++) {
+   for (decltype(slen) s = 0; s < slen; s++) {
       AColor::Line(dc,
                    rect.x + xpos[s], rect.y + ypos[s],
-                   rect.x + xpos[s + 1], rect.y + ypos[s + 1]);
+                   rect.x + xpos[s], zeroLevelYCoordinate);
    }
 
+
    if (showPoints)
    {
       // Draw points where spacing is enough
@@ -1808,7 +1810,7 @@ void TrackArtist::DrawClipWaveform(const WaveTrack *track,
    const unsigned nPortions = portions.size();
 
    // Require at least 1/2 pixel per sample for drawing individual samples.
-   const double threshold1 = 0.5 * rate;
+   const double threshold1 = 1.0 * rate;
    // Require at least 3 pixels per sample for drawing the draggable points.
    const double threshold2 = 3 * rate;
 
@@ -1917,6 +1919,7 @@ void TrackArtist::DrawClipWaveform(const WaveTrack *track,
          }
          else
             DrawIndividualSamples(dc, leftOffset, rect, zoomMin, zoomMax,
+               track->ZeroLevelYCoordinate(mid),
                dB, dBRange,
                clip, zoomInfo,
                bigPoints, showPoints, muted);
diff --git a/src/TrackArtist.h b/src/TrackArtist.h
index f5c76ae..aa1acb7 100644
--- a/src/TrackArtist.h
+++ b/src/TrackArtist.h
@@ -157,6 +157,7 @@ class AUDACITY_DLL_API TrackArtist {
    );
    void DrawIndividualSamples(wxDC & dc, int leftOffset, const wxRect & rect,
                               float zoomMin, float zoomMax,
+                              int zeroLevelYCoordinate,
                               bool dB, float dBRange,
                               const WaveClip *clip,
                               const ZoomInfo &zoomInfo,
diff --git a/src/ViewInfo.cpp b/src/ViewInfo.cpp
index 089554e..6580374 100644
--- a/src/ViewInfo.cpp
+++ b/src/ViewInfo.cpp
@@ -90,6 +90,14 @@ bool ZoomInfo::ZoomOutAvailable() const
 void ZoomInfo::SetZoom(double pixelsPerSecond)
 {
    zoom = std::max(gMinZoom, std::min(gMaxZoom, pixelsPerSecond));
+   AudacityProject * project = GetActiveProject();
+   if ( project ) {
+      double samplesPerSecond = project->GetRate();
+      double pixelsPerSample = pixelsPerSecond/samplesPerSecond;
+      if ( pixelsPerSample > 1 ) {
+         zoom = floor(pixelsPerSample) * samplesPerSecond;
+      }
+   }
 #ifdef EXPERIMENTAL_DA
    // Disable snapping if user zooms in a long way.
    // Helps stop users be trapped in snap-to.
Attachments
Screen Shot 2017-02-21 at 1.16.13 AM.png
Screen Shot 2017-02-21 at 1.16.13 AM.png (13.99 KiB) Viewed 984 times

steve
Site Admin
Posts: 81627
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: Audacity "connects the dots" when you zoom in

Post by steve » Wed Feb 22, 2017 3:12 pm

Unfortunately you've broken something and created a bug. "Zoom to Fit" (Ctrl+E) no longer works reliably.

Example:
With default preferences, generate 10 second sine wave.
From the selection Toolbar, select from 0 to 437 samples.
Zoom to fit.
Depending on the window size, the selection probably does not fit the window.
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)

brian-armstrong
Posts: 18
Joined: Sat Feb 18, 2017 12:30 am
Operating System: macOS 10.15 Catalina or later

Re: Audacity "connects the dots" when you zoom in

Post by brian-armstrong » Thu Feb 23, 2017 8:21 am

steve wrote:Unfortunately you've broken something and created a bug. "Zoom to Fit" (Ctrl+E) no longer works reliably.

Example:
With default preferences, generate 10 second sine wave.
From the selection Toolbar, select from 0 to 437 samples.
Zoom to fit.
Depending on the window size, the selection probably does not fit the window.
This is true, you can't reliably select based on time with this.

Would it make sense to refer to it as a 'snap to' behavior? Although it's different from Audacity's typical behavior, it seems like the concept of equally spaced samples is a consistent idea, but one which is inherently inconsistent with perfectly filling a space 'x' pixels wide with 'y' samples.

steve
Site Admin
Posts: 81627
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: Audacity "connects the dots" when you zoom in

Post by steve » Thu Feb 23, 2017 9:42 am

Perhaps you're not seeing what I'm seeing. I don't just mean that it's not snapping to the sample boundary. I mean it's way off.

Here is the result of the described steps with un-patched Audacity 2.1.3 RC2 version. Note that the selection fits almost exactly in the available window area:
fullwindow001.png
fullwindow001.png (63 KiB) Viewed 955 times
And here is the result with the patched version. Note that the selection does not fill the window:
fullwindow002.png
fullwindow002.png (57.94 KiB) Viewed 955 times
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)

brian-armstrong
Posts: 18
Joined: Sat Feb 18, 2017 12:30 am
Operating System: macOS 10.15 Catalina or later

Re: Audacity "connects the dots" when you zoom in

Post by brian-armstrong » Thu Feb 23, 2017 10:52 am

steve wrote:Perhaps you're not seeing what I'm seeing. I don't just mean that it's not snapping to the sample boundary. I mean it's way off.

Here is the result of the described steps with un-patched Audacity 2.1.3 RC2 version. Note that the selection fits almost exactly in the available window area:
fullwindow001.png
And here is the result with the patched version. Note that the selection does not fill the window:
fullwindow002.png
To be clear, what we're snapping to with this patch is the number of pixels between each sample, not the samples themselves. To do some napkin math, if the track in your image were 800 pixels wide, it's filled by ~0.012 seconds, which comes out to approximately 70,000 pixels/second. At a sample rate of 44,100 samples/second, (pixels/second)/(samples/second) = 1.51 pixels/sample. Since this only considers integer pixels/sample, it'd either have to reduce to 1 pixel/sample spacing, which would leave a large gap like the one you've posted, or it could move up to 2 pixels/sample, which would push the selection past the end of the screen.

You could probably mitigate this by changing the threshold where the "snap" to integer ratios of pixels/sample starts happening from 1 to 4. I've put the patch for that here. Looking at the example you've given, at a sample rate of 48000Hz, it looks like the snapping begins at [0, 290] samples and becomes most dramatic at something like [0, 232] samples. Still, I'd argue that this could be correct behavior if the user wanted equally spaced samples.

Code: Select all

diff --git a/src/TrackArtist.cpp b/src/TrackArtist.cpp
index 7d4385d..8fcaf9a 100644
--- a/src/TrackArtist.cpp
+++ b/src/TrackArtist.cpp
@@ -1308,6 +1308,7 @@ void TrackArtist::DrawMinMaxRMS(wxDC &dc, const wxRect & rect, const double env[
 
 void TrackArtist::DrawIndividualSamples(wxDC &dc, int leftOffset, const wxRect &rect,
                                         float zoomMin, float zoomMax,
+                                        int zeroLevelYCoordinate,
                                         bool dB, float dBRange,
                                         const WaveClip *clip,
                                         const ZoomInfo &zoomInfo,
@@ -1363,12 +1364,13 @@ void TrackArtist::DrawIndividualSamples(wxDC &dc, int leftOffset, const wxRect &
    }
 
    // Draw lines
-   for (decltype(slen) s = 0; s < slen - 1; s++) {
+   for (decltype(slen) s = 0; s < slen; s++) {
       AColor::Line(dc,
                    rect.x + xpos[s], rect.y + ypos[s],
-                   rect.x + xpos[s + 1], rect.y + ypos[s + 1]);
+                   rect.x + xpos[s], zeroLevelYCoordinate);
    }
 
+
    if (showPoints)
    {
       // Draw points where spacing is enough
@@ -1807,17 +1809,17 @@ void TrackArtist::DrawClipWaveform(const WaveTrack *track,
    FindWavePortions(portions, rect, zoomInfo, params);
    const unsigned nPortions = portions.size();
 
-   // Require at least 1/2 pixel per sample for drawing individual samples.
-   const double threshold1 = 0.5 * rate;
-   // Require at least 3 pixels per sample for drawing the draggable points.
-   const double threshold2 = 3 * rate;
+   // Require at least 4 pixels per sample for drawing individual samples.
+   const double threshold1 = 4 * rate;
+   // Require at least 4 pixels per sample for drawing the draggable points.
+   const double threshold2 = 4 * rate;
 
    {
       bool showIndividualSamples = false;
       for (unsigned ii = 0; !showIndividualSamples && ii < nPortions; ++ii) {
          const WavePortion &portion = portions[ii];
          showIndividualSamples =
-            !portion.inFisheye && portion.averageZoom > threshold1;
+            !portion.inFisheye && portion.averageZoom >= threshold1;
       }
 
       if (!showIndividualSamples) {
@@ -1839,8 +1841,8 @@ void TrackArtist::DrawClipWaveform(const WaveTrack *track,
 
    for (unsigned ii = 0; ii < nPortions; ++ii) {
       WavePortion &portion = portions[ii];
-      const bool showIndividualSamples = portion.averageZoom > threshold1;
-      const bool showPoints = portion.averageZoom > threshold2;
+      const bool showIndividualSamples = portion.averageZoom >= threshold1;
+      const bool showPoints = portion.averageZoom >= threshold2;
       wxRect& rect = portion.rect;
       rect.Intersect(mid);
       wxASSERT(rect.width >= 0);
@@ -1917,6 +1919,7 @@ void TrackArtist::DrawClipWaveform(const WaveTrack *track,
          }
          else
             DrawIndividualSamples(dc, leftOffset, rect, zoomMin, zoomMax,
+               track->ZeroLevelYCoordinate(mid),
                dB, dBRange,
                clip, zoomInfo,
                bigPoints, showPoints, muted);
diff --git a/src/TrackArtist.h b/src/TrackArtist.h
index f5c76ae..aa1acb7 100644
--- a/src/TrackArtist.h
+++ b/src/TrackArtist.h
@@ -157,6 +157,7 @@ class AUDACITY_DLL_API TrackArtist {
    );
    void DrawIndividualSamples(wxDC & dc, int leftOffset, const wxRect & rect,
                               float zoomMin, float zoomMax,
+                              int zeroLevelYCoordinate,
                               bool dB, float dBRange,
                               const WaveClip *clip,
                               const ZoomInfo &zoomInfo,
diff --git a/src/ViewInfo.cpp b/src/ViewInfo.cpp
index 089554e..fdc8485 100644
--- a/src/ViewInfo.cpp
+++ b/src/ViewInfo.cpp
@@ -90,6 +90,15 @@ bool ZoomInfo::ZoomOutAvailable() const
 void ZoomInfo::SetZoom(double pixelsPerSecond)
 {
    zoom = std::max(gMinZoom, std::min(gMaxZoom, pixelsPerSecond));
+   AudacityProject * project = GetActiveProject();
+   if ( project ) {
+      double samplesPerSecond = project->GetRate();
+      double pixelsPerSample = pixelsPerSecond/samplesPerSecond;
+      if ( pixelsPerSample >= 4 ) {
+         zoom = floor(pixelsPerSample) * samplesPerSecond;
+      }
+   }
 #ifdef EXPERIMENTAL_DA
    // Disable snapping if user zooms in a long way.
    // Helps stop users be trapped in snap-to.

steve
Site Admin
Posts: 81627
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: Audacity "connects the dots" when you zoom in

Post by steve » Thu Feb 23, 2017 11:42 am

brian-armstrong wrote:You could probably mitigate this by ....
"I" could do ...? Oh no no no. ;) I wouldn't dream of interfering with your project. I'm just offering feedback. I've got plenty of my own work to do.
brian-armstrong wrote:Still, I'd argue that this could be correct behavior if the user wanted equally spaced samples.
OK, I can see what you are doing, but I think it highly unlikely that this would be accepted into the main code base with the "apparent" breakage of "Fit Selection".
On the other hand, I think that the basic idea of "stem plots" as an "option" for displaying wave data at sample level, could be accepted, if implemented in a way that is in keeping with Audacity as a whole. (though I'd add that it's not my decision ;)).
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)

brian-armstrong
Posts: 18
Joined: Sat Feb 18, 2017 12:30 am
Operating System: macOS 10.15 Catalina or later

Re: Audacity "connects the dots" when you zoom in

Post by brian-armstrong » Fri Feb 24, 2017 3:29 am

steve wrote:
brian-armstrong wrote:You could probably mitigate this by ....
"I" could do ...? Oh no no no. ;) I wouldn't dream of interfering with your project. I'm just offering feedback. I've got plenty of my own work to do.
I'm not sure I understand this comment since the code I included has this change. Wasn't it clear I meant it in a general "you"?
steve wrote:
brian-armstrong wrote:Still, I'd argue that this could be correct behavior if the user wanted equally spaced samples.
OK, I can see what you are doing, but I think it highly unlikely that this would be accepted into the main code base with the "apparent" breakage of "Fit Selection".
On the other hand, I think that the basic idea of "stem plots" as an "option" for displaying wave data at sample level, could be accepted, if implemented in a way that is in keeping with Audacity as a whole. (though I'd add that it's not my decision ;)).
Rendering the samples with uneven spacing is inherently misrepresentative of the underlying data. This is true for the existing lines plot but it's exacerbated by the stem plot. Isn't this something that could be gated behind a user option?

steve
Site Admin
Posts: 81627
Joined: Sat Dec 01, 2007 11:43 am
Operating System: Linux *buntu

Re: Audacity "connects the dots" when you zoom in

Post by steve » Fri Feb 24, 2017 3:46 pm

brian-armstrong wrote:I'm not sure I understand this comment since the code I included has this change. Wasn't it clear I meant it in a general "you"?
No offence intended or taken - just checking that we're both clear.
9/10 questions are answered in the FREQUENTLY ASKED QUESTIONS (FAQ)

Post Reply