X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fcanvas%2Fwave_view.cc;h=17ee8609ff79dc641d084ea5bba29430f8a0514d;hb=c80e8727dffd2a0958ae377aff5f0e7e2904770f;hp=f1f1653d70c9846ea19b5a4d75db1f2305d506e9;hpb=b0518fa2be9bd078647ac3af6822b89f8897c899;p=ardour.git diff --git a/libs/canvas/wave_view.cc b/libs/canvas/wave_view.cc index f1f1653d70..17ee8609ff 100644 --- a/libs/canvas/wave_view.cc +++ b/libs/canvas/wave_view.cc @@ -73,7 +73,13 @@ WaveView::DrawingRequestQueue WaveView::request_queue; PBD::Signal0 WaveView::VisualPropertiesChanged; PBD::Signal0 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 region) : Item (c) @@ -158,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 (); } @@ -264,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 (); @@ -745,7 +751,7 @@ boost::shared_ptr WaveView::cache_request_result (boost::shared_ptr req) const { if (!req->image) { - cerr << "asked to cache null image!!!\n"; + // cerr << "asked to cache null image!!!\n"; return boost::shared_ptr (); } @@ -792,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) { @@ -857,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) */ @@ -901,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 @@ -932,13 +938,16 @@ WaveView::queue_get_image (boost::shared_ptr 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 (); @@ -949,14 +958,14 @@ WaveView::queue_get_image (boost::shared_ptr 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 (); + } } } @@ -967,8 +976,8 @@ WaveView::generate_image (boost::shared_ptr 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); @@ -979,7 +988,9 @@ WaveView::generate_image (boost::shared_ptr 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 peaks (new PeakData[n_peaks]); @@ -993,7 +1004,19 @@ WaveView::generate_image (boost::shared_ptr 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; @@ -1018,11 +1041,11 @@ WaveView::generate_image (boost::shared_ptr 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(this)->ImageReady (); /* emit signal */ } @@ -1052,7 +1075,7 @@ WaveView::render (Rect const & area, Cairo::RefPtr 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 @@ -1207,13 +1230,12 @@ WaveView::render (Rect const & area, Cairo::RefPtr 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)); @@ -1476,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)); } @@ -1554,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 req = requestor->current_request; @@ -1567,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); @@ -1575,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 */ } @@ -1633,25 +1655,25 @@ WaveViewCache::lookup_image (boost::shared_ptr 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; } }