diff options
Diffstat (limited to 'libs/waveview/waveview/wave_view.h')
-rw-r--r-- | libs/waveview/waveview/wave_view.h | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/libs/waveview/waveview/wave_view.h b/libs/waveview/waveview/wave_view.h new file mode 100644 index 0000000000..4b1c8eb42b --- /dev/null +++ b/libs/waveview/waveview/wave_view.h @@ -0,0 +1,268 @@ +/* + Copyright (C) 2011-2013 Paul Davis + Copyright (C) 2017 Tim Mayberry + Author: Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef _WAVEVIEW_WAVE_VIEW_H_ +#define _WAVEVIEW_WAVE_VIEW_H_ + +#include <boost/shared_ptr.hpp> +#include <boost/scoped_ptr.hpp> + +#include <glibmm/refptr.h> + +#include "ardour/types.h" +#include "canvas/item.h" +#include "waveview/visibility.h" + +namespace ARDOUR { + class AudioRegion; +} + +namespace Gdk { + class Pixbuf; +} + +namespace ArdourWaveView { + +class WaveViewCacheGroup; +class WaveViewDrawRequest; +class WaveViewDrawRequestQueue; +class WaveViewImage; +class WaveViewProperties; +class WaveViewDrawingThread; + +class LIBWAVEVIEW_API WaveView : public ArdourCanvas::Item, public sigc::trackable +{ +public: + enum Shape { Normal, Rectified }; + + std::string debug_name () const; + + /* Displays a single channel of waveform data for the given Region. + + x = 0 in the waveview corresponds to the first waveform datum taken + from region->start() samples into the source data. + + x = N in the waveview corresponds to the (N * spp)'th sample + measured from region->start() into the source data. + + when drawing, we will map the zeroth-pixel of the waveview + into a window. + + The waveview itself contains a set of pre-rendered Cairo::ImageSurfaces + that cache sections of the display. This is filled on-demand and + never cleared until something explicitly marks the cache invalid + (such as a change in samples_per_pixel, the log scaling, rectified or + other view parameters). + */ + + WaveView (ArdourCanvas::Canvas*, boost::shared_ptr<ARDOUR::AudioRegion>); + WaveView (Item*, boost::shared_ptr<ARDOUR::AudioRegion>); + ~WaveView (); + + virtual void prepare_for_render (ArdourCanvas::Rect const& window_area) const; + + virtual void render (ArdourCanvas::Rect const & area, Cairo::RefPtr<Cairo::Context>) const; + + void compute_bounding_box () const; + + void set_samples_per_pixel (double); + void set_height (ArdourCanvas::Distance); + void set_channel (int); + void set_region_start (ARDOUR::frameoffset_t); + + /** Change the first position drawn by @param pixels. + * @param pixels must be positive. This is used by + * AudioRegionViews in Ardour to avoid drawing the + * first pixel of a waveform, and exists in case + * there are uses for WaveView where we do not + * want this behaviour. + */ + void set_start_shift (double pixels); + + void set_fill_color (Gtkmm2ext::Color); + void set_outline_color (Gtkmm2ext::Color); + + void region_resized (); + void gain_changed (); + + void set_show_zero_line (bool); + bool show_zero_line () const; + + void set_zero_color (Gtkmm2ext::Color); + void set_clip_color (Gtkmm2ext::Color); + void set_logscaled (bool); + + void set_gradient_depth (double); + double gradient_depth () const; + + void set_shape (Shape); + + void set_always_get_image_in_thread (bool yn); + + /* currently missing because we don't need them (yet): + * set_shape_independent(); + * set_logscaled_independent(); + */ + + static void set_global_gradient_depth (double); + static void set_global_logscaled (bool); + static void set_global_shape (Shape); + static void set_global_show_waveform_clipping (bool); + + static double global_gradient_depth () { return _global_gradient_depth; } + + static bool global_logscaled () { return _global_logscaled; } + + static Shape global_shape () { return _global_shape; } + + void set_amplitude_above_axis (double v); + + double amplitude_above_axis () const; + + static void set_clip_level (double dB); + static PBD::Signal0<void> ClipLevelChanged; + + static void start_drawing_thread (); + static void stop_drawing_thread (); + + static void set_image_cache_size (uint64_t); + +#ifdef CANVAS_COMPATIBILITY + void*& property_gain_src () { + return _foo_void; + } + void*& property_gain_function () { + return _foo_void; + } + +private: + void* _foo_void; +#endif + +private: + friend class WaveViewThreadClient; + friend class WaveViewDrawingThread; + + boost::shared_ptr<ARDOUR::AudioRegion> _region; + + boost::scoped_ptr<WaveViewProperties> _props; + + mutable boost::shared_ptr<WaveViewImage> _image; + + mutable boost::shared_ptr<WaveViewCacheGroup> _cache_group; + + bool _shape_independent; + bool _logscaled_independent; + bool _gradient_depth_independent; + + /** Under almost conditions, this is going to return _region->length(), + * but if region_start has been reset, then we need to use this modified + * computation. + */ + ARDOUR::framecnt_t region_length () const; + + /** Under almost conditions, this is going to return _region->start() + + * _region->length(), but if region_start has been reset, then we need to use + * this modified computation. + */ + ARDOUR::framepos_t region_end () const; + + /** + * _image stays non-null after the first time it is set + */ + bool rendered () const { return _image.get(); } + + bool draw_image_in_gui_thread () const; + + /** If true, calls to render() will render a missing wave image in the GUI + * thread. Generally set to false, but true after a call to set_height(). + */ + mutable bool _draw_image_in_gui_thread; + + /** If true, calls to render() will render a missing wave image in the GUI + * thread. Set true for waveviews we expect to keep updating (e.g. while + * recording) + */ + bool _always_draw_image_in_gui_thread; + + void init(); + + mutable boost::shared_ptr<WaveViewDrawRequest> current_request; + + PBD::ScopedConnectionList invalidation_connection; + + static double _global_gradient_depth; + static bool _global_logscaled; + static Shape _global_shape; + static bool _global_show_waveform_clipping; + static double _global_clip_level; + + static PBD::Signal0<void> VisualPropertiesChanged; + + void handle_visual_property_change (); + void handle_clip_level_change (); + + struct LineTips { + double top; + double bot; + double spread; + bool clip_max; + bool clip_min; + + LineTips () : top (0.0), bot (0.0), clip_max (false), clip_min (false) {} + }; + + static ArdourCanvas::Coord y_extent (double, Shape const, double const height); + + static void compute_tips (ARDOUR::PeakData const& peak, LineTips& tips, double const effective_height); + + static void draw_image (Cairo::RefPtr<Cairo::ImageSurface>&, ARDOUR::PeakData*, int n_peaks, + boost::shared_ptr<WaveViewDrawRequest>); + static void draw_absent_image (Cairo::RefPtr<Cairo::ImageSurface>&, ARDOUR::PeakData*, int); + + ARDOUR::framecnt_t optimal_image_width_samples () const; + + void set_image (boost::shared_ptr<WaveViewImage> img) const; + + // @return true if item area intersects with draw area + bool get_item_and_draw_rect_in_window_coords (ArdourCanvas::Rect const& canvas_rect, + ArdourCanvas::Rect& item_area, + ArdourCanvas::Rect& draw_rect) const; + + boost::shared_ptr<WaveViewDrawRequest> create_draw_request (WaveViewProperties const&) const; + + void queue_draw_request (boost::shared_ptr<WaveViewDrawRequest> const&) const; + + static void process_draw_request (boost::shared_ptr<WaveViewDrawRequest>); + + boost::shared_ptr<WaveViewCacheGroup> get_cache_group () const; + + /** + * Notify the Cache that we are dropping our reference to the + * CacheGroup so it can also do so if it is the only reference holder + * of the cache group. + */ + void reset_cache_group (); +}; + +} /* namespace */ + +#endif |