X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fcanvas%2Fcanvas%2Fwave_view.h;h=9f87855e5412788183c92fb0d8285cb6ffa8332a;hb=77723e900f888947b4354a7d7d4d7d9edf50e6b0;hp=b80ae5183da5519b3bad7d301917e5ebf1595bb9;hpb=37b1f5017e904005fae6b815b1519b3fa595d8d3;p=ardour.git diff --git a/libs/canvas/canvas/wave_view.h b/libs/canvas/canvas/wave_view.h index b80ae5183d..9f87855e54 100644 --- a/libs/canvas/canvas/wave_view.h +++ b/libs/canvas/canvas/wave_view.h @@ -45,34 +45,178 @@ class WaveViewTest; namespace ArdourCanvas { -class LIBCANVAS_API WaveView : virtual public Item, public Outline, public Fill +struct LIBCANVAS_API WaveViewThreadRequest { -public: - enum Shape { - Normal, - Rectified, + 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 */ +}; - /* Displays a single channel of waveform data for the given Region. +class LIBCANVAS_API WaveView; - x = 0 in the waveview corresponds to the first waveform datum taken - from region->start() samples into the source data. +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 (); +}; - x = N in the waveview corresponds to the (N * spp)'th sample - measured from region->start() into the source data. +class LIBCANVAS_API WaveView : public Item, public sigc::trackable +{ +public: - when drawing, we will map the zeroth-pixel of the waveview - into a window. + enum Shape { + Normal, + Rectified + }; - 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). - */ + std::string debug_name() const; + /* final ImageSurface rendered with colours */ - WaveView (Group *, boost::shared_ptr); + 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; @@ -83,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); @@ -98,6 +251,8 @@ 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(); set_logscaled_independent() @@ -118,6 +273,9 @@ public: 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; @@ -125,20 +283,21 @@ public: void*& property_gain_function () { return _foo_void; } -private: + private: void* _foo_void; #endif + private: friend class ::WaveViewTest; + friend class WaveViewThreadClient; - void invalidate_image (); + 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; @@ -149,18 +308,46 @@ 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 ARDOUR::framepos_t _sample_start; - mutable ARDOUR::framepos_t _sample_end; - mutable Cairo::RefPtr _image; - 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; @@ -171,10 +358,39 @@ private: 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 (); - void ensure_cache (ARDOUR::framepos_t sample_start, ARDOUR::framepos_t sample_end) const; - ArdourCanvas::Coord position (double) const; - void draw_image (ARDOUR::PeakData*, int npeaks) const; + 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; }; }