fix crash when copy'ing latent plugins
[ardour.git] / libs / canvas / wave_view.cc
index a193e8c33945fd64a6e316c88c15bbf6e1b84bbb..17ee8609ff79dc641d084ea5bba29430f8a0514d 100644 (file)
@@ -73,6 +73,14 @@ WaveView::DrawingRequestQueue WaveView::request_queue;
 PBD::Signal0<void> WaveView::VisualPropertiesChanged;
 PBD::Signal0<void> WaveView::ClipLevelChanged;
 
+/* NO_THREAD_WAVEVIEWS is defined by the top level wscript
+ * if --no-threaded-waveviws is provided at the configure step.
+ */
+
+#ifndef NO_THREADED_WAVEVIEWS
+#define ENABLE_THREADED_WAVEFORM_RENDERING
+#endif
+
 WaveView::WaveView (Canvas* c, boost::shared_ptr<ARDOUR::AudioRegion> region)
        : Item (c)
        , _region (region)
@@ -156,7 +164,7 @@ WaveView::debug_name() const
 void
 WaveView::image_ready ()
 {
-        DEBUG_TRACE (DEBUG::WaveView, string_compose ("queue draw for %1 at %2 (vis = %3 CR %4)\n", this, g_get_monotonic_time(), visible(), current_request));
+       DEBUG_TRACE (DEBUG::WaveView, string_compose ("queue draw for %1 at %2 (vis = %3 CR %4)\n", this, g_get_monotonic_time(), visible(), current_request));
        redraw ();
 }
 
@@ -262,7 +270,7 @@ WaveView::set_clip_level (double dB)
 void
 WaveView::invalidate_image_cache ()
 {
-        DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 invalidates image cache and cancels current request\n", this));
+       DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 invalidates image cache and cancels current request\n", this));
        cancel_my_render_request ();
        Glib::Threads::Mutex::Lock lci (current_image_lock);
        _current_image.reset ();
@@ -743,7 +751,7 @@ boost::shared_ptr<WaveViewCache::Entry>
 WaveView::cache_request_result (boost::shared_ptr<WaveViewThreadRequest> req) const
 {
        if (!req->image) {
-               cerr << "asked to cache null image!!!\n";
+               // cerr << "asked to cache null image!!!\n";
                return boost::shared_ptr<WaveViewCache::Entry> ();
        }
 
@@ -790,9 +798,9 @@ WaveView::get_image (framepos_t start, framepos_t end, bool& full_image) const
                 * while we're here.
                 */
 
-                DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 CR %2 stop? %3 image %4\n", this, current_request,
-                                                              (current_request ? current_request->should_stop() : false),
-                                                              (current_request ? current_request->image : 0)));
+               DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 CR %2 stop? %3 image %4\n", this, current_request,
+                                       (current_request ? current_request->should_stop() : false),
+                                       (current_request ? current_request->image : 0)));
 
                if (current_request && !current_request->should_stop() && current_request->image) {
 
@@ -835,7 +843,12 @@ WaveView::get_image (framepos_t start, framepos_t end, bool& full_image) const
 
        if (!ret || !full_image) {
 
-               if ((rendered && get_image_in_thread) || always_get_image_in_thread) {
+#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));
 
@@ -850,7 +863,7 @@ WaveView::get_image (framepos_t start, framepos_t end, bool& full_image) const
                        req->height = _height;
                        req->fill_color = _fill_color;
                        req->amplitude = _region_amplitude * _amplitude_above_axis;
-                        req->width = desired_image_width ();
+                       req->width = desired_image_width ();
 
                        /* draw image in this (the GUI thread) */
 
@@ -894,21 +907,21 @@ WaveView::get_image_from_cache (framepos_t start, framepos_t end, bool& full) co
 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.
-         */
+       /* 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;
+       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;
-        }
+       if (canvas_width_samples > one_tenth_of_second) {
+               return  canvas_width_samples;
+       }
 
-        return one_tenth_of_second;
+       return one_tenth_of_second;
 }
 
 void
@@ -925,13 +938,16 @@ WaveView::queue_get_image (boost::shared_ptr<const ARDOUR::Region> region, frame
        req->height = _height;
        req->fill_color = _fill_color;
        req->amplitude = _region_amplitude * _amplitude_above_axis;
-        req->width = desired_image_width ();
+       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 ();
+               Glib::Threads::Mutex::Lock lm (request_queue_lock);
+               if (current_request) {
+                       current_request->cancel ();
+               }
        }
 
        start_drawing_thread ();
@@ -942,14 +958,14 @@ WaveView::queue_get_image (boost::shared_ptr<const ARDOUR::Region> region, frame
                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));
+               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 ();
-                }
+               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 ();
+               }
        }
 }
 
@@ -960,8 +976,8 @@ WaveView::generate_image (boost::shared_ptr<WaveViewThreadRequest> req, bool in_
 
                /* 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.
+                * 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);
@@ -972,7 +988,9 @@ WaveView::generate_image (boost::shared_ptr<WaveViewThreadRequest> req, bool in_
 
                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));
+               const int n_peaks = std::max (1LL, llrint (ceil ((sample_end - sample_start) / (req->samples_per_pixel))));
+
+               assert (n_peaks > 0 && n_peaks < 32767);
 
                boost::scoped_array<ARDOUR::PeakData> peaks (new PeakData[n_peaks]);
 
@@ -986,7 +1004,19 @@ WaveView::generate_image (boost::shared_ptr<WaveViewThreadRequest> req, bool in_
                                                             req->channel,
                                                             req->samples_per_pixel);
 
+               if (req->should_stop()) {
+                       // cerr << "Request stopped after reading peaks\n";
+                       return;
+               }
+
                req->image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, n_peaks, req->height);
+
+               // http://cairographics.org/manual/cairo-Image-Surfaces.html#cairo-image-surface-create
+               // This function always returns a valid pointer, but it will return a pointer to a "nil" surface..
+               // but there's some evidence that req->image can be NULL.
+               // http://tracker.ardour.org/view.php?id=6478
+               assert (req->image);
+
                /* make sure we record the sample positions that were actually used */
                req->start = sample_start;
                req->end = sample_end;
@@ -1011,11 +1041,11 @@ WaveView::generate_image (boost::shared_ptr<WaveViewThreadRequest> req, bool in_
                        draw_absent_image (req->image, peaks.get(), n_peaks);
                }
        } else {
-               cerr << "Request stopped before image generation\n";
+               // 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));
+               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 */
        }
 
@@ -1045,7 +1075,7 @@ WaveView::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) cons
                return;
        }
 
-        DEBUG_TRACE (DEBUG::WaveView, string_compose ("render %1 at %2\n", this, g_get_monotonic_time()));
+       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
@@ -1200,13 +1230,12 @@ WaveView::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) cons
                 * draw or the available width of the image.
                 */
 
-               draw_width = min ((double) image_to_draw->image->get_width() - (draw_start - image_to_draw->start),
-                                 (draw_end - draw_start));
+               draw_width = min ((double) image_to_draw->image->get_width(), (draw_end - draw_start));
 
 
-               DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 draw just %2 of %3 (iwidth %4 off %5 img @ %6 rs @ %7)\n", name, draw_width, (draw_end - draw_start),
+               DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 draw just %2 of %3 @ %8 (iwidth %4 off %5 img @ %6 rs @ %7)\n", name, draw_width, (draw_end - draw_start),
                                                              image_to_draw->image->get_width(), image_origin_in_self_coordinates,
-                                                             image_to_draw->start, _region_start));
+                                                             image_to_draw->start, _region_start, draw_start));
        } else {
                draw_width = draw_end - draw_start;
                DEBUG_TRACE (DEBUG::WaveView, string_compose ("use current image, span entire render width %1..%2\n", draw_start, draw_end));
@@ -1469,19 +1498,19 @@ WaveView::cancel_my_render_request () const
         * ever starting up.
         */
 
+       Glib::Threads::Mutex::Lock lm (request_queue_lock);
+
        if (current_request) {
                current_request->cancel ();
        }
 
-       Glib::Threads::Mutex::Lock lm (request_queue_lock);
-
        /* now remove it from the queue and reset our request pointer so that
           have no outstanding request (that we know about)
        */
 
        request_queue.erase (this);
        current_request.reset ();
-        DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 now has no request %2\n", this));
+       DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 now has no request %2\n", this));
 
 }
 
@@ -1547,7 +1576,7 @@ WaveView::drawing_thread ()
                requestor = *(request_queue.begin());
                request_queue.erase (request_queue.begin());
 
-                DEBUG_TRACE (DEBUG::WaveView, string_compose ("start request for %1 at %2\n", requestor, g_get_monotonic_time()));
+               DEBUG_TRACE (DEBUG::WaveView, string_compose ("start request for %1 at %2\n", requestor, g_get_monotonic_time()));
 
                boost::shared_ptr<WaveViewThreadRequest> req = requestor->current_request;
 
@@ -1560,7 +1589,7 @@ WaveView::drawing_thread ()
                 * as we do rendering.
                 */
 
-               request_queue_lock.unlock (); /* some RAII would be good here */
+               lm.release (); /* some RAII would be good here */
 
                try {
                        requestor->generate_image (req, true);
@@ -1568,7 +1597,7 @@ WaveView::drawing_thread ()
                        req->image.clear(); /* just in case it was set before the exception, whatever it was */
                }
 
-               request_queue_lock.lock ();
+               lm.acquire ();
 
                req.reset (); /* drop/delete request as appropriate */
        }
@@ -1626,25 +1655,25 @@ WaveViewCache::lookup_image (boost::shared_ptr<ARDOUR::AudioSource> src,
                        continue;
                }
 
-                switch (Evoral::coverage (start, end, e->start, e->end)) {
-                case Evoral::OverlapExternal:  /* required range is inside image range */
-                       DEBUG_TRACE (DEBUG::WaveView, string_compose ("found image spanning %1..%2 covers %3..%4\n",
-                                                                     e->start, e->end, start, end));
-                       use (src, e);
-                        full_coverage = true;
-                        return e;
-
-                case Evoral::OverlapStart: /* required range start is covered by image range */
-                        if ((e->end - start) > max_coverage) {
-                                best_partial = e;
-                                max_coverage = e->end - start;
-                        }
-                        break;
-
-                case Evoral::OverlapNone:
-                case Evoral::OverlapEnd:
-                case Evoral::OverlapInternal:
-                        break;
+               switch (Evoral::coverage (start, end, e->start, e->end)) {
+                       case Evoral::OverlapExternal:  /* required range is inside image range */
+                               DEBUG_TRACE (DEBUG::WaveView, string_compose ("found image spanning %1..%2 covers %3..%4\n",
+                                                       e->start, e->end, start, end));
+                               use (src, e);
+                               full_coverage = true;
+                               return e;
+
+                       case Evoral::OverlapStart: /* required range start is covered by image range */
+                               if ((e->end - start) > max_coverage) {
+                                       best_partial = e;
+                                       max_coverage = e->end - start;
+                               }
+                               break;
+
+                       case Evoral::OverlapNone:
+                       case Evoral::OverlapEnd:
+                       case Evoral::OverlapInternal:
+                               break;
                }
        }