initial semi-working attempt at getting waveview cache to work correctly
This commit is contained in:
parent
aaaeb958c1
commit
79384aeb66
@ -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 end () const {
|
||||
return _end;
|
||||
double pixel_end () const {
|
||||
return _pixel_end;
|
||||
}
|
||||
|
||||
double sample_start () const {
|
||||
return _sample_start;
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -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->pixel_start()
|
||||
<< " .. "
|
||||
<< image->end ()
|
||||
<< " pixels "
|
||||
<< to_pixel_offset (_region_start, image->start(), _samples_per_pixel)
|
||||
<< " .. "
|
||||
<< 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 ()
|
||||
|
Loading…
Reference in New Issue
Block a user