fix conflicts caused by meterbridge merge
[ardour.git] / libs / canvas / wave_view.cc
index 0248b9ddaabb090b1ee514f48be60d4a6cbb08de..4ea9ff9fd31fe6c60349b4030981f40fe5a7d6b5 100644 (file)
@@ -44,6 +44,7 @@ using namespace ArdourCanvas;
 double WaveView::_global_gradient_depth = 0.6;
 bool WaveView::_global_logscaled = false;
 WaveView::Shape WaveView::_global_shape = WaveView::Normal;
+bool WaveView::_global_show_waveform_clipping = true;
 
 PBD::Signal0<void> WaveView::VisualPropertiesChanged;
 
@@ -163,9 +164,10 @@ alt_log_meter (float power)
 struct LineTips {
     double top;
     double bot;
-    bool clipped;
+    bool clip_max;
+    bool clip_min;
     
-    LineTips() : top (0.0), bot (0.0), clipped (false) {}
+    LineTips() : top (0.0), bot (0.0), clip_max (false), clip_min (false) {}
 };
 
 void
@@ -177,6 +179,18 @@ WaveView::draw_image (PeakData* _peaks, int n_peaks) const
 
        boost::scoped_array<LineTips> tips (new LineTips[n_peaks]);
 
+       /* Clip level nominally set to -0.9dBFS to account for inter-sample
+          interpolation possibly clipping (value may be too low).
+
+          We adjust by the region's own gain (but note: not by any gain
+          automation or its gain envelope) so that clip indicators are closer
+          to providing data about on-disk data. This multiplication is
+          needed because the data we get from AudioRegion::read_peaks()
+          has been scaled by scale_amplitude() already.
+       */
+
+       const double clip_level = 0.98853 * _region->scale_amplitude();
+
        if (_shape == WaveView::Rectified) {
 
                /* each peak is a line from the bottom of the waveview
@@ -188,10 +202,26 @@ WaveView::draw_image (PeakData* _peaks, int n_peaks) const
                        for (int i = 0; i < n_peaks; ++i) {
                                tips[i].bot = height();
                                tips[i].top = position (alt_log_meter (fast_coefficient_to_dB (max (fabs (_peaks[i].max), fabs (_peaks[i].min)))));
+
+                               if (fabs (_peaks[i].max) >= clip_level) {
+                                       tips[i].clip_max = true;
+                               }
+
+                               if (fabs (_peaks[i].min) >= clip_level) {
+                                       tips[i].clip_min = true;
+                               }
                        }
                } else {for (int i = 0; i < n_peaks; ++i) {
                                tips[i].bot = height();
                                tips[i].top = position (max (fabs (_peaks[i].max), fabs (_peaks[i].min)));
+
+                               if (fabs (_peaks[i].max) >= clip_level) {
+                                       tips[i].clip_max = true;
+                               }
+
+                               if (fabs (_peaks[i].min) >= clip_level) {
+                                       tips[i].clip_min = true;
+                               }
                        }
                }
 
@@ -202,6 +232,14 @@ WaveView::draw_image (PeakData* _peaks, int n_peaks) const
                                Coord top = _peaks[i].min;
                                Coord bot = _peaks[i].max;
 
+                               if (fabs (top) >= clip_level) {
+                                       tips[i].clip_max = true;
+                               }
+
+                               if (fabs (bot) >= clip_level) {
+                                       tips[i].clip_min = true;
+                               }
+
                                if (top > 0.0) {
                                        top = position (alt_log_meter (fast_coefficient_to_dB (top)));
                                } else if (top < 0.0) {
@@ -220,13 +258,23 @@ WaveView::draw_image (PeakData* _peaks, int n_peaks) const
 
                                tips[i].top = top;
                                tips[i].bot = bot;
-
                        } 
 
                } else {
                        for (int i = 0; i < n_peaks; ++i) {
+
+                               if (fabs (_peaks[i].max) >= clip_level) {
+                                       tips[i].clip_max = true;
+                               }
+
+                               if (fabs (_peaks[i].min) >= clip_level) {
+                                       tips[i].clip_min = true;
+                               }
+
                                tips[i].top = position (_peaks[i].min);
                                tips[i].bot = position (_peaks[i].max);
+
+
                        }
                }
        }
@@ -292,16 +340,43 @@ WaveView::draw_image (PeakData* _peaks, int n_peaks) const
         * modelled on pyramix, except that we add clipping indicators.
         */
 
-       context->set_source_rgba (0, 0, 0, 1.0);
+       if (_global_show_waveform_clipping) {
+               
+               context->set_source_rgba (0, 0, 0, 1.0);
+               
+               /* the height of the clip-indicator should be at most 7 pixels,
+                  or 5% of the height of the waveview item.
+               */
+               const double clip_height = min (7.0, ceil (_height * 0.05));
+               
+               for (int i = 0; i < n_peaks; ++i) {
+                       context->move_to (i, tips[i].top);
+                       
+                       bool show_top_clip = (_shape == WaveView::Rectified && (tips[i].clip_max || tips[i].clip_min)) ||
+                               tips[i].clip_max;
+                       
+                       if (show_top_clip) {
+                               context->set_source_rgba (1.0, 0, 0, 1.0);
+                               context->rel_line_to (0, clip_height);
+                               context->stroke ();
+                               context->set_source_rgba (0.0, 0, 0, 1.0);
+                       } else {
+                               context->rel_line_to (0, 1.0);
+                               context->stroke ();
+                       }
 
-       for (int i = 0; i < n_peaks; ++i) {
-               context->move_to (i, tips[i].top);
-               context->rel_line_to (0, 1.0);
-               context->stroke ();
-               if (_shape != WaveView::Rectified) {
-                       context->move_to (i, tips[i].bot);
-                       context->rel_line_to (0, -1.0);
-                       context->stroke ();
+                       if (_shape != WaveView::Rectified) {
+                               context->move_to (i, tips[i].bot);
+                               if (tips[i].clip_min) {
+                                       context->set_source_rgba (1.0, 0, 0, 1.0);
+                                       context->rel_line_to (0, -clip_height);
+                                       context->stroke ();
+                                       context->set_source_rgba (0.0, 0, 0, 1.0);
+                               } else {
+                                       context->rel_line_to (0, -1.0);
+                                       context->stroke ();
+                               }
+                       }
                }
        }
 
@@ -581,13 +656,17 @@ WaveView::position (double s) const
           can ensure correct single pixel behaviour.
         */
 
+       Coord pos;
+
        switch (_shape) {
        case Rectified:
-               return floor (_height - (s * _height));
+               pos = floor (_height - (s * _height));
        default:
+               pos = floor ((1.0-s) * (_height / 2.0));
                break;
        }
-       return floor ((1.0-s) * (_height / 2.0));
+
+       return min (_height, (max (0.0, pos)));
 }
 
 void
@@ -598,3 +677,12 @@ WaveView::set_global_gradient_depth (double depth)
                VisualPropertiesChanged (); /* EMIT SIGNAL */
        }
 }
+
+void
+WaveView::set_global_show_waveform_clipping (bool yn)
+{
+       if (_global_show_waveform_clipping != yn) {
+               _global_show_waveform_clipping = yn;
+               VisualPropertiesChanged (); /* EMIT SIGNAL */
+       }
+}