+ const double clip_level = dB_to_coefficient (dB);
+ if (clip_level != _clip_level) {
+ _clip_level = clip_level;
+ ClipLevelChanged ();
+ }
+}
+
+void
+WaveView::invalidate_image_cache ()
+{
+ vector <uint32_t> deletion_list;
+ vector <CacheEntry> caches;
+
+ if (_image_cache.find (_region->audio_source ()) != _image_cache.end ()) {
+ caches = _image_cache.find (_region->audio_source ())->second;
+ } else {
+ return;
+ }
+
+ for (uint32_t i = 0; i < caches.size (); ++i) {
+
+ if (_channel != caches[i].channel
+ || _height != caches[i].height
+ || _region_amplitude != caches[i].amplitude
+ || _fill_color != caches[i].fill_color) {
+
+ continue;
+ }
+
+ deletion_list.push_back (i);
+
+ }
+
+ while (deletion_list.size() > 0) {
+ caches[deletion_list.back ()].image.clear ();
+ caches.erase (caches.begin() + deletion_list.back());
+ deletion_list.pop_back();
+ }
+
+ if (caches.size () == 0) {
+ _image_cache.erase(_region->audio_source ());
+ } else {
+ _image_cache[_region->audio_source ()] = caches;
+ }
+
+}
+
+void
+WaveView::consolidate_image_cache () const
+{
+ list <uint32_t> deletion_list;
+ vector <CacheEntry> caches;
+ uint32_t other_entries = 0;
+
+ if (_image_cache.find (_region->audio_source ()) != _image_cache.end ()) {
+ caches = _image_cache.find (_region->audio_source ())->second;
+ }
+
+ for (uint32_t i = 0; i < caches.size (); ++i) {
+
+ if (_channel != caches[i].channel
+ || _height != caches[i].height
+ || _region_amplitude != caches[i].amplitude
+ || _fill_color != caches[i].fill_color) {
+
+ other_entries++;
+ continue;
+ }
+
+ framepos_t segment_start = caches[i].start;
+ framepos_t segment_end = caches[i].end;
+
+ for (uint32_t j = i; j < caches.size (); ++j) {
+
+ if (i == j || _channel != caches[j].channel
+ || _height != caches[i].height
+ || _region_amplitude != caches[i].amplitude
+ || _fill_color != caches[i].fill_color) {
+
+ continue;
+ }
+
+ if (caches[j].start >= segment_start && caches[j].end <= segment_end) {
+
+ deletion_list.push_back (j);
+ }
+ }
+ }
+
+ deletion_list.sort ();
+ deletion_list.unique ();
+
+ while (deletion_list.size() > 0) {
+ caches[deletion_list.back ()].image.clear ();
+ caches.erase (caches.begin() + deletion_list.back ());
+ deletion_list.pop_back();
+ }
+
+ /* We don't care if this channel/height/amplitude has anything in the cache - just drop the Last Added entries
+ until we reach a size where there is a maximum of CACHE_HIGH_WATER + other entries.
+ */
+
+ while (caches.size() > CACHE_HIGH_WATER + other_entries) {
+ caches.front ().image.clear ();
+ caches.erase(caches.begin ());
+ }
+
+ if (caches.size () == 0) {
+ _image_cache.erase (_region->audio_source ());
+ } else {
+ _image_cache[_region->audio_source ()] = caches;
+ }
+}
+
+Coord
+WaveView::y_extent (double s, bool /*round_to_lower_edge*/) const
+{
+ /* it is important that this returns an integral value, so that we
+ * can ensure correct single pixel behaviour.
+ *
+ * we need (_height - max(wave_line_width))
+ * wave_line_width == 1 IFF top==bottom (1 sample per pixel or flat line)
+ * wave_line_width == 2 otherwise
+ * then round away from the zero line, towards peak
+ */
+ if (_shape == Rectified) {
+ // we only ever have 1 point and align to the bottom (not center)
+ return floor ((1.0 - s) * (_height - 2.0));
+ } else {
+ /* currently canvas rectangle is off-by-one and we
+ * cannot draw a pixel at 0 (-.5 .. +.5) without it being
+ * clipped. A value 1.0 (ideally one point at y=0) ends
+ * up a pixel down. and a value of -1.0 (ideally y = _height-1)
+ * currently is on the bottom separator line :(
+ * So to make the complete waveform appear centered in
+ * a region, we translate by +.5 (instead of -.5)
+ * and waste two pixel of height: -4 (instad of -2)
+ *
+ * This needs fixing in canvas/rectangle the intersect
+ * functions and probably a couple of other places as well...
+ */
+ Coord pos;
+ if (s < 0) {
+ pos = ceil ((1.0 - s) * .5 * (_height - 4.0));
+ } else {
+ pos = floor ((1.0 - s) * .5 * (_height - 4.0));
+ }
+ return min (_height - 4.0, (max (0.0, pos)));
+ }