X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=libs%2Fcanvas%2Fcanvas%2Fwave_view.h;h=9f87855e5412788183c92fb0d8285cb6ffa8332a;hb=77723e900f888947b4354a7d7d4d7d9edf50e6b0;hp=b75b8c5312a691ee11f6b3746a270783d962ace6;hpb=11619a37bff79c050e39e434bc9899b516cbe4a1;p=ardour.git diff --git a/libs/canvas/canvas/wave_view.h b/libs/canvas/canvas/wave_view.h index b75b8c5312..9f87855e54 100644 --- a/libs/canvas/canvas/wave_view.h +++ b/libs/canvas/canvas/wave_view.h @@ -20,6 +20,7 @@ #include #include +#include #include "pbd/properties.h" @@ -27,6 +28,7 @@ #include +#include "canvas/visibility.h" #include "canvas/item.h" #include "canvas/fill.h" #include "canvas/outline.h" @@ -43,15 +45,179 @@ class WaveViewTest; namespace ArdourCanvas { -class WaveView : virtual public Item, public Outline, public Fill +struct LIBCANVAS_API WaveViewThreadRequest +{ + public: + enum RequestType { + Quit, + Cancel, + Draw + }; + + WaveViewThreadRequest () : stop (0) {} + + bool should_stop () const { return (bool) g_atomic_int_get (&stop); } + void cancel() { g_atomic_int_set (&stop, 1); } + + RequestType type; + framepos_t start; + framepos_t end; + double width; + double height; + double samples_per_pixel; + uint16_t channel; + double region_amplitude; + Color fill_color; + boost::weak_ptr region; + + /* resulting image, after request has been satisfied */ + + Cairo::RefPtr image; + + private: + gint stop; /* intended for atomic access */ +}; + +class LIBCANVAS_API WaveView; + +class LIBCANVAS_API WaveViewCache +{ + public: + WaveViewCache(); + ~WaveViewCache(); + + struct Entry { + + /* these properties define the cache entry as unique. + + If an image is in use by a WaveView and any of these + properties are modified on the WaveView, the image can no + longer be used (or may no longer be usable for start/end + parameters). It will remain in the cache until flushed for + some reason (typically the cache is full). + */ + + int channel; + Coord height; + float amplitude; + Color fill_color; + double samples_per_pixel; + framepos_t start; + framepos_t end; + + /* the actual image referred to by the cache entry */ + + Cairo::RefPtr image; + + /* last time the cache entry was used */ + uint64_t timestamp; + + Entry (int chan, Coord hght, float amp, Color fcl, double spp, framepos_t strt, framepos_t ed, + Cairo::RefPtr img) + : channel (chan) + , height (hght) + , amplitude (amp) + , fill_color (fcl) + , samples_per_pixel (spp) + , start (strt) + , end (ed) + , image (img) {} + }; + + uint64_t image_cache_threshold () const { return _image_cache_threshold; } + void set_image_cache_threshold (uint64_t); + + void add (boost::shared_ptr, boost::shared_ptr); + void use (boost::shared_ptr, boost::shared_ptr); + + void consolidate_image_cache (boost::shared_ptr, + int channel, + Coord height, + float amplitude, + Color fill_color, + double samples_per_pixel); + + boost::shared_ptr lookup_image (boost::shared_ptr, + framepos_t start, framepos_t end, + int _channel, + Coord height, + float amplitude, + Color fill_color, + double samples_per_pixel, + bool& full_image); + + private: + /* an unsorted, unindexd collection of cache entries associated with + a particular AudioSource. All cache Entries in the collection + share the AudioSource in common, but represent different parameter + settings (e.g. height, color, samples per pixel etc.) + */ + typedef std::vector > CacheLine; + /* Indexed, non-sortable structure used to lookup images associated + * with a particular AudioSource + */ + typedef std::map ,CacheLine> ImageCache; + ImageCache cache_map; + + /* Linear, sortable structure used when we need to do a timestamp-based + * flush of entries from the cache. + */ + typedef std::pair,boost::shared_ptr > ListEntry; + typedef std::vector CacheList; + CacheList cache_list; + + struct SortByTimestamp { + bool operator() (const WaveViewCache::ListEntry& a, const WaveViewCache::ListEntry& b) { + return a.second->timestamp < b.second->timestamp; + } + }; + friend struct SortByTimestamp; + + uint64_t image_cache_size; + uint64_t _image_cache_threshold; + + uint64_t compute_image_cache_size (); + void cache_flush (); + bool cache_full (); +}; + +class LIBCANVAS_API WaveView : public Item, public sigc::trackable { public: + enum Shape { Normal, - Rectified, + Rectified }; - WaveView (Group *, boost::shared_ptr); + std::string debug_name() const; + + /* final ImageSurface rendered with colours */ + + Cairo::RefPtr _image; + PBD::Signal0 ImageReady; + + /* 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 (Canvas *, boost::shared_ptr); + WaveView (Item*, boost::shared_ptr); + ~WaveView (); void render (Rect const & area, Cairo::RefPtr) const; void compute_bounding_box () const; @@ -61,6 +227,15 @@ public: 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 (Color); void set_outline_color (Color); @@ -76,6 +251,7 @@ public: double gradient_depth() const { return _gradient_depth; } 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(); @@ -85,6 +261,7 @@ public: 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; } @@ -93,6 +270,12 @@ public: void set_amplitude_above_axis (double v); double amplitude_above_axis () const { return _amplitude_above_axis; } + static void set_clip_level (double dB); + static PBD::Signal0 ClipLevelChanged; + + static void start_drawing_thread (); + static void stop_drawing_thread (); + #ifdef CANVAS_COMPATIBILITY void*& property_gain_src () { return _foo_void; @@ -100,70 +283,21 @@ public: void*& property_gain_function () { return _foo_void; } -private: + private: void* _foo_void; #endif - /** A cached, pre-rendered image of some section of a waveform. - - It spans a range given relative to the start of the source - of the waveform data, so a range from N..M corresponds - to the sample range N..M within the source. - - Invalidated by a changes to: - - samples_per_pixel - colors - height - - */ - - class CacheEntry - { - public: - CacheEntry (WaveView const *, double, double, int); - ~CacheEntry (); - - double start () const { - return _start; - } + private: + friend class ::WaveViewTest; + friend class WaveViewThreadClient; - double end () const { - return _end; - } - - boost::shared_array peaks () const { - return _peaks; - } - - Cairo::RefPtr image(); - void clear_image (); - - private: - Coord position (Coord) const; - - WaveView const * _wave_view; - - double _start; - double _end; - int _n_peaks; - - boost::shared_array _peaks; - Cairo::RefPtr _image; - }; - - friend class CacheEntry; - friend class ::WaveViewTest; - - void invalidate_whole_cache (); - void invalidate_image_cache (); + void invalidate_image_cache (); boost::shared_ptr _region; int _channel; double _samples_per_pixel; Coord _height; - Color _wave_color; bool _show_zero; Color _zero_color; Color _clip_color; @@ -174,23 +308,89 @@ private: bool _logscaled_independent; bool _gradient_depth_independent; double _amplitude_above_axis; - + float _region_amplitude; + double _start_shift; + /** The `start' value to use for the region; we can't use the region's * value as the crossfade editor needs to alter it. */ ARDOUR::frameoffset_t _region_start; - mutable std::list _cache; - - PBD::ScopedConnection invalidation_connection; + /** 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; + + /** If true, calls to get_image() will render a missing wave image + in the calling thread. Generally set to false, but true after a + call to set_height(). + */ + mutable bool get_image_in_thread; + + /** If true, calls to get_image() will render a missing wave image + in the calling thread. Set true for waveviews we expect to + keep updating (e.g. while recording) + */ + bool always_get_image_in_thread; + + /** Set to true by render(). Used so that we know if the wave view + * has actually been displayed on screen. ::set_height() when this + * is true does not use get_image_in_thread, because it implies + * that the height is being set BEFORE the waveview is drawn. + */ + mutable bool rendered; + + PBD::ScopedConnectionList invalidation_connection; + PBD::ScopedConnection image_ready_connection; static double _global_gradient_depth; static bool _global_logscaled; static Shape _global_shape; + static bool _global_show_waveform_clipping; + static double _clip_level; static PBD::Signal0 VisualPropertiesChanged; void handle_visual_property_change (); + void handle_clip_level_change (); + + boost::shared_ptr get_image (framepos_t start, framepos_t end, bool& full_image) const; + boost::shared_ptr get_image_from_cache (framepos_t start, framepos_t end, bool& full_image) const; + + ArdourCanvas::Coord y_extent (double, bool) const; + void draw_image (Cairo::RefPtr&, ARDOUR::PeakData*, int n_peaks, boost::shared_ptr) const; + void draw_absent_image (Cairo::RefPtr&, ARDOUR::PeakData*, int) const; + + void cancel_my_render_request () const; + + void queue_get_image (boost::shared_ptr region, framepos_t start, framepos_t end) const; + void generate_image (boost::shared_ptr, bool in_render_thread) const; + boost::shared_ptr cache_request_result (boost::shared_ptr req) const; + + void image_ready (); + + mutable boost::shared_ptr _current_image; + + mutable boost::shared_ptr current_request; + void send_request (boost::shared_ptr) const; + bool idle_send_request (boost::shared_ptr) const; + + static WaveViewCache* images; + + static void drawing_thread (); + + static gint drawing_thread_should_quit; + static Glib::Threads::Mutex request_queue_lock; + static Glib::Threads::Cond request_cond; + static Glib::Threads::Thread* _drawing_thread; + typedef std::set DrawingRequestQueue; + static DrawingRequestQueue request_queue; }; }