+ if (current_request->start <= start && current_request->end >= end) {
+
+ ret.reset (new WaveViewCache::Entry (current_request->channel,
+ current_request->height,
+ current_request->amplitude,
+ current_request->fill_color,
+ current_request->samples_per_pixel,
+ current_request->start,
+ current_request->end,
+ current_request->image));
+
+ cache_request_result (current_request);
+ DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1: got image from completed request, spans %2..%3\n",
+ name, current_request->start, current_request->end));
+ }
+
+ /* drop our handle on the current request */
+ current_request.reset ();
+ }
+ }
+
+ if (!ret) {
+
+ /* no current image draw request, so look in the cache */
+
+ ret = get_image_from_cache (start, end, full_image);
+ DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1: lookup from cache gave %2 (full %3)\n",
+ name, ret, full_image));
+
+ }
+
+
+
+ if (!ret || !full_image) {
+
+#ifndef ENABLE_THREADED_WAVEFORM_RENDERING
+ if (1)
+#else
+ if ((rendered && get_image_in_thread) || always_get_image_in_thread)
+#endif
+ {
+
+ DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1: generating image in caller thread\n", name));
+
+ boost::shared_ptr<WaveViewThreadRequest> req (new WaveViewThreadRequest);
+
+ req->type = WaveViewThreadRequest::Draw;
+ req->start = start;
+ req->end = end;
+ req->samples_per_pixel = _samples_per_pixel;
+ req->region = _region; /* weak ptr, to avoid storing a reference in the request queue */
+ req->channel = _channel;
+ req->height = _height;
+ req->fill_color = _fill_color;
+ req->amplitude = _region_amplitude * _amplitude_above_axis;
+ req->width = desired_image_width ();
+
+ /* draw image in this (the GUI thread) */
+
+ generate_image (req, false);
+
+ /* cache the result */
+
+ ret = cache_request_result (req);
+
+ /* reset this so that future missing images are
+ * generated in a a worker thread.
+ */
+
+ get_image_in_thread = false;
+
+ } else {
+ queue_get_image (_region, start, end);
+ }
+ }
+
+ if (ret) {
+ DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 got an image from %2 .. %3 (full ? %4)\n", name, ret->start, ret->end, full_image));
+ } else {
+ DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 no useful image available\n", name));
+ }
+
+ return ret;
+}
+
+boost::shared_ptr<WaveViewCache::Entry>
+WaveView::get_image_from_cache (framepos_t start, framepos_t end, bool& full) const
+{
+ if (!images) {
+ return boost::shared_ptr<WaveViewCache::Entry>();
+ }
+
+ return images->lookup_image (_region->audio_source (_channel), start, end, _channel,
+ _height, _region_amplitude * _amplitude_above_axis, _fill_color, _samples_per_pixel, full);
+}
+
+framecnt_t
+WaveView::desired_image_width () const
+{
+ /* compute how wide the image should be, in samples.
+ *
+ * We want at least 1 canvas width's worth, but if that
+ * represents less than 1/10th of a second, use 1/10th of
+ * a second instead.
+ */
+
+ framecnt_t canvas_width_samples = _canvas->visible_area().width() * _samples_per_pixel;
+ const framecnt_t one_tenth_of_second = _region->session().frame_rate() / 10;
+
+ if (canvas_width_samples > one_tenth_of_second) {
+ return canvas_width_samples;
+ }
+
+ return one_tenth_of_second;
+}
+
+void
+WaveView::queue_get_image (boost::shared_ptr<const ARDOUR::Region> region, framepos_t start, framepos_t end) const
+{
+ boost::shared_ptr<WaveViewThreadRequest> req (new WaveViewThreadRequest);
+
+ req->type = WaveViewThreadRequest::Draw;
+ req->start = start;
+ req->end = end;
+ req->samples_per_pixel = _samples_per_pixel;
+ req->region = _region; /* weak ptr, to avoid storing a reference in the request queue */
+ req->channel = _channel;
+ req->height = _height;
+ req->fill_color = _fill_color;
+ req->amplitude = _region_amplitude * _amplitude_above_axis;
+ req->width = desired_image_width ();
+
+ if (current_request) {
+ /* this will stop rendering in progress (which might otherwise
+ be long lived) for any current request.
+ */
+ current_request->cancel ();
+ }
+
+ start_drawing_thread ();
+
+ /* swap requests (protected by lock) */
+
+ {
+ Glib::Threads::Mutex::Lock lm (request_queue_lock);
+ current_request = req;
+
+ DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 now has current request %2\n", this, req));
+
+ if (request_queue.insert (this).second) {
+ /* this waveview was not already in the request queue, make sure we wake
+ the rendering thread in case it is asleep.
+ */
+ request_cond.signal ();
+ }
+ }
+}
+
+void
+WaveView::generate_image (boost::shared_ptr<WaveViewThreadRequest> req, bool in_render_thread) const
+{
+ if (!req->should_stop()) {
+
+ /* sample position is canonical here, and we want to generate
+ * an image that spans about 3x the canvas width. We get to that
+ * width by using an image sample count of the screen width added
+ * on each side of the desired image center.
+ */
+
+ const framepos_t center = req->start + ((req->end - req->start) / 2);
+ const framecnt_t image_samples = req->width;
+
+ /* we can request data from anywhere in the Source, between 0 and its length
+ */
+
+ framepos_t sample_start = max (_region_start, (center - image_samples));
+ framepos_t sample_end = min (center + image_samples, region_end());
+ const int n_peaks = llrintf ((sample_end - sample_start)/ (req->samples_per_pixel));
+
+ boost::scoped_array<ARDOUR::PeakData> peaks (new PeakData[n_peaks]);
+
+ /* Note that Region::read_peaks() takes a start position based on an
+ offset into the Region's **SOURCE**, rather than an offset into
+ the Region itself.
+ */
+
+ framecnt_t peaks_read = _region->read_peaks (peaks.get(), n_peaks,
+ sample_start, sample_end - sample_start,
+ req->channel,
+ req->samples_per_pixel);
+
+ req->image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, n_peaks, req->height);
+ /* make sure we record the sample positions that were actually used */
+ req->start = sample_start;
+ req->end = sample_end;
+
+ if (peaks_read > 0) {
+
+ /* region amplitude will have been used to generate the
+ * peak values already, but not the visual-only
+ * amplitude_above_axis. So apply that here before
+ * rendering.
+ */
+
+ if (_amplitude_above_axis != 1.0) {
+ for (framecnt_t i = 0; i < n_peaks; ++i) {
+ peaks[i].max *= _amplitude_above_axis;
+ peaks[i].min *= _amplitude_above_axis;
+ }
+ }
+
+ draw_image (req->image, peaks.get(), n_peaks, req);
+ } else {
+ draw_absent_image (req->image, peaks.get(), n_peaks);
+ }
+ } else {
+ cerr << "Request stopped before image generation\n";
+ }
+
+ if (in_render_thread && !req->should_stop()) {
+ DEBUG_TRACE (DEBUG::WaveView, string_compose ("done with request for %1 at %2 CR %3 req %4 range %5 .. %6\n", this, g_get_monotonic_time(), current_request, req, req->start, req->end));
+ const_cast<WaveView*>(this)->ImageReady (); /* emit signal */
+ }
+
+ return;
+}
+
+/** Given a waveform that starts at window x-coordinate @param wave_origin
+ * and the first pixel that we will actually draw @param draw_start, return
+ * the offset into an image of the entire waveform that we will need to use.
+ *
+ * Note: most of our cached images are NOT of the entire waveform, this is just
+ * computationally useful when determining which the sample range span for
+ * the image we need.
+ */
+static inline double
+window_to_image (double wave_origin, double image_start)
+{
+ return image_start - wave_origin;
+}
+
+void
+WaveView::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
+{
+ assert (_samples_per_pixel != 0);
+
+ if (!_region) {
+ return;
+ }
+
+ DEBUG_TRACE (DEBUG::WaveView, string_compose ("render %1 at %2\n", this, g_get_monotonic_time()));
+
+ /* a WaveView is intimately connected to an AudioRegion. It will
+ * display the waveform within the region, anywhere from the start of
+ * the region to its end.
+ *
+ * the area we've been aked to render may overlap with area covered
+ * by the region in any of the normal ways:
+ *
+ * - it may begin and end within the area covered by the region
+ * - it may start before and end after the area covered by region
+ * - it may start before and end within the area covered by the region
+ * - it may start within and end after the area covered by the region
+ * - it may be precisely coincident with the area covered by region.
+ *
+ * So let's start by determining the area covered by the region, in
+ * window coordinates. It begins at zero (in item coordinates for this
+ * waveview, and extends to region_length() / _samples_per_pixel.
+ */
+
+ Rect self = item_to_window (Rect (0.0, 0.0, region_length() / _samples_per_pixel, _height));
+
+ // cerr << name << " RENDER " << area << " self = " << self << endl;
+
+ /* Now lets get the intersection with the area we've been asked to draw */
+
+ boost::optional<Rect> d = self.intersection (area);
+
+ if (!d) {
+ return;
+ }
+
+ Rect draw = d.get();
+
+ /* "draw" is now a rectangle that defines the rectangle we need to
+ * update/render the waveview into, in window coordinate space.
+ */
+
+ /* window coordinates - pixels where x=0 is the left edge of the canvas
+ * window. We round down in case we were asked to
+ * draw "between" pixels at the start and/or end.
+ */
+
+ double draw_start = floor (draw.x0);
+ const double draw_end = floor (draw.x1);
+
+ // cerr << "Need to draw " << draw_start << " .. " << draw_end << " vs. " << area << " and self = " << self << endl;
+
+ /* image coordnates: pixels where x=0 is the start of this waveview,