*/
+#include <cmath>
#include <cairomm/cairomm.h>
#include "gtkmm2ext/utils.h"
#include "pbd/signals.h"
#include "ardour/types.h"
+#include "ardour/dB.h"
#include "ardour/audioregion.h"
#include "canvas/wave_view.h"
#include "canvas/utils.h"
+#include "canvas/canvas.h"
#include <gdkmm/general.h>
using namespace ARDOUR;
using namespace ArdourCanvas;
+bool WaveView::_gradient_waveforms = true;
+PBD::Signal0<void> WaveView::InvalidateAllImages;
+
WaveView::WaveView (Group* parent, boost::shared_ptr<ARDOUR::AudioRegion> region)
: Item (parent)
, Outline (parent)
, _samples_per_pixel (0)
, _height (64)
, _wave_color (0xffffffff)
+ , _show_zero (true)
+ , _zero_color (0xff0000ff)
+ , _shape (Normal)
+ , _clip_color (0xff0000ff)
+ , _logscaled (false)
+ , _amplitude (1.0)
, _region_start (0)
{
-
+ InvalidateAllImages.connect_same_thread (invalidation_connection, boost::bind (&WaveView::rebuild, this));
+}
+
+void
+WaveView::rebuild ()
+{
+ invalidate_image_cache ();
+ _canvas->item_visual_property_changed (this);
}
void
_bounding_box_dirty = true;
}
+void
+WaveView::set_logscaled (bool yn)
+{
+ if (_logscaled != yn) {
+ _logscaled = yn;
+ invalidate_whole_cache ();
+ _canvas->item_visual_property_changed (this);
+ }
+}
+
+void
+WaveView::set_amplitude (double a)
+{
+ if (_amplitude != a) {
+ _amplitude = a;
+ invalidate_whole_cache ();
+ _canvas->item_visual_property_changed (this);
+ }
+}
+
+void
+WaveView::set_zero_color (Color c)
+{
+ if (_zero_color != c) {
+ _zero_color = c;
+ invalidate_whole_cache ();
+ _canvas->item_visual_property_changed (this);
+ }
+}
+
+void
+WaveView::set_clip_color (Color c)
+{
+ if (_clip_color != c) {
+ _clip_color = c;
+ invalidate_whole_cache ();
+ _canvas->item_visual_property_changed (this);
+ }
+}
+
+void
+WaveView::set_show_zero_line (bool yn)
+{
+ if (_show_zero != yn) {
+ _show_zero = yn;
+ invalidate_whole_cache ();
+ _canvas->item_visual_property_changed (this);
+ }
+}
+
+void
+WaveView::set_shape (Shape s)
+{
+ if (_shape != s) {
+ _shape = s;
+ _canvas->item_visual_property_changed (this);
+ }
+}
+
void
WaveView::set_region_start (frameoffset_t start)
{
{
}
+static inline float
+_log_meter (float power, double lower_db, double upper_db, double non_linearity)
+{
+ return (power < lower_db ? 0.0 : pow((power-lower_db)/(upper_db-lower_db), non_linearity));
+}
+
+static inline float
+alt_log_meter (float power)
+{
+ return _log_meter (power, -192.0, 0.0, 8.0);
+}
+
Cairo::RefPtr<Cairo::ImageSurface>
WaveView::CacheEntry::image ()
{
_image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, _n_peaks, _wave_view->_height);
Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create (_image);
- _wave_view->setup_outline_context (context);
- context->move_to (0.5, position (_peaks[0].min));
- for (int i = 1; i < _n_peaks; ++i) {
- context->line_to (i + 0.5, position (_peaks[i].max));
+ /* Draw the edge of the waveform, top half first, the loop back
+ * for the bottom half to create a clockwise path
+ */
+
+ context->begin_new_path();
+
+ if (_wave_view->_logscaled) {
+ for (int i = 0; i < _n_peaks; ++i) {
+ Coord y = _peaks[i].max;
+ if (y > 0.0) {
+ context->line_to (i + 0.5, position (_wave_view->amplitude() * alt_log_meter (fast_coefficient_to_dB (y))));
+ } else if (y < 0.0) {
+ context->line_to (i + 0.5, position (_wave_view->amplitude() * -alt_log_meter (fast_coefficient_to_dB (-y))));
+ } else {
+ context->line_to (i + 0.5, position (0.0));
+ }
+ }
+ } else {
+ for (int i = 0; i < _n_peaks; ++i) {
+ context->line_to (i + 0.5, position (_wave_view->amplitude() * _peaks[i].max));
+ }
}
- context->stroke ();
+
+ /* from final top point, move out of the clip zone */
+
+ context->line_to (_n_peaks + 10, position (0.0));
- context->move_to (0.5, position (_peaks[0].min));
- for (int i = 1; i < _n_peaks; ++i) {
- context->line_to (i + 0.5, position (_peaks[i].min));
+ /* bottom half, in reverse */
+
+ if (_wave_view->_logscaled) {
+ for (int i = _n_peaks-1; i >= 0; --i) {
+ Coord y = _peaks[i].min;
+ if (y > 0.0) {
+ context->line_to (i + 0.5, position (_wave_view->amplitude() * alt_log_meter (fast_coefficient_to_dB (y))));
+ } else if (y < 0.0) {
+ context->line_to (i + 0.5, position (_wave_view->amplitude() * -alt_log_meter (fast_coefficient_to_dB (-y))));
+ } else {
+ context->line_to (i + 0.5, position (0.0));
+ }
+ }
+ } else {
+ for (int i = _n_peaks-1; i >= 0; --i) {
+ context->line_to (i + 0.5, position (_wave_view->amplitude() * _peaks[i].min));
+ }
}
+
+ /* from final bottom point, move out of the clip zone */
+
+ context->line_to (-10.0, position (0.0));
+
+ context->close_path ();
+
+ if (WaveView::gradient_waveforms()) {
+
+ Cairo::RefPtr<Cairo::LinearGradient> gradient (Cairo::LinearGradient::create (0, 0, 0, _wave_view->_height));
+
+ double r, g, b, a;
+
+ color_to_rgba (_wave_view->_fill_color, r, g, b, a);
+ gradient->add_color_stop_rgba (0.1, r, g, b, a);
+ gradient->add_color_stop_rgba (0.9, r, g, b, a);
+
+ /* generate a new color for the middle of the gradient */
+ double h, s, v;
+ color_to_hsv (_wave_view->_fill_color, h, s, v);
+ /* tone down the saturation */
+ s *= 0.75;
+ Color center = hsv_to_color (h, s, v, a);
+ color_to_rgba (center, r, g, b, a);
+ gradient->add_color_stop_rgba (0.5, r, g, b, a);
+
+ context->set_source (gradient);
+ } else {
+ set_source_rgba (context, _wave_view->_fill_color);
+ }
+
+ context->fill_preserve ();
+ _wave_view->setup_outline_context (context);
context->stroke ();
- set_source_rgba (context, _wave_view->_fill_color);
- for (int i = 0; i < _n_peaks; ++i) {
- context->move_to (i + 0.5, position (_peaks[i].max) - 1);
- context->line_to (i + 0.5, position (_peaks[i].min) + 1);
+ if (_wave_view->show_zero_line()) {
+ set_source_rgba (context, _wave_view->_zero_color);
+ context->move_to (0, position (0.0));
+ context->line_to (_n_peaks, position (0.0));
context->stroke ();
}
}
Coord
-WaveView::CacheEntry::position (float s) const
+WaveView::CacheEntry::position (double s) const
{
- return (s + 1) * _wave_view->_height / 2;
+ switch (_wave_view->_shape) {
+ case Rectified:
+ return _wave_view->_height - (s * _wave_view->_height);
+ default:
+ break;
+ }
+ return (s+1.0) * (_wave_view->_height / 2.0);
}
void
{
_image.clear ();
}
-
-
-
+
+void
+WaveView::set_gradient_waveforms (bool yn)
+{
+ if (_gradient_waveforms != yn) {
+ _gradient_waveforms = yn;
+ InvalidateAllImages (); /* EMIT SIGNAL */
+ }
+}