X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fcanvas%2Fwave_view.cc;h=4ea9ff9fd31fe6c60349b4030981f40fe5a7d6b5;hb=580520b12f90251e38d94a1a5d6a199b76142b0b;hp=0248b9ddaabb090b1ee514f48be60d4a6cbb08de;hpb=c6fe6b09965f78a89d29f79980b8f0bcbbeddba0;p=ardour.git diff --git a/libs/canvas/wave_view.cc b/libs/canvas/wave_view.cc index 0248b9ddaa..4ea9ff9fd3 100644 --- a/libs/canvas/wave_view.cc +++ b/libs/canvas/wave_view.cc @@ -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 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 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 */ + } +}