diff options
Diffstat (limited to 'libs/canvas/canvas')
-rw-r--r-- | libs/canvas/canvas/wave_view.h | 243 |
1 files changed, 209 insertions, 34 deletions
diff --git a/libs/canvas/canvas/wave_view.h b/libs/canvas/canvas/wave_view.h index 9ace1486ce..9a4cbafa7c 100644 --- a/libs/canvas/canvas/wave_view.h +++ b/libs/canvas/canvas/wave_view.h @@ -45,51 +45,174 @@ class WaveViewTest; namespace ArdourCanvas { -class LIBCANVAS_API WaveView : public Item +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<const ARDOUR::Region> region; + + /* resulting image, after request has been satisfied */ + + Cairo::RefPtr<Cairo::ImageSurface> 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). + */ - struct CacheEntry { 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<Cairo::ImageSurface> image; - CacheEntry(int chan, Coord hght, float amp, Color fcl, framepos_t strt, framepos_t ed, Cairo::RefPtr<Cairo::ImageSurface> img) : + /* last time the cache entry was used */ + uint64_t timestamp; - channel (chan), height (hght), amplitude (amp), fill_color (fcl), - start (strt), end (ed), image (img) {} + Entry (int chan, Coord hght, float amp, Color fcl, double spp, framepos_t strt, framepos_t ed, + Cairo::RefPtr<Cairo::ImageSurface> img) + : channel (chan) + , height (hght) + , amplitude (amp) + , fill_color (fcl) + , samples_per_pixel (spp) + , start (strt) + , end (ed) + , image (img) {} }; - /* final ImageSurface rendered with colours */ - Cairo::RefPtr<Cairo::ImageSurface> _image; + uint64_t image_cache_threshold () const { return _image_cache_threshold; } + void set_image_cache_threshold (uint64_t); - /* Displays a single channel of waveform data for the given Region. + void add (boost::shared_ptr<ARDOUR::AudioSource>, boost::shared_ptr<Entry>); + void use (boost::shared_ptr<ARDOUR::AudioSource>, boost::shared_ptr<Entry>); + + void consolidate_image_cache (boost::shared_ptr<ARDOUR::AudioSource>, + int channel, + Coord height, + float amplitude, + Color fill_color, + double samples_per_pixel); + + boost::shared_ptr<Entry> lookup_image (boost::shared_ptr<ARDOUR::AudioSource>, + framepos_t start, framepos_t end, + int _channel, + Coord height, + float amplitude, + Color fill_color, + double samples_per_pixel); + + 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<boost::shared_ptr<Entry> > CacheLine; + /* Indexed, non-sortable structure used to lookup images associated + * with a particular AudioSource + */ + typedef std::map <boost::shared_ptr<ARDOUR::AudioSource>,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<ARDOUR::AudioSource>,boost::shared_ptr<Entry> > ListEntry; + typedef std::vector<ListEntry> 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 = 0 in the waveview corresponds to the first waveform datum taken - from region->start() samples into the source data. +class LIBCANVAS_API WaveView : public Item, public sigc::trackable +{ +public: - x = N in the waveview corresponds to the (N * spp)'th sample - measured from region->start() into the source data. + enum Shape { + Normal, + Rectified + }; - when drawing, we will map the zeroth-pixel of the waveview - into a window. + std::string debug_name() const; - 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). - */ + /* final ImageSurface rendered with colours */ + Cairo::RefPtr<Cairo::ImageSurface> _image; + PBD::Signal0<void> 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<ARDOUR::AudioRegion>); WaveView (Item*, boost::shared_ptr<ARDOUR::AudioRegion>); @@ -147,6 +270,9 @@ public: static void set_clip_level (double dB); static PBD::Signal0<void> ClipLevelChanged; + static void start_drawing_thread (); + static void stop_drawing_thread (); + #ifdef CANVAS_COMPATIBILITY void*& property_gain_src () { return _foo_void; @@ -154,17 +280,16 @@ public: void*& property_gain_function () { return _foo_void; } -private: + private: void* _foo_void; #endif + private: friend class ::WaveViewTest; + friend class WaveViewThreadClient; - static std::map <boost::shared_ptr<ARDOUR::AudioSource>, std::vector <CacheEntry> > _image_cache; - void consolidate_image_cache () const; - void invalidate_source (boost::weak_ptr<ARDOUR::AudioSource>); - void invalidate_image_cache (); + void invalidate_image_cache (); boost::shared_ptr<ARDOUR::AudioRegion> _region; int _channel; @@ -188,8 +313,32 @@ private: */ ARDOUR::frameoffset_t _region_start; + /** 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; + + /** 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 _source_invalidated_connection; + PBD::ScopedConnection image_ready_connection; static double _global_gradient_depth; static bool _global_logscaled; @@ -202,11 +351,37 @@ private: void handle_visual_property_change (); void handle_clip_level_change (); - void get_image (Cairo::RefPtr<Cairo::ImageSurface>& image, framepos_t start, framepos_t end, double& image_offset) const; - + boost::shared_ptr<WaveViewCache::Entry> get_image (framepos_t start, framepos_t end) const; + boost::shared_ptr<WaveViewCache::Entry> get_image_from_cache (framepos_t start, framepos_t end) const; + ArdourCanvas::Coord y_extent (double, bool) const; + void draw_image (Cairo::RefPtr<Cairo::ImageSurface>&, ARDOUR::PeakData*, int n_peaks, boost::shared_ptr<WaveViewThreadRequest>) const; void draw_absent_image (Cairo::RefPtr<Cairo::ImageSurface>&, ARDOUR::PeakData*, int) const; - void draw_image (Cairo::RefPtr<Cairo::ImageSurface>&, ARDOUR::PeakData*, int) const; + + void cancel_my_render_request () const; + + void queue_get_image (boost::shared_ptr<const ARDOUR::Region> region, framepos_t start, framepos_t end) const; + void generate_image (boost::shared_ptr<WaveViewThreadRequest>, bool in_render_thread) const; + boost::shared_ptr<WaveViewCache::Entry> cache_request_result (boost::shared_ptr<WaveViewThreadRequest> req) const; + + void image_ready (); + + mutable boost::shared_ptr<WaveViewCache::Entry> _current_image; + + mutable boost::shared_ptr<WaveViewThreadRequest> current_request; + void send_request (boost::shared_ptr<WaveViewThreadRequest>) const; + bool idle_send_request (boost::shared_ptr<WaveViewThreadRequest>) 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<WaveView const *> DrawingRequestQueue; + static DrawingRequestQueue request_queue; }; } |