[pitivi] previewers: Fix squished audio waveform
- From: Alexandru Băluț <alexbalut src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pitivi] previewers: Fix squished audio waveform
- Date: Sat, 29 Sep 2018 20:23:11 +0000 (UTC)
commit ba117e11ee9f119b9b2ce771d5236f98feb141b9
Author: Alexandru Băluț <alexandru balut gmail com>
Date: Wed Sep 19 04:10:05 2018 +0200
previewers: Fix squished audio waveform
Refactored the code to calculate `surface_width` correctly. As a result,
removed some handlers which are not needed anymore, along with the
`_force_redraw` field. Now when the pre-drawn `surface` is created we
store the start, end, zoom, which are checked later in `do_draw` to
determine whether a new pre-drawn `surface` is needed.
Delayed the conversion of float values to int as much as possible, to
avoid small inconsistencies in how we calculate the `offset` at which
the `surface` is drawn.
Fixes #2195
pitivi/timeline/previewers.py | 97 +++++++++++++++++++++----------------------
1 file changed, 47 insertions(+), 50 deletions(-)
---
diff --git a/pitivi/timeline/previewers.py b/pitivi/timeline/previewers.py
index a73e673a..f9541d27 100644
--- a/pitivi/timeline/previewers.py
+++ b/pitivi/timeline/previewers.py
@@ -61,8 +61,8 @@ THUMB_HEIGHT = EXPANDED_SIZE - 2 * THUMB_MARGIN_PX
THUMB_PERIOD = int(Gst.SECOND / 2)
assert Gst.SECOND % THUMB_PERIOD == 0
# For the waveforms, ensures we always have a little extra surface when
-# scrolling while playing.
-MARGIN = 500
+# scrolling while playing, in pixels.
+WAVEFORM_SURFACE_EXTRA_PX = 500
PREVIEW_GENERATOR_SIGNALS = {
"done": (GObject.SIGNAL_RUN_LAST, None, ()),
@@ -969,28 +969,20 @@ class AudioPreviewer(Previewer, Zoomable, Loggable):
self.samples = None
self.peaks = None
- self._end = 0
- self._surface_x = 0
+ self.surface = None
+ # The zoom level when self.surface has been created.
+ self._surface_zoom_level = 0
+ # The samples range used when self.surface has been created.
+ self._surface_start_ns = 0
+ self._surface_end_ns = 0
# Guard against malformed URIs
self.wavefile = None
self._uri = quote_uri(get_proxy_target(ges_elem).props.id)
self._num_failures = 0
- self.surface = None
-
- self._force_redraw = True
-
- self.ges_elem.connect("notify::in-point", self._inpoint_changed_cb)
- self.connect("notify::height-request", self._height_changed_cb)
self.become_controlled()
- def _inpoint_changed_cb(self, unused_b_element, unused_value):
- self._force_redraw = True
-
- def _height_changed_cb(self, unused_widget, unused_param_spec):
- self._force_redraw = True
-
def _startLevelsDiscovery(self):
filename = get_wavefile_location_for_uri(self._uri)
@@ -1026,9 +1018,6 @@ class AudioPreviewer(Previewer, Zoomable, Loggable):
bus.add_signal_watch()
bus.connect("message", self._busMessageCb)
- def zoomChanged(self):
- self._force_redraw = True
-
def _prepareSamples(self):
proxy = self.ges_elem.get_parent().get_asset().get_proxy_target()
self._wavebin.finalize(proxy=proxy)
@@ -1067,43 +1056,51 @@ class AudioPreviewer(Previewer, Zoomable, Loggable):
return True
return False
- def _get_num_inpoint_samples(self):
- if self.ges_elem.props.in_point:
- asset_duration = self.ges_elem.get_asset().get_filesource_asset().get_duration()
- return int(len(self.samples) / (float(asset_duration) / float(self.ges_elem.props.in_point)))
-
- return 0
-
- # pylint: disable=arguments-differ
+ # pylint: disable=arguments-differ,too-many-locals
def do_draw(self, context):
if not self.samples:
# Nothing to draw.
return
- clipped_rect = Gdk.cairo_get_clip_rectangle(context)[1]
-
- num_inpoint_samples = self._get_num_inpoint_samples()
- drawn_start = self.pixelToNs(clipped_rect.x)
- drawn_duration = self.pixelToNs(clipped_rect.width)
- start = int(drawn_start / SAMPLE_DURATION) + num_inpoint_samples
- end = int((drawn_start + drawn_duration) / SAMPLE_DURATION) + num_inpoint_samples
-
- if self._force_redraw or self._surface_x > clipped_rect.x or self._end < end:
- end = int(min(len(self.samples), end + (self.pixelToNs(MARGIN) /
- SAMPLE_DURATION)))
- self._end = end
- self._surface_x = clipped_rect.x
- surface_width = min(self.props.width_request - clipped_rect.x,
- clipped_rect.width + MARGIN)
- surface_height = int(self.get_parent().get_allocation().height)
- self.surface = renderer.fill_surface(self.samples[start:end],
- surface_width,
- surface_height)
-
- self._force_redraw = False
-
+ # The area we have to refresh is determined by the start and end
+ # calculated in the context of the asset duration.
+ rect = Gdk.cairo_get_clip_rectangle(context)[1]
+ inpoint = self.ges_elem.props.in_point
+ max_duration = self.ges_elem.get_asset().get_filesource_asset().get_duration()
+ start_ns = min(max(0, self.pixelToNs(rect.x) + inpoint), max_duration)
+ end_ns = min(max(0, self.pixelToNs(rect.x + rect.width) + inpoint), max_duration)
+
+ zoom = self.getCurrentZoomLevel()
+ height = self.get_allocation().height
+ if not self.surface or \
+ height != self.surface.get_height() or \
+ zoom != self._surface_zoom_level or \
+ start_ns < self._surface_start_ns or \
+ end_ns > self._surface_end_ns:
+ if self.surface:
+ self.surface.finish()
+ self.surface = None
+ self._surface_zoom_level = zoom
+ # The generated waveform is for an extended range if possible,
+ # so if the user scrolls we don't rebuild the waveform every time.
+ extra = self.pixelToNs(WAVEFORM_SURFACE_EXTRA_PX)
+ self._surface_start_ns = max(0, start_ns - extra)
+ self._surface_end_ns = min(end_ns + extra, max_duration)
+
+ range_start = min(max(0, int(self._surface_start_ns / SAMPLE_DURATION)), len(self.samples))
+ range_end = min(max(0, int(self._surface_end_ns / SAMPLE_DURATION)), len(self.samples))
+ samples = self.samples[range_start:range_end]
+ surface_width = self.nsToPixel(self._surface_end_ns - self._surface_start_ns)
+ self.surface = renderer.fill_surface(samples, surface_width, height)
+
+ # Paint the surface, ignoring the clipped rect.
+ # We only have to make sure the offset is correct:
+ # 1. + self._start_surface_ns, because that's the position of
+ # the surface in context, if the entire asset would be drawn.
+ # 2. - inpoint, because we're drawing a clip, not the entire asset.
context.set_operator(cairo.OPERATOR_OVER)
- context.set_source_surface(self.surface, self._surface_x, 0)
+ offset = self.nsToPixel(self._surface_start_ns - inpoint)
+ context.set_source_surface(self.surface, offset, 0)
context.paint()
def _emit_done_on_idle(self):
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]