summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2013-06-20 14:37:31 -0400
committerPaul Davis <paul@linuxaudiosystems.com>2013-06-20 14:37:31 -0400
commit27c943f1ddca0890ac2e5547b249334e0266c0a5 (patch)
treefd3c1c6b4421cfbf720d3a91ab58141461446017 /libs
parent3604bf12a2806b2a21b4ac6a1776fa55ed7963b4 (diff)
new image cache design for waveviews, with various fixes.
Rather than maintain a set of images in a cache, when we no longer have the required waveform data, create a new image that is appropriately centered and extends to roughly twice the screen width (or the limits of the region's source file(s), as necessary)
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/audiosource.cc5
-rw-r--r--libs/canvas/canvas/wave_view.h11
-rw-r--r--libs/canvas/wave_view.cc233
3 files changed, 84 insertions, 165 deletions
diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc
index 74dd52d504..767ea1f9a8 100644
--- a/libs/ardour/audiosource.cc
+++ b/libs/ardour/audiosource.cc
@@ -342,14 +342,14 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, framecnt_t npeaks, framepos_t
/* fix for near-end-of-file conditions */
if (cnt > _length - start) {
- // cerr << "too close to end @ " << _length << " given " << start << " + " << cnt << endl;
+ cerr << "too close to end @ " << _length << " given " << start << " + " << cnt << " (" << _length - start << ")" << endl;
cnt = _length - start;
framecnt_t old = npeaks;
npeaks = min ((framecnt_t) floor (cnt / samples_per_visual_peak), npeaks);
zero_fill = old - npeaks;
}
- // cerr << "actual npeaks = " << npeaks << " zf = " << zero_fill << endl;
+ cerr << "actual npeaks = " << npeaks << " zf = " << zero_fill << endl;
if (npeaks == cnt) {
@@ -527,6 +527,7 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, framecnt_t npeaks, framepos_t
}
if (zero_fill) {
+ cerr << "Zero fill end of peaks (@ " << npeaks << " with " << zero_fill << endl;
memset (&peaks[npeaks], 0, sizeof (PeakData) * zero_fill);
}
diff --git a/libs/canvas/canvas/wave_view.h b/libs/canvas/canvas/wave_view.h
index e4e9d9b7ed..78c6486fce 100644
--- a/libs/canvas/canvas/wave_view.h
+++ b/libs/canvas/canvas/wave_view.h
@@ -71,6 +71,7 @@ public:
WaveView (Group *, boost::shared_ptr<ARDOUR::AudioRegion>);
+ ~WaveView ();
void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
void compute_bounding_box () const;
@@ -141,7 +142,7 @@ private:
class CacheEntry
{
public:
- CacheEntry (WaveView const *, double, double);
+ CacheEntry (WaveView const *, double, double, ARDOUR::framepos_t, ARDOUR::framepos_t);
~CacheEntry ();
double pixel_start () const {
@@ -171,7 +172,7 @@ private:
Coord position (Coord) const;
WaveView const * _wave_view;
-
+
double _pixel_start;
double _pixel_end;
ARDOUR::framecnt_t _sample_start;
@@ -208,8 +209,8 @@ private:
* value as the crossfade editor needs to alter it.
*/
ARDOUR::frameoffset_t _region_start;
-
- mutable std::list<CacheEntry*> _cache;
+
+ mutable CacheEntry* _cache;
PBD::ScopedConnection invalidation_connection;
@@ -220,6 +221,8 @@ private:
static PBD::Signal0<void> VisualPropertiesChanged;
void handle_visual_property_change ();
+ void ensure_cache (ARDOUR::framecnt_t pixel_start, ARDOUR::framecnt_t pixel_end,
+ ARDOUR::framepos_t sample_start, ARDOUR::framepos_t sample_end) const;
};
}
diff --git a/libs/canvas/wave_view.cc b/libs/canvas/wave_view.cc
index 1839b88cc4..6e190ee71a 100644
--- a/libs/canvas/wave_view.cc
+++ b/libs/canvas/wave_view.cc
@@ -67,10 +67,17 @@ WaveView::WaveView (Group* parent, boost::shared_ptr<ARDOUR::AudioRegion> region
, _gradient_depth_independent (false)
, _amplitude_above_axis (1.0)
, _region_start (region->start())
+ , _cache (0)
{
VisualPropertiesChanged.connect_same_thread (invalidation_connection, boost::bind (&WaveView::handle_visual_property_change, this));
}
+WaveView::~WaveView ()
+{
+ delete _cache;
+ _cache = 0;
+}
+
void
WaveView::handle_visual_property_change ()
{
@@ -131,18 +138,6 @@ WaveView::set_samples_per_pixel (double samples_per_pixel)
}
static inline double
-to_src_sample_offset (frameoffset_t src_sample_start, double pixel_offset, double spp)
-{
- return llrintf (src_sample_start + (pixel_offset * spp));
-}
-
-static inline double
-to_pixel_offset (frameoffset_t src_sample_start, double sample_offset, double spp)
-{
- return llrintf ((sample_offset - src_sample_start) / spp);
-}
-
-static inline double
image_to_window (double wave_origin, double image_start)
{
return wave_origin + image_start;
@@ -155,6 +150,53 @@ window_to_image (double wave_origin, double image_start)
}
void
+WaveView::ensure_cache (framecnt_t start, framecnt_t end,
+ framepos_t sample_start, framepos_t sample_end) const
+{
+ if (_cache && _cache->sample_start() <= sample_start && _cache->sample_end() >= sample_end) {
+ /* cache already covers required range, do nothing */
+ return;
+ }
+
+ if (_cache) {
+ delete _cache;
+ _cache = 0;
+ }
+
+ /* sample position is canonical here, and we want to generate
+ * an image that spans about twice the canvas width
+ */
+
+ const framepos_t center = sample_start + ((sample_end - sample_start) / 2);
+ const framecnt_t canvas_samples = 2 * (_canvas->visible_area().width() * _samples_per_pixel);
+
+ /* we can request data from anywhere in the Source, between 0 and its length
+ */
+
+ sample_start = max ((framepos_t) 0, (center - canvas_samples));
+ sample_end = min (center + canvas_samples, _region->source_length (0));
+
+ if (sample_end <= sample_start) {
+ cerr << "sample start = " << sample_start << endl;
+ cerr << "center+ = " << center<< endl;
+ cerr << "CS = " << canvas_samples << endl;
+ cerr << "pui = " << center + canvas_samples << endl;
+ cerr << "sl = " << _region->source_length (0) << endl;
+ cerr << "st = " << _region->start () << endl;
+ cerr << "END: " << sample_end << endl;
+ assert (false);
+ }
+
+ start = floor (sample_start / (double) _samples_per_pixel);
+ end = ceil (sample_end / (double) _samples_per_pixel);
+
+ assert (end > start);
+
+ cerr << name << " cache miss - new CE, span " << start << " .. " << end << " (" << sample_start << " .. " << sample_end << ")\n";
+ _cache = new CacheEntry (this, start, end, sample_start, sample_end);
+}
+
+void
WaveView::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
{
assert (_samples_per_pixel != 0);
@@ -170,139 +212,23 @@ WaveView::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) cons
return;
}
- /* we have a set of cached images that have precise pixel positions
- * whose origin is 0,0 within our own rect. To convert these pixel
- * positions so that they are useful when rendering, they need to
- * be offset by the window position of our own origin. This is given
- * by self.x0
- */
-
Rect draw = d.get();
+ /* pixel coordinates - we round up and down in case we were asked to
+ * draw "between" pixels at the start and/or end
+ */
+ double draw_start = floor (draw.x0);
+ double draw_end = ceil (draw.x1);
- /* pixel coordinates */
- double start = floor (draw.x0);
- double const end = ceil (draw.x1);
-
- list<CacheEntry*>::iterator cache;
-
- cache = _cache.begin ();
-
- while (end > start) {
-
- /* Step through cache entries that end at or before our current position */
-
- for (; cache != _cache.end(); ++cache) {
- if (image_to_window (self.x0, (*cache)->pixel_start()) <= start) {
- break;
- }
- }
-
- /* Now either:
-
- 1. we have run out of cache entries
-
- 2. we have found a cache entry that starts after start
- create a new cache entry to "fill in" before the one we have found.
-
- 3. we have found a cache entry that starts at or before
- start, but finishes before end: create a new cache entry
- to extend the cache further along the timeline.
-
- Set up a pointer to the cache entry that we will use on this iteration.
- */
-
- CacheEntry* image = 0;
-
- /* Cairo limit, caused by its use of 16.16 fixed point */
- const double BIG_IMAGE_SIZE = 32767.0;
-
- if (cache == _cache.end ()) {
-
- /* Case 1: we have run out of cache entries, so make a new one for
- the whole required area and put it in the list.
-
- We would like to avoid lots of little images in the
- cache, so when we create a new one, make it as wide
- as possible, within the limits inherent in Cairo.
-
- However, we don't want to try to make it larger than
- the source could allow, so clamp with that too.
- */
-
- double const region_end_pixel = image_to_window (self.x0, floor (_region->latest_possible_frame() / _samples_per_pixel));
- double const end_pixel = min (region_end_pixel, start + BIG_IMAGE_SIZE);
-
- if (end_pixel <= start) {
- /* nothing more to draw */
- image = 0;
- } else {
-
- CacheEntry* c = new CacheEntry (this, window_to_image (self.x0, start), window_to_image (self.x0, end_pixel));
-
- _cache.push_back (c);
- image = c;
- }
-
- } else if (image_to_window (self.x0, (*cache)->pixel_start()) > start) {
-
- /* Case 2: we have a cache entry, but it begins after
- * start, so we need another one for the missing section.
- *
- * Create a new cached image that extends as far as the
- * next cached image's start, or the end of the region,
- * or the end of a BIG_IMAGE, whichever comes first.
- */
-
- double end_pixel;
-
- if (end > image_to_window (self.x0, (*cache)->pixel_start())) {
- double const region_end_pixel = image_to_window (self.x0, floor (_region->length() / _samples_per_pixel));
- end_pixel = min (region_end_pixel, max (image_to_window (self.x0, (*cache)->pixel_start()), start + BIG_IMAGE_SIZE));
- } else {
- end_pixel = image_to_window (self.x0, (*cache)->pixel_start());
- }
-
- CacheEntry* c = new CacheEntry (this, window_to_image (self.x0, start), window_to_image (self.x0, end_pixel));
-
- cache = _cache.insert (cache, c);
- ++cache;
- image = c;
-
- } else {
-
- /* Case 3: we have a cache entry that covers some of what
- we have left to render
- */
-
+ /* sample coordinates - note, these are not subject to rounding error */
+ framepos_t sample_start = _region_start + (draw_start * _samples_per_pixel);
+ framepos_t sample_end = _region_start + (draw_end * _samples_per_pixel);
- image = *cache;
- ++cache;
- }
+ ensure_cache (draw_start, draw_end, sample_start, sample_end);
- if (!image) {
- break;
- }
-
- double this_end = min (end, image_to_window (self.x0, image->pixel_end ()));
-#if 0
- cerr << "\t\tDraw image between "
- << start << " .. " << this_end
- << " using image spanning "
- << image->pixel_start() << " (" << image_to_window (self.x0, image->pixel_start()) << ")"
- << " .. "
- << image->pixel_end () << " (" << image_to_window (self.x0, image->pixel_end()) << ")"
- << " offset into image = " << image_to_window (self.x0, image->pixel_start()) - start
- << endl;
-#endif
-
- context->rectangle (start, draw.y0, this_end - start, draw.height());
- context->set_source (image->image(), self.x0 + (image_to_window (self.x0, image->pixel_start()) - start), self.y0);
- context->fill ();
-
- start = this_end;
-
- }
+ context->rectangle (draw_start, draw.y0, draw_end - draw_start, draw.height());
+ context->set_source (_cache->image(), self.x0 + _cache->pixel_start(), self.y0);
+ context->fill ();
}
void
@@ -351,26 +277,15 @@ void
WaveView::invalidate_whole_cache ()
{
begin_visual_change ();
-
- for (list<CacheEntry*>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
- delete *i;
- }
-
- _cache.clear ();
-
+ delete _cache;
+ _cache = 0;
end_visual_change ();
}
void
WaveView::invalidate_image_cache ()
{
- begin_visual_change ();
-
- for (list<CacheEntry*>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
- (*i)->clear_image ();
- }
-
- end_visual_change ();
+ invalidate_whole_cache ();
}
void
@@ -485,19 +400,19 @@ WaveView::region_resized ()
end_change ();
}
-WaveView::CacheEntry::CacheEntry (WaveView const * wave_view, double pixel_start, double pixel_end)
+WaveView::CacheEntry::CacheEntry (WaveView const * wave_view, double pixel_start, double pixel_end,
+ framepos_t sample_start,framepos_t sample_end)
: _wave_view (wave_view)
, _pixel_start (pixel_start)
, _pixel_end (pixel_end)
+ , _sample_start (sample_start)
+ , _sample_end (sample_end)
, _n_peaks (_pixel_end - _pixel_start)
{
_peaks.reset (new PeakData[_n_peaks]);
- _sample_start = _wave_view->_region_start + pixel_start * _wave_view->_samples_per_pixel;
- _sample_end = _wave_view->_region_start + pixel_end * _wave_view->_samples_per_pixel;
-
_wave_view->_region->read_peaks (_peaks.get(), _n_peaks,
- _sample_start, _sample_end,
+ _sample_start, _sample_end - _sample_start,
_wave_view->_channel,
_wave_view->_samples_per_pixel);
}