X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fcanvas%2Fwave_view.cc;h=f4e56cb1d53f6fb2af75ca26a69276fdd9293efc;hb=bace0e85dcc1d6d06ded3d6174fbdc803d180744;hp=3a04961d129bee9c91c5afd7bea0e411cb8f97de;hpb=dece3c20cabb7ce0a9851891ea9aacaf8042ce36;p=ardour.git diff --git a/libs/canvas/wave_view.cc b/libs/canvas/wave_view.cc index 3a04961d12..f4e56cb1d5 100644 --- a/libs/canvas/wave_view.cc +++ b/libs/canvas/wave_view.cc @@ -37,6 +37,7 @@ #include "ardour/lmath.h" #include "ardour/audioregion.h" #include "ardour/audiosource.h" +#include "ardour/session.h" #include "canvas/canvas.h" #include "canvas/colors.h" @@ -44,6 +45,8 @@ #include "canvas/utils.h" #include "canvas/wave_view.h" +#include "evoral/Range.hpp" + #include #include "gtkmm2ext/gui_thread.h" @@ -87,7 +90,6 @@ WaveView::WaveView (Canvas* c, boost::shared_ptr region) , _amplitude_above_axis (1.0) , _region_amplitude (region->scale_amplitude ()) , _start_shift (0.0) - , idle_queued (false) , _region_start (region->start()) , get_image_in_thread (false) , always_get_image_in_thread (false) @@ -121,7 +123,6 @@ WaveView::WaveView (Item* parent, boost::shared_ptr region) , _amplitude_above_axis (1.0) , _region_amplitude (region->scale_amplitude ()) , _start_shift (0.0) - , idle_queued (false) , _region_start (region->start()) , get_image_in_thread (false) , always_get_image_in_thread (false) @@ -151,6 +152,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)); redraw (); } @@ -256,6 +258,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)); cancel_my_render_request (); _current_image.reset (); } @@ -396,7 +399,7 @@ WaveView::draw_image (Cairo::RefPtr& image, PeakData* _peak has been scaled by scale_amplitude() already. */ - const double clip_level = _clip_level * _region_amplitude; + const double clip_level = _clip_level * req->amplitude; if (_shape == WaveView::Rectified) { @@ -572,7 +575,7 @@ WaveView::draw_image (Cairo::RefPtr& image, PeakData* _peak outline_context->stroke (); } else { - const double height_2 = _height * .5; + const int height_zero = floor( _height * .5); for (int i = 0; i < n_peaks; ++i) { @@ -598,10 +601,11 @@ WaveView::draw_image (Cairo::RefPtr& image, PeakData* _peak } } - /* zero line, show only if there is enough spread */ + /* zero line, show only if there is enough spread + or the waveform line does not cross zero line */ - if (tips[i].spread >= 5.0 && show_zero_line()) { - zero_context->move_to (i, floor(height_2)); + if (show_zero_line() && ((tips[i].spread >= 5.0) || (tips[i].top > height_zero ) || (tips[i].bot < height_zero)) ) { + zero_context->move_to (i, height_zero); zero_context->rel_line_to (1.0, 0); } @@ -622,7 +626,11 @@ WaveView::draw_image (Cairo::RefPtr& image, PeakData* _peak clipped = true; } - if (!clipped) { + if (!clipped && tips[i].spread > 2.0) { + /* only draw the outline if the spread + implies 3 or more pixels (so that we see 1 + white pixel in the middle). + */ outline_context->move_to (i, tips[i].bot); /* normal lower terminal dot; line moves up */ outline_context->rel_line_to (0, -1.0); @@ -642,10 +650,15 @@ WaveView::draw_image (Cairo::RefPtr& image, PeakData* _peak } if (!clipped) { + /* special case where only 1 pixel of + * the waveform line is drawn (and + * nothing else). + * + * we draw a 1px "line", pretending + * that the span is 1.0 (whether it is + * zero or 1.0) + */ wave_context->move_to (i, tips[i].top); - /* special case where outline only is drawn. - * we draw a 1px "line", pretending that the span is 1.0 - */ wave_context->rel_line_to (0, 1.0); } } @@ -724,6 +737,7 @@ WaveView::draw_image (Cairo::RefPtr& image, PeakData* _peak boost::shared_ptr WaveView::cache_request_result (boost::shared_ptr req) const { + boost::shared_ptr ret (new WaveViewCache::Entry (req->channel, req->height, req->amplitude, @@ -766,6 +780,10 @@ WaveView::get_image (framepos_t start, framepos_t end, bool& full_image) const * have an image there. if so, use it (and put it in the cache * 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))); if (current_request && !current_request->should_stop() && current_request->image) { @@ -809,7 +827,7 @@ 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) { - + DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1: generating image in caller thread\n", name)); boost::shared_ptr req (new WaveViewThreadRequest); @@ -820,10 +838,10 @@ WaveView::get_image (framepos_t start, framepos_t end, bool& full_image) const 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->width = _canvas->visible_area().width(); 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) */ @@ -864,6 +882,26 @@ WaveView::get_image_from_cache (framepos_t start, framepos_t end, bool& full) co _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 region, framepos_t start, framepos_t end) const { @@ -875,10 +913,10 @@ WaveView::queue_get_image (boost::shared_ptr region, frame 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->width = _canvas->visible_area().width(); 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 @@ -894,44 +932,31 @@ WaveView::queue_get_image (boost::shared_ptr region, frame { Glib::Threads::Mutex::Lock lm (request_queue_lock); current_request = req; - } - /* this is always called from the GUI thread (there is only one), and - * the same thread runs the idle callback chain. thus we do not need - * any locks to protect idle_queued - it is only ever set or read in - * the unitary GUI thread. - */ - - if (!idle_queued) { - Glib::signal_idle().connect (sigc::mem_fun (*this, &WaveView::idle_send_request)); - idle_queued = true; - } -} + DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 now has current request %2\n", this, req)); -bool -WaveView::idle_send_request () const -{ - Glib::Threads::Mutex::Lock lm (request_queue_lock); - - request_queue.insert (this); - request_cond.signal (); /* wake thread - must be done while holding lock */ - idle_queued = false; - - return false; /* do not call from idle again */ + 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 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 twice the canvas width + * 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 * req->samples_per_pixel; /* one canvas width */ + const framecnt_t image_samples = req->width; /* we can request data from anywhere in the Source, between 0 and its length */ @@ -959,6 +984,12 @@ WaveView::generate_image (boost::shared_ptr req, bool in_ 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; @@ -973,6 +1004,7 @@ WaveView::generate_image (boost::shared_ptr req, bool in_ } 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(this)->ImageReady (); /* emit signal */ } @@ -1002,6 +1034,8 @@ 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())); + /* 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. @@ -1428,6 +1462,8 @@ WaveView::cancel_my_render_request () const request_queue.erase (this); current_request.reset (); + DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 now has no request %2\n", this)); + } void @@ -1488,6 +1524,8 @@ 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())); + boost::shared_ptr req = requestor->current_request; if (!req) { @@ -1565,25 +1603,25 @@ WaveViewCache::lookup_image (boost::shared_ptr src, continue; } - if (end <= e->end && start >= e->start) { - /* found an image that covers the range we need */ + 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; - } - - if (start >= e->start) { - /* found an image that covers the start, but not the - * end. See if it is longer than any other similar - * partial image that we've found so far. - */ - - if ((e->end - e->start) > max_coverage) { - best_partial = e; - max_coverage = e->end - e->start; - } + 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; } }