mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-07 23:35:03 +01:00
initial semi-working attempt at getting waveview cache to work correctly
This commit is contained in:
parent
aaaeb958c1
commit
79384aeb66
2 changed files with 102 additions and 85 deletions
|
|
@ -51,6 +51,25 @@ public:
|
||||||
Rectified,
|
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>);
|
WaveView (Group *, boost::shared_ptr<ARDOUR::AudioRegion>);
|
||||||
|
|
||||||
void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
|
void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
|
||||||
|
|
@ -122,15 +141,23 @@ private:
|
||||||
class CacheEntry
|
class CacheEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CacheEntry (WaveView const *, double, double, int);
|
CacheEntry (WaveView const *, double, double);
|
||||||
~CacheEntry ();
|
~CacheEntry ();
|
||||||
|
|
||||||
double start () const {
|
double pixel_start () const {
|
||||||
return _start;
|
return _pixel_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
double end () const {
|
double pixel_end () const {
|
||||||
return _end;
|
return _pixel_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
double sample_start () const {
|
||||||
|
return _sample_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
double sample_end () const {
|
||||||
|
return _sample_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::shared_array<ARDOUR::PeakData> peaks () const {
|
boost::shared_array<ARDOUR::PeakData> peaks () const {
|
||||||
|
|
@ -145,9 +172,11 @@ private:
|
||||||
|
|
||||||
WaveView const * _wave_view;
|
WaveView const * _wave_view;
|
||||||
|
|
||||||
double _start;
|
double _pixel_start;
|
||||||
double _end;
|
double _pixel_end;
|
||||||
int _n_peaks;
|
ARDOUR::framecnt_t _sample_start;
|
||||||
|
ARDOUR::framecnt_t _sample_end;
|
||||||
|
int _n_peaks;
|
||||||
|
|
||||||
boost::shared_array<ARDOUR::PeakData> _peaks;
|
boost::shared_array<ARDOUR::PeakData> _peaks;
|
||||||
Cairo::RefPtr<Cairo::ImageSurface> _image;
|
Cairo::RefPtr<Cairo::ImageSurface> _image;
|
||||||
|
|
|
||||||
|
|
@ -151,56 +151,50 @@ WaveView::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) cons
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect self = item_to_window (Rect (0.0, 0.0, _region->length() / _samples_per_pixel, _height));
|
Rect self = item_to_window (Rect (0.0, 0.0, floor (_region->length() / _samples_per_pixel), _height));
|
||||||
boost::optional<Rect> draw = self.intersection (area);
|
boost::optional<Rect> d = self.intersection (area);
|
||||||
|
|
||||||
if (!draw) {
|
if (!d) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rect draw = d.get();
|
||||||
|
|
||||||
/* pixel coordinates */
|
/* pixel coordinates */
|
||||||
|
|
||||||
double start = draw->x0;
|
double start = floor (draw.x0);
|
||||||
double const end = draw->x1;
|
double const end = ceil (draw.x1);
|
||||||
|
|
||||||
list<CacheEntry*>::iterator cache = _cache.begin ();
|
list<CacheEntry*>::iterator cache = _cache.begin ();
|
||||||
|
|
||||||
// cerr << name << " draw " << area << "self = " << self << "\n\twill use " << draw.get() << endl;
|
cache = _cache.begin ();
|
||||||
#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
|
|
||||||
|
|
||||||
while ((end - start) > 1.0) {
|
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 */
|
/* Step through cache entries that end at or before our current position */
|
||||||
|
|
||||||
while (cache != _cache.end() && (*cache)->end() <= start_sample_offset) {
|
for (; cache != _cache.end(); ++cache) {
|
||||||
++cache;
|
if ((*cache)->pixel_start() <= start) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now either:
|
/* Now either:
|
||||||
|
|
||||||
1. we have run out of cache entries
|
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.
|
Set up a pointer to the cache entry that we will use on this iteration.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CacheEntry* image = 0;
|
CacheEntry* image = 0;
|
||||||
|
const double BIG_IMAGE_SIZE = 32767.0;
|
||||||
|
|
||||||
if (cache == _cache.end ()) {
|
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
|
We would like to avoid lots of little images in the
|
||||||
cache, so when we create a new one, make it as wide
|
cache, so when we create a new one, make it as wide
|
||||||
as possible, within a sensible limit (here, the
|
as possible, within the limits inherent in Cairo.
|
||||||
visible width of the canvas we're on).
|
|
||||||
|
|
||||||
However, we don't want to try to make it larger than
|
However, we don't want to try to make it larger than
|
||||||
the region actually is, so clamp with that too.
|
the region actually is, so clamp with that too.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
double const rend = _region->length() / _samples_per_pixel;
|
double const rend = floor (_region->length() / _samples_per_pixel);
|
||||||
double const endpoint = min (rend, max (end, start + _canvas->visible_area().width()));
|
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);
|
_cache.push_back (c);
|
||||||
image = 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
|
/* Case 2: we have a cache entry, but it begins after
|
||||||
* start(_sample_offset), so we need another one for
|
* start, so we need another one for the missing section.
|
||||||
* the missing bit.
|
|
||||||
*
|
*
|
||||||
* Create a new cached image that extends as far as the
|
* Create a new cached image that extends as far as the
|
||||||
* next cached image's start, or the end of the region,
|
* 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_pixel;
|
||||||
double end_sample_offset;
|
|
||||||
int npeaks;
|
|
||||||
|
|
||||||
if (end_sample_offset < (*cache)->start()) {
|
if (end < (*cache)->pixel_start()) {
|
||||||
double const rend = _region->length() / _samples_per_pixel;
|
double const rend = floor (_region->length() / _samples_per_pixel);
|
||||||
end_sample_offset = to_src_sample_offset (_region_start, end_pixel, _samples_per_pixel);
|
end_pixel = min (rend, max (end, BIG_IMAGE_SIZE));
|
||||||
end_pixel = min (rend, end);
|
|
||||||
} else {
|
} else {
|
||||||
end_sample_offset = (*cache)->start();
|
end_pixel = (*cache)->pixel_start();
|
||||||
end_pixel = to_pixel_offset (_region_start, end_sample_offset, _samples_per_pixel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
npeaks = end_pixel - start;
|
cerr << "Create new cache entry to reach " << (*cache)->pixel_start()
|
||||||
assert (npeaks > 0);
|
<< " range is " << start << " .. " << end_pixel
|
||||||
|
<< endl;
|
||||||
|
|
||||||
CacheEntry* c = new CacheEntry (this,
|
CacheEntry* c = new CacheEntry (this, start, end_pixel);
|
||||||
start_sample_offset,
|
|
||||||
end_sample_offset,
|
|
||||||
npeaks);
|
|
||||||
|
|
||||||
cache = _cache.insert (cache, c);
|
cache = _cache.insert (cache, c);
|
||||||
++cache;
|
++cache;
|
||||||
|
|
@ -270,36 +263,28 @@ WaveView::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) cons
|
||||||
|
|
||||||
image = *cache;
|
image = *cache;
|
||||||
++cache;
|
++cache;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double this_end = min (end, image->pixel_end ());
|
||||||
|
double const image_origin = image->pixel_start ();
|
||||||
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);
|
|
||||||
#if 0
|
#if 0
|
||||||
cerr << "\t\tDraw image between "
|
cerr << "\t\tDraw image between "
|
||||||
<< start
|
<< start
|
||||||
<< " .. "
|
<< " .. "
|
||||||
<< this_end
|
<< this_end
|
||||||
<< " using image spanning "
|
<< " using image spanning "
|
||||||
<< image->start()
|
<< image->pixel_start()
|
||||||
<< " .. "
|
<< " .. "
|
||||||
<< image->end ()
|
<< image->pixel_end ()
|
||||||
<< " pixels "
|
|
||||||
<< to_pixel_offset (_region_start, image->start(), _samples_per_pixel)
|
|
||||||
<< " .. "
|
|
||||||
<< to_pixel_offset (_region_start, image->end(), _samples_per_pixel)
|
|
||||||
<< endl;
|
<< endl;
|
||||||
#endif
|
#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->set_source (image->image(), self.x0 - image_origin, self.y0);
|
||||||
context->fill ();
|
context->fill ();
|
||||||
|
|
||||||
start = this_end;
|
start = this_end;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -483,18 +468,21 @@ WaveView::region_resized ()
|
||||||
end_change ();
|
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)
|
: _wave_view (wave_view)
|
||||||
, _start (start)
|
, _pixel_start (pixel_start)
|
||||||
, _end (end)
|
, _pixel_end (pixel_end)
|
||||||
, _n_peaks (npeaks)
|
, _n_peaks (_pixel_end - _pixel_start)
|
||||||
{
|
{
|
||||||
_peaks.reset (new PeakData[_n_peaks]);
|
_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,
|
_wave_view->_region->read_peaks (_peaks.get(), _n_peaks,
|
||||||
(framecnt_t) floor (_start),
|
_sample_start, _sample_end,
|
||||||
(framecnt_t) ceil (_end - _start),
|
_wave_view->_channel,
|
||||||
_wave_view->_channel, _wave_view->_samples_per_pixel);
|
_wave_view->_samples_per_pixel);
|
||||||
}
|
}
|
||||||
|
|
||||||
WaveView::CacheEntry::~CacheEntry ()
|
WaveView::CacheEntry::~CacheEntry ()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue