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<const ARDOUR::Region> region;
+
+ /* resulting image, after request has been satisfied */
+
+ Cairo::RefPtr<Cairo::ImageSurface> 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<Cairo::ImageSurface> 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<Cairo::ImageSurface> 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<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,
+ 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<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 = 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<ARDOUR::AudioRegion>);
+ 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>);
~WaveView ();
void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
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);
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()
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;
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<ARDOUR::AudioRegion> _region;
int _channel;
double _samples_per_pixel;
Coord _height;
- Color _wave_color;
bool _show_zero;
Color _zero_color;
Color _clip_color;
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<Cairo::ImageSurface> _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;
static PBD::Signal0<void> VisualPropertiesChanged;
void handle_visual_property_change ();
+ void handle_clip_level_change ();
+
+ boost::shared_ptr<WaveViewCache::Entry> get_image (framepos_t start, framepos_t end, bool& full_image) const;
+ boost::shared_ptr<WaveViewCache::Entry> 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<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 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 ();
- 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<WaveView const *> DrawingRequestQueue;
+ static DrawingRequestQueue request_queue;
};
}