diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2013-06-18 23:03:20 -0400 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2013-06-18 23:03:20 -0400 |
commit | 79384aeb66cabf52dbbc428903210c6c858c47f9 (patch) | |
tree | 4eb44d823bec6992dd43b87dce778277f6031544 /libs | |
parent | aaaeb958c1f493723d2768cbe772c2b11d9f0951 (diff) |
initial semi-working attempt at getting waveview cache to work correctly
Diffstat (limited to 'libs')
-rw-r--r-- | libs/canvas/canvas/wave_view.h | 45 | ||||
-rw-r--r-- | libs/canvas/wave_view.cc | 142 |
2 files changed, 102 insertions, 85 deletions
diff --git a/libs/canvas/canvas/wave_view.h b/libs/canvas/canvas/wave_view.h index b75b8c5312..e4e9d9b7ed 100644 --- a/libs/canvas/canvas/wave_view.h +++ b/libs/canvas/canvas/wave_view.h @@ -51,6 +51,25 @@ public: Rectified, }; + /* 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 (Group *, boost::shared_ptr<ARDOUR::AudioRegion>); void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const; @@ -122,15 +141,23 @@ private: class CacheEntry { public: - CacheEntry (WaveView const *, double, double, int); + CacheEntry (WaveView const *, double, double); ~CacheEntry (); - double start () const { - return _start; + double pixel_start () const { + return _pixel_start; + } + + double pixel_end () const { + return _pixel_end; + } + + double sample_start () const { + return _sample_start; } - double end () const { - return _end; + double sample_end () const { + return _sample_end; } boost::shared_array<ARDOUR::PeakData> peaks () const { @@ -145,9 +172,11 @@ private: WaveView const * _wave_view; - double _start; - double _end; - int _n_peaks; + double _pixel_start; + double _pixel_end; + ARDOUR::framecnt_t _sample_start; + ARDOUR::framecnt_t _sample_end; + int _n_peaks; boost::shared_array<ARDOUR::PeakData> _peaks; Cairo::RefPtr<Cairo::ImageSurface> _image; diff --git a/libs/canvas/wave_view.cc b/libs/canvas/wave_view.cc index c22ee54d50..a918dfddad 100644 --- a/libs/canvas/wave_view.cc +++ b/libs/canvas/wave_view.cc @@ -151,56 +151,50 @@ WaveView::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) cons return; } - Rect self = item_to_window (Rect (0.0, 0.0, _region->length() / _samples_per_pixel, _height)); - boost::optional<Rect> draw = self.intersection (area); + Rect self = item_to_window (Rect (0.0, 0.0, floor (_region->length() / _samples_per_pixel), _height)); + boost::optional<Rect> d = self.intersection (area); - if (!draw) { + if (!d) { return; } + + Rect draw = d.get(); /* pixel coordinates */ - double start = draw->x0; - double const end = draw->x1; + double start = floor (draw.x0); + double const end = ceil (draw.x1); list<CacheEntry*>::iterator cache = _cache.begin (); - // cerr << name << " draw " << area << "self = " << self << "\n\twill use " << draw.get() << endl; -#if 0 - cerr << " Cache contains " << _cache.size() << endl; - while (cache != _cache.end()) { - cerr << "\tsample span " << (*cache)->start() << " .. " << (*cache)->end() - << " pixel span " - << to_pixel_offset (_region_start, (*cache)->start(), _samples_per_pixel) - << " .. " - << to_pixel_offset (_region_start, (*cache)->end(), _samples_per_pixel) - << endl; - ++cache; - } - cache = _cache.begin(); -#endif + cache = _cache.begin (); while ((end - start) > 1.0) { - // cerr << "***** RANGE = " << start << " .. " << end << " = " << end - start << endl; - - frameoffset_t start_sample_offset = to_src_sample_offset (_region_start, start, _samples_per_pixel); - /* Step through cache entries that end at or before our current position */ - while (cache != _cache.end() && (*cache)->end() <= start_sample_offset) { - ++cache; + for (; cache != _cache.end(); ++cache) { + if ((*cache)->pixel_start() <= start) { + break; + } } /* Now either: + 1. we have run out of cache entries - 2. the one we are looking at finishes after start(_sample_offset) but also starts after start(_sample_offset). - 3. the one we are looking at finishes after start(_sample_offset) and starts before start(_sample_offset). + + 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; + const double BIG_IMAGE_SIZE = 32767.0; if (cache == _cache.end ()) { @@ -209,54 +203,53 @@ WaveView::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) cons 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 a sensible limit (here, the - visible width of the canvas we're on). + as possible, within the limits inherent in Cairo. However, we don't want to try to make it larger than the region actually is, so clamp with that too. */ - double const rend = _region->length() / _samples_per_pixel; - double const endpoint = min (rend, max (end, start + _canvas->visible_area().width())); + double const rend = floor (_region->length() / _samples_per_pixel); + double const end_pixel = min (rend, max (end, BIG_IMAGE_SIZE)); + + if ((end_pixel - start) < 1.0) { + /* nothing more to draw */ + break; + } + + cerr << "Create new cache entry to grow cache," + << " range is " << start << " .. " << end_pixel + << endl; + + CacheEntry* c = new CacheEntry (this, start, end_pixel); - CacheEntry* c = new CacheEntry (this, - start_sample_offset, - to_src_sample_offset (_region_start, endpoint, _samples_per_pixel), - endpoint - start); _cache.push_back (c); image = c; - } else if ((*cache)->start() > start_sample_offset) { + } else if ((*cache)->pixel_start() > start) { - /* Case 2: we have a cache entry, but it starts after - * start(_sample_offset), so we need another one for - * the missing bit. + /* 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 the render area, whichever comes first. + * or the end of a BIG_IMAGE, whichever comes first. */ - double end_pixel; - double end_sample_offset; - int npeaks; + double end_pixel; - if (end_sample_offset < (*cache)->start()) { - double const rend = _region->length() / _samples_per_pixel; - end_sample_offset = to_src_sample_offset (_region_start, end_pixel, _samples_per_pixel); - end_pixel = min (rend, end); + if (end < (*cache)->pixel_start()) { + double const rend = floor (_region->length() / _samples_per_pixel); + end_pixel = min (rend, max (end, BIG_IMAGE_SIZE)); } else { - end_sample_offset = (*cache)->start(); - end_pixel = to_pixel_offset (_region_start, end_sample_offset, _samples_per_pixel); + end_pixel = (*cache)->pixel_start(); } + + cerr << "Create new cache entry to reach " << (*cache)->pixel_start() + << " range is " << start << " .. " << end_pixel + << endl; - npeaks = end_pixel - start; - assert (npeaks > 0); - - CacheEntry* c = new CacheEntry (this, - start_sample_offset, - end_sample_offset, - npeaks); + CacheEntry* c = new CacheEntry (this, start, end_pixel); cache = _cache.insert (cache, c); ++cache; @@ -270,36 +263,28 @@ WaveView::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) cons image = *cache; ++cache; - } - - - double this_end = min (end, to_pixel_offset (_region_start, image->end (), _samples_per_pixel)); - double const image_origin = to_pixel_offset (_region_start, image->start(), _samples_per_pixel); + double this_end = min (end, image->pixel_end ()); + double const image_origin = image->pixel_start (); #if 0 cerr << "\t\tDraw image between " << start << " .. " << this_end << " using image spanning " - << image->start() - << " .. " - << image->end () - << " pixels " - << to_pixel_offset (_region_start, image->start(), _samples_per_pixel) + << image->pixel_start() << " .. " - << to_pixel_offset (_region_start, image->end(), _samples_per_pixel) + << image->pixel_end () << endl; #endif - // cerr << "Fill rect " << draw->x0 << ", " << self.y0 << ' ' << draw->width() << " x " << draw->height() << endl; - - context->rectangle (start, draw->y0, this_end - start, _height); + context->rectangle (start, draw.y0, this_end - start, _height); context->set_source (image->image(), self.x0 - image_origin, self.y0); context->fill (); start = this_end; + } } @@ -483,18 +468,21 @@ WaveView::region_resized () end_change (); } -WaveView::CacheEntry::CacheEntry (WaveView const * wave_view, double start, double end, int npeaks) +WaveView::CacheEntry::CacheEntry (WaveView const * wave_view, double pixel_start, double pixel_end) : _wave_view (wave_view) - , _start (start) - , _end (end) - , _n_peaks (npeaks) + , _pixel_start (pixel_start) + , _pixel_end (pixel_end) + , _n_peaks (_pixel_end - _pixel_start) { _peaks.reset (new PeakData[_n_peaks]); + _sample_start = pixel_start * _wave_view->_samples_per_pixel; + _sample_end = pixel_end * _wave_view->_samples_per_pixel; + _wave_view->_region->read_peaks (_peaks.get(), _n_peaks, - (framecnt_t) floor (_start), - (framecnt_t) ceil (_end - _start), - _wave_view->_channel, _wave_view->_samples_per_pixel); + _sample_start, _sample_end, + _wave_view->_channel, + _wave_view->_samples_per_pixel); } WaveView::CacheEntry::~CacheEntry () |