Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/space_sequencer/sequencer_draw.c')
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c329
1 files changed, 259 insertions, 70 deletions
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 0472e1264ce..b3c39e2fa6f 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 when 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 coordinate 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,87 +366,187 @@ 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 = floor((seq->startofs + seq->anim_startofs) / FPS *
- SOUND_WAVE_SAMPLES_PER_SECOND);
- endsample = ceil((seq->startofs + seq->anim_startofs + seq->enddisp - seq->startdisp) / FPS *
- SOUND_WAVE_SAMPLES_PER_SECOND);
- samplestep = (endsample - startsample) * stepsize / (x2 - x1);
+ /* 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);
- length = min_ii(
- floor((waveform->length - startsample) / samplestep - (x1_offset - x1) / stepsize),
- length);
+ 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");
- if (length < 2) {
- return;
- }
+ WaveVizData *tri_strip_iter = tri_strip_arr;
+ WaveVizData *line_strip_iter = line_strip_arr;
- /* 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);
+ /* The y coordinate for the middle of the strip. */
+ float y_mid = (y1 + y2) / 2.0f;
+ /* The length from the middle of the strip to the top/bottom. */
+ float y_scale = (y2 - y1) / 2.0f;
+ float volume = seq->volume;
- 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);
+ /* 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. */
- for (int i = 0; i < length; i++) {
- float sampleoffset = startsample + ((x1_offset - x1) / stepsize + i) * samplestep;
- int p = sampleoffset;
+ float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS;
- value1 = waveform->data[p * 3];
- value2 = waveform->data[p * 3 + 1];
+ /* How many samples do we have for each pixel? */
+ float samples_per_pix = samples_per_frame * frames_per_pixel;
- 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];
- }
+ float strip_start_offset = seq->startofs + seq->anim_startofs;
+ float start_sample = 0;
- if (value2 < waveform->data[j * 3 + 1]) {
- value2 = waveform->data[j * 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 off-screen. 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;
+ }
+
+ 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;
+
+ 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. */
- float f = sampleoffset - p;
- value1 = (1.0f - f) * value1 + f * waveform->data[p * 3 + 3];
- value2 = (1.0f - f) * value2 + f * waveform->data[p * 3 + 4];
+ float f = sample_offset - p;
+ value_min = (1.0f - f) * value_min + f * waveform->data[p * 3 + 3];
+ value_max = (1.0f - f) * value_max + f * waveform->data[p * 3 + 4];
+ rms = (1.0f - f) * rms + f * waveform->data[p * 3 + 5];
+ if (samples_per_pix > 1.0f) {
+ /* We need to sum up the values we skip over until the next step. */
+ float next_pos = sample_offset + samples_per_pix;
+ int end_idx = next_pos;
+
+ for (int j = p + 1; (j < waveform->length) && (j < end_idx); j++) {
+ value_min = min_ff(value_min, waveform->data[j * 3]);
+ value_max = max_ff(value_max, waveform->data[j * 3 + 1]);
+ rms = max_ff(rms, waveform->data[j * 3 + 2]);
+ }
+ }
}
if (fcu && !BKE_fcurve_is_empty(fcu)) {
- float evaltime = x1_offset + (i * stepsize);
+ float evaltime = x1_offset + (i * frames_per_pixel);
volume = evaluate_fcurve(fcu, evaltime);
CLAMP_MIN(volume, 0.0f);
}
- value1 *= volume;
- value2 *= volume;
- if (value2 > 1 || value1 < -1) {
- immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f);
+ value_min *= volume;
+ value_max *= volume;
+ rms *= volume;
+
+ bool clipping = false;
- CLAMP_MAX(value2, 1.0f);
- CLAMP_MIN(value1, -1.0f);
+ if (value_max > 1 || value_min < -1) {
+ clipping = true;
+
+ CLAMP_MAX(value_max, 1.0f);
+ CLAMP_MIN(value_min, -1.0f);
}
- else {
- immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f);
+
+ bool is_line_strip = (value_max - value_min < 0.05f);
+
+ if (was_line_strip != -1 && is_line_strip != was_line_strip) {
+ /* If the previously added strip type isn't the same as the current one,
+ * add transition areas so they transition smoothly between each other. */
+ if (is_line_strip) {
+ /* This will be a line strip, end the tri strip. */
+ tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel;
+ tri_strip_iter->pos[1] = y_mid + value_min * y_scale;
+ tri_strip_iter->clip = clipping;
+ tri_strip_iter->rms_pos = tri_strip_iter->pos[1];
+ tri_strip_iter->end = true;
+
+ /* End of section. */
+ tri_strip_iter++;
+
+ /* Check if we are at the end.
+ * If so, skip one point line. */
+ if (i + 1 == pix_strip_len) {
+ continue;
+ }
+ }
+ else {
+ /* This will be a tri strip. */
+ line_strip_iter--;
+ tri_strip_iter->pos[0] = line_strip_iter->pos[0];
+ tri_strip_iter->pos[1] = line_strip_iter->pos[1];
+ tri_strip_iter->clip = line_strip_iter->clip;
+ tri_strip_iter->rms_pos = line_strip_iter->pos[1];
+ tri_strip_iter++;
+
+ /* Check if line had only one point. */
+ line_strip_iter--;
+ if (line_strip_iter < line_strip_arr || line_strip_iter->end) {
+ /* Only one point, skip it. */
+ line_strip_iter++;
+ }
+ else {
+ /* End of section. */
+ line_strip_iter++;
+ line_strip_iter->end = true;
+ line_strip_iter++;
+ }
+ }
}
- immVertex2f(pos, x1_offset + i * stepsize, ymid + value1 * yscale);
- immVertex2f(pos, x1_offset + i * stepsize, ymid + value2 * yscale);
+ was_line_strip = is_line_strip;
+
+ if (is_line_strip) {
+ line_strip_iter->pos[0] = x1_offset + i * frames_per_pixel;
+ line_strip_iter->pos[1] = y_mid + value_min * y_scale;
+ line_strip_iter->clip = clipping;
+ line_strip_iter++;
+ }
+ else {
+ tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel;
+ tri_strip_iter->pos[1] = y_mid + value_min * y_scale;
+ tri_strip_iter->clip = clipping;
+ tri_strip_iter->rms_pos = y_mid + max_ff(-rms, value_min) * y_scale;
+ tri_strip_iter++;
+
+ tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel;
+ tri_strip_iter->pos[1] = y_mid + value_max * y_scale;
+ tri_strip_iter->clip = clipping;
+ tri_strip_iter->rms_pos = y_mid + min_ff(rms, value_max) * y_scale;
+ tri_strip_iter++;
+ }
}
- immEnd();
- immUnbindProgram();
- GPU_blend(GPU_BLEND_NONE);
+ WaveVizData *tri_strip_end = tri_strip_iter;
+ WaveVizData *line_strip_end = line_strip_iter;
+
+ tri_strip_iter = tri_strip_arr;
+ line_strip_iter = line_strip_arr;
+
+ draw_waveform(line_strip_iter, line_strip_end, GPU_PRIM_LINE_STRIP, false);
+ draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, false);
+ draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, true);
+
+ MEM_freeN(tri_strip_arr);
+ MEM_freeN(line_strip_arr);
}
}
@@ -1125,7 +1314,7 @@ static void draw_seq_strip(const bContext *C,
}
else {
text_margin_y = y2;
- y_threshold = 1;
+ y_threshold = false;
}
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);