[Bf-blender-cvs] [2946f72a2a1] master: VSE: Use lines to draw waveform

Sebastian Parborg noreply at git.blender.org
Mon Aug 16 15:13:51 CEST 2021


Commit: 2946f72a2a1f4afc4967ceda28df4294de304b81
Author: Sebastian Parborg
Date:   Thu Jul 1 18:51:26 2021 +0200
Branches: master
https://developer.blender.org/rB2946f72a2a1f4afc4967ceda28df4294de304b81

VSE: Use lines to draw waveform

Refactor and improve waveform drawing.

Drawing now can use line strips to draw waveforms instead of only
triangle strips. This makes us able to properly visualize thin waveforms
as they would not be visible before. We now also draw the RMS value of
the waveform.

The waveform drawing is now also properly aligned to the screen pixels
to avoid flickering when transforming the strip.

Reviewed By: Richard Antalik

Differential Revision: https://developer.blender.org/D11184

===================================================================

M	extern/audaspace/bindings/C/AUD_Special.cpp
M	source/blender/editors/space_sequencer/sequencer_draw.c

===================================================================

diff --git a/extern/audaspace/bindings/C/AUD_Special.cpp b/extern/audaspace/bindings/C/AUD_Special.cpp
index 97e5f5540de..5cc33525d1d 100644
--- a/extern/audaspace/bindings/C/AUD_Special.cpp
+++ b/extern/audaspace/bindings/C/AUD_Special.cpp
@@ -247,7 +247,7 @@ AUD_API int AUD_readSound(AUD_Sound* sound, float* buffer, int length, int sampl
 
 		buffer[i * 3] = min;
 		buffer[i * 3 + 1] = max;
-		buffer[i * 3 + 2] = sqrt(power) / len;
+		buffer[i * 3 + 2] = sqrt(power / len); // RMS
 
 		if(overallmax < max)
 			overallmax = max;
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index e49a88c88d2..888e232ce45 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -228,9 +228,93 @@ void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3])
   }
 }
 
+typedef struct WaveVizData {
+  float pos[2];
+  float rms_pos;
+  bool clip;
+  bool end;
+} WaveVizData;
+
+static int get_section_len(WaveVizData *start, WaveVizData *end)
+{
+  int len = 0;
+  while (start != end) {
+    len++;
+    if (start->end) {
+      return len;
+    }
+    start++;
+  }
+  return len;
+}
+
+static void draw_waveform(WaveVizData *iter, WaveVizData *end, GPUPrimType prim_type, bool use_rms)
+{
+  int strip_len = get_section_len(iter, end);
+  if (strip_len > 1) {
+    GPU_blend(GPU_BLEND_ALPHA);
+    GPUVertFormat *format = immVertexFormat();
+    uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+    uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+
+    immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+    immBegin(prim_type, strip_len);
+
+    while (iter != end) {
+      if (iter->clip) {
+        immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f);
+      }
+      else if (use_rms) {
+        immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.8f);
+      }
+      else {
+        immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f);
+      }
+
+      if (use_rms) {
+        immVertex2f(pos, iter->pos[0], iter->rms_pos);
+      }
+      else {
+        immVertex2f(pos, iter->pos[0], iter->pos[1]);
+      }
+
+      if (iter->end) {
+        /* End of line. */
+        iter++;
+        strip_len = get_section_len(iter, end);
+        if (strip_len != 0) {
+          immEnd();
+          immUnbindProgram();
+          immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+          immBegin(prim_type, strip_len);
+        }
+      }
+      else {
+        iter++;
+      }
+    }
+    immEnd();
+    immUnbindProgram();
+
+    GPU_blend(GPU_BLEND_NONE);
+  }
+}
+
+static float clamp_frame_coord_to_pixel(float frame_coord,
+                                        float pixel_frac,
+                                        float frames_per_pixel)
+{
+  float cur_pixel = (frame_coord / frames_per_pixel);
+  float new_pixel = (int)(frame_coord / frames_per_pixel) + pixel_frac;
+  if (cur_pixel > new_pixel) {
+    new_pixel += 1.0f;
+  }
+  return new_pixel * frames_per_pixel;
+}
+
 /**
  * \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2.
- * \param stepsize: The width of a pixel.
+ * \param frames_per_pixel: The amount of pixels a whole frame takes up (x-axis direction).
  */
 static void draw_seq_waveform_overlay(View2D *v2d,
                                       const bContext *C,
@@ -241,29 +325,34 @@ static void draw_seq_waveform_overlay(View2D *v2d,
                                       float y1,
                                       float x2,
                                       float y2,
-                                      float stepsize)
+                                      float frames_per_pixel)
 {
-  /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */
-  int x1_offset = max_ff(v2d->cur.xmin, x1);
-  int x2_offset = min_ff(v2d->cur.xmax + 1.0f, x2);
-
   if (seq->sound && ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) {
-    int length = floor((x2_offset - x1_offset) / stepsize) + 1;
-    float ymid = (y1 + y2) / 2.0f;
-    float yscale = (y2 - y1) / 2.0f;
-    float samplestep;
-    float startsample, endsample;
-    float volume = seq->volume;
-    float value1, value2;
-    bSound *sound = seq->sound;
-    SoundWaveform *waveform;
-
-    if (length < 2) {
+    /* Make sure that the start drawing position is aligned to the pixels on the screen to avoid
+     * flickering whem moving around the strip.
+     * To do this we figure out the fractional offset in pixel space by checking where the
+     * window starts.
+     * We then append this pixel offset to our strip start coordiate to ensure we are aligned to
+     * the screen pixel grid. */
+    float pixel_frac = v2d->cur.xmin / frames_per_pixel - floor(v2d->cur.xmin / frames_per_pixel);
+    float x1_adj = clamp_frame_coord_to_pixel(x1, pixel_frac, frames_per_pixel);
+
+    /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */
+    float x1_offset = max_ff(v2d->cur.xmin, x1_adj);
+    float x2_offset = min_ff(v2d->cur.xmax, x2);
+
+    /* Calculate how long the strip that is in view is in pixels. */
+    int pix_strip_len = round((x2_offset - x1_offset) / frames_per_pixel);
+
+    if (pix_strip_len < 2) {
       return;
     }
 
+    bSound *sound = seq->sound;
+
     BLI_spin_lock(sound->spinlock);
     if (!sound->waveform) {
+      /* Load the waveform data if it hasn't been loaded and cached already. */
       if (!(sound->tags & SOUND_TAGS_WAVEFORM_LOADING)) {
         /* Prevent sounds from reloading. */
         sound->tags |= SOUND_TAGS_WAVEFORM_LOADING;
@@ -277,89 +366,188 @@ static void draw_seq_waveform_overlay(View2D *v2d,
     }
     BLI_spin_unlock(sound->spinlock);
 
-    waveform = sound->waveform;
+    SoundWaveform *waveform = sound->waveform;
 
     /* Waveform could not be built. */
     if (waveform->length == 0) {
       return;
     }
 
-    startsample = (seq->startofs + seq->anim_startofs) / FPS * SOUND_WAVE_SAMPLES_PER_SECOND;
-    startsample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND;
+    /* F-curve lookup is quite expensive, so do this after precondition. */
+    FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL);
 
-    endsample = (seq->enddisp - seq->startdisp) / FPS * SOUND_WAVE_SAMPLES_PER_SECOND;
-    endsample += startsample;
+    WaveVizData *tri_strip_arr = MEM_callocN(sizeof(*tri_strip_arr) * pix_strip_len * 2,
+                                             "tri_strip");
+    WaveVizData *line_strip_arr = MEM_callocN(sizeof(*line_strip_arr) * pix_strip_len,
+                                              "line_strip");
 
-    samplestep = (endsample - startsample) * stepsize / (x2 - x1);
+    WaveVizData *tri_strip_iter = tri_strip_arr;
+    WaveVizData *line_strip_iter = line_strip_arr;
 
-    length = min_ii(
-        floor((waveform->length - startsample) / samplestep - (x1_offset - x1) / stepsize),
-        length);
+    /* The y coordinate for the middle of the strip. */
+    float y_mid = (y1 + y2) / 2.0f;
+    /* The lenght from the middle of the strip to the top/bottom. */
+    float y_scale = (y2 - y1) / 2.0f;
+    float volume = seq->volume;
 
-    if (length < 2) {
-      return;
-    }
+    /* Value to keep track if the previous item to be drawn was a line strip. */
+    int8_t was_line_strip = -1; /* -1 == no previous value. */
 
-    /* F-curve lookup is quite expensive, so do this after precondition. */
-    FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL);
+    float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS;
 
-    GPU_blend(GPU_BLEND_ALPHA);
-    GPUVertFormat *format = immVertexFormat();
-    uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
-    uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
-    immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
-    immBegin(GPU_PRIM_TRI_STRIP, length * 2);
+    /* How many samples do we have for each pixel? */
+    float samples_per_pix = samples_per_frame * frames_per_pixel;
 
-    for (int i = 0; i < length; i++) {
-      float sampleoffset = startsample + ((x1_offset - x1) / stepsize + i) * samplestep;
-      int p = sampleoffset;
+    float strip_start_offset = seq->startofs + seq->anim_startofs;
+    float start_sample = 0;
 
-      value1 = waveform->data[p * 3];
-      value2 = waveform->data[p * 3 + 1];
+    if (strip_start_offset != 0) {
+      /* If start offset is not zero, we need to make sure that we pick the same start sample as if
+       * we simply scrolled the start of the strip offscreen. Otherwise we will get flickering when
+       * changing start offset as the pixel alignment will not be the same for the drawn samples.
+       */
+      strip_start_offset = clamp_frame_coord_to_pixel(
+          x1 - strip_start_offset, pixel_frac, frames_per_pixel);
+      start_sample = fabsf(strip_start_offset - x1_adj) * samples_per_frame;
+    }
 
-      if (samplestep > 1.0f) {
-        for (int j = p + 1; (j < waveform->length) && (j < p + samplestep); j++) {
-          if (value1 > waveform->data[j * 3]) {
-            value1 = waveform->data[j * 3];
-          }
+    start_sample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND;
+    /* If we scrolled the start off-screen, then the start sample should be at the first visible
+     * sample. */
+    start_sample += (x1_offset - x1_adj) * samples_per_frame;
 
-          if (value2 < waveform->data[j * 3 + 1]) {
-            value2 = waveform->data[j * 3 + 1];
-          }
-        }
+    for (int i = 0; i < pix_strip_len; i++) {
+      float sample_offset = start_sample + i * samples_per_pix;
+      int p = sample_offset;
+
+      if (p >= waveform->length) {
+        break;
       }
-      else if (p + 1 < waveform->length) {
+
+      float value_min = waveform->data[p * 3];
+      float value_max = waveform->data[p * 3 + 1];
+      float rms = waveform->data[p * 3 + 2];
+
+      if (p + 1 < waveform->length) {
         /* Use simple linear interpolation

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list