code cleanup: use `const char` for args and replace wm_keyconfig_list_find() -> BLI_f...
[blender-staging.git] / source / blender / blenkernel / intern / seqmodifier.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2012 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation,
22  *                 Sergey Sharybin
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/blenkernel/intern/seqmodifier.c
28  *  \ingroup bke
29  */
30
31 #include <stddef.h>
32 #include <string.h>
33
34 #include "MEM_guardedalloc.h"
35
36 #include "BLI_listbase.h"
37 #include "BLI_path_util.h"
38 #include "BLI_string.h"
39 #include "BLI_utildefines.h"
40 #include "BLI_math.h"
41
42 #include "BLF_translation.h"
43
44 #include "DNA_sequence_types.h"
45
46 #include "BKE_colortools.h"
47 #include "BKE_sequencer.h"
48
49 #include "IMB_imbuf.h"
50 #include "IMB_imbuf_types.h"
51
52 static SequenceModifierTypeInfo *modifiersTypes[NUM_SEQUENCE_MODIFIER_TYPES];
53 static int modifierTypesInit = FALSE;
54
55 /*********************** Modifiers *************************/
56
57 typedef void (*modifier_apply_threaded_cb) (int width, int height, unsigned char *rect, float *rect_float,
58                                             unsigned char *mask_rect, float *mask_rect_float, void *data_v);
59
60 typedef struct ModifierInitData {
61         ImBuf *ibuf;
62         ImBuf *mask;
63         void *user_data;
64
65         modifier_apply_threaded_cb apply_callback;
66 } ModifierInitData;
67
68 typedef struct ModifierThread {
69         int width, height;
70
71         unsigned char *rect, *mask_rect;
72         float *rect_float, *mask_rect_float;
73
74         void *user_data;
75
76         modifier_apply_threaded_cb apply_callback;
77 } ModifierThread;
78
79
80 static ImBuf *modifier_mask_get(SequenceModifierData *smd, SeqRenderData context, int cfra, int make_float)
81 {
82         return BKE_sequencer_render_mask_input(context, smd->mask_input_type, smd->mask_sequence, smd->mask_id, cfra, make_float);
83 }
84
85 static void modifier_init_handle(void *handle_v, int start_line, int tot_line, void *init_data_v)
86 {
87         ModifierThread *handle = (ModifierThread *) handle_v;
88         ModifierInitData *init_data = (ModifierInitData *) init_data_v;
89         ImBuf *ibuf = init_data->ibuf;
90         ImBuf *mask = init_data->mask;
91
92         int offset = 4 * start_line * ibuf->x;
93
94         memset(handle, 0, sizeof(ModifierThread));
95
96         handle->width = ibuf->x;
97         handle->height = tot_line;
98         handle->apply_callback = init_data->apply_callback;
99         handle->user_data = init_data->user_data;
100
101         if (ibuf->rect)
102                 handle->rect = (unsigned char *) ibuf->rect + offset;
103
104         if (ibuf->rect_float)
105                 handle->rect_float = ibuf->rect_float + offset;
106
107         if (mask) {
108                 if (mask->rect)
109                         handle->mask_rect = (unsigned char *) mask->rect + offset;
110
111                 if (mask->rect_float)
112                         handle->mask_rect_float = mask->rect_float + offset;
113         }
114         else {
115                 handle->mask_rect = NULL;
116                 handle->mask_rect_float = NULL;
117         }
118 }
119
120 static void *modifier_do_thread(void *thread_data_v)
121 {
122         ModifierThread *td = (ModifierThread *) thread_data_v;
123
124         td->apply_callback(td->width, td->height, td->rect, td->rect_float, td->mask_rect, td->mask_rect_float, td->user_data);
125
126         return NULL;
127 }
128
129 static void modifier_apply_threaded(ImBuf *ibuf, ImBuf *mask, modifier_apply_threaded_cb apply_callback, void *user_data)
130 {
131         ModifierInitData init_data;
132
133         init_data.ibuf = ibuf;
134         init_data.mask = mask;
135         init_data.user_data = user_data;
136
137         init_data.apply_callback = apply_callback;
138
139         IMB_processor_apply_threaded(ibuf->y, sizeof(ModifierThread), &init_data,
140                                      modifier_init_handle, modifier_do_thread);
141 }
142
143 /* **** Color Balance Modifier **** */
144
145 static void colorBalance_init_data(SequenceModifierData *smd)
146 {
147         ColorBalanceModifierData *cbmd = (ColorBalanceModifierData *) smd;
148         int c;
149
150         cbmd->color_multiply = 1.0f;
151
152         for (c = 0; c < 3; c++) {
153                 cbmd->color_balance.lift[c] = 1.0f;
154                 cbmd->color_balance.gamma[c] = 1.0f;
155                 cbmd->color_balance.gain[c] = 1.0f;
156         }
157 }
158
159 static void colorBalance_apply(SequenceModifierData *smd, ImBuf *ibuf, ImBuf *mask)
160 {
161         ColorBalanceModifierData *cbmd = (ColorBalanceModifierData *) smd;
162
163         BKE_sequencer_color_balance_apply(&cbmd->color_balance, ibuf, cbmd->color_multiply, FALSE, mask);
164 }
165
166 static SequenceModifierTypeInfo seqModifier_ColorBalance = {
167         CTX_N_(BLF_I18NCONTEXT_ID_SEQUENCE, "Color Balance"),  /* name */
168         "ColorBalanceModifierData",                            /* struct_name */
169         sizeof(ColorBalanceModifierData),                      /* struct_size */
170         colorBalance_init_data,                                /* init_data */
171         NULL,                                                  /* free_data */
172         NULL,                                                  /* copy_data */
173         colorBalance_apply                                     /* apply */
174 };
175
176 /* **** Curves Modifier **** */
177
178 static void curves_init_data(SequenceModifierData *smd)
179 {
180         CurvesModifierData *cmd = (CurvesModifierData *) smd;
181
182         curvemapping_set_defaults(&cmd->curve_mapping, 4, 0.0f, 0.0f, 1.0f, 1.0f);
183 }
184
185 static void curves_free_data(SequenceModifierData *smd)
186 {
187         CurvesModifierData *cmd = (CurvesModifierData *) smd;
188
189         curvemapping_free_data(&cmd->curve_mapping);
190 }
191
192 static void curves_copy_data(SequenceModifierData *target, SequenceModifierData *smd)
193 {
194         CurvesModifierData *cmd = (CurvesModifierData *) smd;
195         CurvesModifierData *cmd_target = (CurvesModifierData *) target;
196
197         curvemapping_copy_data(&cmd_target->curve_mapping, &cmd->curve_mapping);
198 }
199
200 static void curves_apply_threaded(int width, int height, unsigned char *rect, float *rect_float,
201                                   unsigned char *mask_rect, float *mask_rect_float, void *data_v)
202 {
203         CurveMapping *curve_mapping = (CurveMapping *) data_v;
204         int x, y;
205
206         for (y = 0; y < height; y++) {
207                 for (x = 0; x < width; x++) {
208                         int pixel_index = (y * width + x) * 4;
209
210                         if (rect_float) {
211                                 float *pixel = rect_float + pixel_index;
212                                 float result[3];
213
214                                 curvemapping_evaluate_premulRGBF(curve_mapping, result, pixel);
215
216                                 if (mask_rect_float) {
217                                         float *m = mask_rect_float + pixel_index;
218
219                                         pixel[0] = pixel[0] * (1.0f - m[0]) + result[0] * m[0];
220                                         pixel[1] = pixel[1] * (1.0f - m[1]) + result[1] * m[1];
221                                         pixel[2] = pixel[2] * (1.0f - m[2]) + result[2] * m[2];
222                                 }
223                                 else {
224                                         pixel[0] = result[0];
225                                         pixel[1] = result[1];
226                                         pixel[2] = result[2];
227                                 }
228                         }
229                         if (rect) {
230                                 unsigned char *pixel = rect + pixel_index;
231                                 float result[3], tempc[4];
232
233                                 straight_uchar_to_premul_float(tempc, pixel);
234
235                                 curvemapping_evaluate_premulRGBF(curve_mapping, result, tempc);
236
237                                 if (mask_rect) {
238                                         float t[3];
239
240                                         rgb_uchar_to_float(t, mask_rect + pixel_index);
241
242                                         tempc[0] = tempc[0] * (1.0f - t[0]) + result[0] * t[0];
243                                         tempc[1] = tempc[1] * (1.0f - t[1]) + result[1] * t[1];
244                                         tempc[2] = tempc[2] * (1.0f - t[2]) + result[2] * t[2];
245                                 }
246                                 else {
247                                         tempc[0] = result[0];
248                                         tempc[1] = result[1];
249                                         tempc[2] = result[2];
250                                 }
251
252                                 premul_float_to_straight_uchar(pixel, tempc);
253                         }
254                 }
255         }
256 }
257
258 static void curves_apply(struct SequenceModifierData *smd, ImBuf *ibuf, ImBuf *mask)
259 {
260         CurvesModifierData *cmd = (CurvesModifierData *) smd;
261
262         float black[3] = {0.0f, 0.0f, 0.0f};
263         float white[3] = {1.0f, 1.0f, 1.0f};
264
265         curvemapping_initialize(&cmd->curve_mapping);
266
267         curvemapping_premultiply(&cmd->curve_mapping, 0);
268         curvemapping_set_black_white(&cmd->curve_mapping, black, white);
269
270         modifier_apply_threaded(ibuf, mask, curves_apply_threaded, &cmd->curve_mapping);
271
272         curvemapping_premultiply(&cmd->curve_mapping, 1);
273 }
274
275 static SequenceModifierTypeInfo seqModifier_Curves = {
276         CTX_N_(BLF_I18NCONTEXT_ID_SEQUENCE, "Curves"),   /* name */
277         "CurvesModifierData",                            /* struct_name */
278         sizeof(CurvesModifierData),                      /* struct_size */
279         curves_init_data,                                /* init_data */
280         curves_free_data,                                /* free_data */
281         curves_copy_data,                                /* copy_data */
282         curves_apply                                     /* apply */
283 };
284
285 /* **** Hue Correct Modifier **** */
286
287 static void hue_correct_init_data(SequenceModifierData *smd)
288 {
289         HueCorrectModifierData *hcmd = (HueCorrectModifierData *) smd;
290         int c;
291
292         curvemapping_set_defaults(&hcmd->curve_mapping, 1, 0.0f, 0.0f, 1.0f, 1.0f);
293         hcmd->curve_mapping.preset = CURVE_PRESET_MID9;
294
295         for (c = 0; c < 3; c++) {
296                 CurveMap *cuma = &hcmd->curve_mapping.cm[c];
297
298                 curvemap_reset(cuma, &hcmd->curve_mapping.clipr, hcmd->curve_mapping.preset, CURVEMAP_SLOPE_POSITIVE);
299         }
300
301         /* default to showing Saturation */
302         hcmd->curve_mapping.cur = 1;
303 }
304
305 static void hue_correct_free_data(SequenceModifierData *smd)
306 {
307         HueCorrectModifierData *hcmd = (HueCorrectModifierData *) smd;
308
309         curvemapping_free_data(&hcmd->curve_mapping);
310 }
311
312 static void hue_correct_copy_data(SequenceModifierData *target, SequenceModifierData *smd)
313 {
314         HueCorrectModifierData *hcmd = (HueCorrectModifierData *) smd;
315         HueCorrectModifierData *hcmd_target = (HueCorrectModifierData *) target;
316
317         curvemapping_copy_data(&hcmd_target->curve_mapping, &hcmd->curve_mapping);
318 }
319
320 static void hue_correct_apply_threaded(int width, int height, unsigned char *rect, float *rect_float,
321                                 unsigned char *mask_rect, float *mask_rect_float, void *data_v)
322 {
323         CurveMapping *curve_mapping = (CurveMapping *) data_v;
324         int x, y;
325
326         for (y = 0; y < height; y++) {
327                 for (x = 0; x < width; x++) {
328                         int pixel_index = (y * width + x) * 4;
329                         float pixel[3], result[3], mask[3] = {1.0f, 1.0f, 1.0f};
330                         float hsv[3], f;
331
332                         if (rect_float)
333                                 copy_v3_v3(pixel, rect_float + pixel_index);
334                         else
335                                 rgb_uchar_to_float(pixel, rect + pixel_index);
336
337                         rgb_to_hsv(pixel[0], pixel[1], pixel[2], hsv, hsv + 1, hsv + 2);
338
339                         /* adjust hue, scaling returned default 0.5 up to 1 */
340                         f = curvemapping_evaluateF(curve_mapping, 0, hsv[0]);
341                         hsv[0] += f - 0.5f;
342
343                         /* adjust saturation, scaling returned default 0.5 up to 1 */
344                         f = curvemapping_evaluateF(curve_mapping, 1, hsv[0]);
345                         hsv[1] *= (f * 2.0f);
346
347                         /* adjust value, scaling returned default 0.5 up to 1 */
348                         f = curvemapping_evaluateF(curve_mapping, 2, hsv[0]);
349                         hsv[2] *= (f * 2.f);
350
351                         hsv[0] = hsv[0] - floorf(hsv[0]); /* mod 1.0 */
352                         CLAMP(hsv[1], 0.0f, 1.0f);
353
354                         /* convert back to rgb */
355                         hsv_to_rgb(hsv[0], hsv[1], hsv[2], result, result + 1, result + 2);
356
357                         if (mask_rect_float)
358                                 copy_v3_v3(mask, mask_rect_float + pixel_index);
359                         else if (mask_rect)
360                                 rgb_uchar_to_float(mask, mask_rect + pixel_index);
361
362                         result[0] = pixel[0] * (1.0f - mask[0]) + result[0] * mask[0];
363                         result[1] = pixel[1] * (1.0f - mask[1]) + result[1] * mask[1];
364                         result[2] = pixel[2] * (1.0f - mask[2]) + result[2] * mask[2];
365
366                         if (rect_float)
367                                 copy_v3_v3(rect_float + pixel_index, result);
368                         else
369                                 rgb_float_to_uchar(rect + pixel_index, result);
370                 }
371         }
372 }
373
374 static void hue_correct_apply(struct SequenceModifierData *smd, ImBuf *ibuf, ImBuf *mask)
375 {
376         HueCorrectModifierData *hcmd = (HueCorrectModifierData *) smd;
377
378         curvemapping_initialize(&hcmd->curve_mapping);
379
380         modifier_apply_threaded(ibuf, mask, hue_correct_apply_threaded, &hcmd->curve_mapping);
381 }
382
383 static SequenceModifierTypeInfo seqModifier_HueCorrect = {
384         CTX_N_(BLF_I18NCONTEXT_ID_SEQUENCE, "Hue Correct"),    /* name */
385         "HueCorrectModifierData",                              /* struct_name */
386         sizeof(HueCorrectModifierData),                        /* struct_size */
387         hue_correct_init_data,                                 /* init_data */
388         hue_correct_free_data,                                 /* free_data */
389         hue_correct_copy_data,                                 /* copy_data */
390         hue_correct_apply                                      /* apply */
391 };
392
393 /* **** Bright/Contrast Modifier **** */
394
395 typedef struct BrightContrastThreadData {
396         float bright;
397         float contrast;
398 } BrightContrastThreadData;
399
400 static void brightcontrast_apply_threaded(int width, int height, unsigned char *rect, float *rect_float,
401                                           unsigned char *mask_rect, float *mask_rect_float, void *data_v)
402 {
403         BrightContrastThreadData *data = (BrightContrastThreadData *) data_v;
404         int x, y;
405
406         float i;
407         int c;
408         float a, b, v;
409         float brightness = data->bright / 100.0f;
410         float contrast = data->contrast;
411         float delta = contrast / 200.0f;
412
413         a = 1.0f - delta * 2.0f;
414         /*
415          * The algorithm is by Werner D. Streidt
416          * (http://visca.com/ffactory/archives/5-99/msg00021.html)
417          * Extracted of OpenCV demhist.c
418          */
419         if (contrast > 0) {
420                 a = 1.0f / a;
421                 b = a * (brightness - delta);
422         }
423         else {
424                 delta *= -1;
425                 b = a * (brightness + delta);
426         }
427
428         for (y = 0; y < height; y++) {
429                 for (x = 0; x < width; x++) {
430                         int pixel_index = (y * width + x) * 4;
431
432                         if (rect) {
433                                 unsigned char *pixel = rect + pixel_index;
434
435                                 for (c = 0; c < 3; c++) {
436                                         i = (float) pixel[c] / 255.0f;
437                                         v = a * i + b;
438
439                                         if (mask_rect) {
440                                                 unsigned char *m = mask_rect + pixel_index;
441                                                 float t = (float) m[c] / 255.0f;
442
443                                                 v = (float) pixel[c] / 255.0f * (1.0f - t) + v * t;
444                                         }
445
446                                         pixel[c] = FTOCHAR(v);
447                                 }
448                         }
449                         else if (rect_float) {
450                                 float *pixel = rect_float + pixel_index;
451
452                                 for (c = 0; c < 3; c++) {
453                                         i = pixel[c];
454                                         v = a * i + b;
455
456                                         if (mask_rect_float) {
457                                                 float *m = mask_rect_float + pixel_index;
458
459                                                 pixel[c] = pixel[c] * (1.0f - m[c]) + v * m[c];
460                                         }
461                                         else
462                                                 pixel[c] = v;
463                                 }
464                         }
465                 }
466         }
467 }
468
469 static void brightcontrast_apply(struct SequenceModifierData *smd, ImBuf *ibuf, ImBuf *mask)
470 {
471         BrightContrastModifierData *bcmd = (BrightContrastModifierData *) smd;
472         BrightContrastThreadData data;
473
474         data.bright = bcmd->bright;
475         data.contrast = bcmd->contrast;
476
477         modifier_apply_threaded(ibuf, mask, brightcontrast_apply_threaded, &data);
478 }
479
480 static SequenceModifierTypeInfo seqModifier_BrightContrast = {
481         CTX_N_(BLF_I18NCONTEXT_ID_SEQUENCE, "Bright/Contrast"),   /* name */
482         "BrightContrastModifierData",                             /* struct_name */
483         sizeof(BrightContrastModifierData),                       /* struct_size */
484         NULL,                                                     /* init_data */
485         NULL,                                                     /* free_data */
486         NULL,                                                     /* copy_data */
487         brightcontrast_apply                                      /* apply */
488 };
489
490 /*********************** Modifier functions *************************/
491
492 static void sequence_modifier_type_info_init(void)
493 {
494 #define INIT_TYPE(typeName) (modifiersTypes[seqModifierType_##typeName] = &seqModifier_##typeName)
495
496         INIT_TYPE(ColorBalance);
497         INIT_TYPE(Curves);
498         INIT_TYPE(HueCorrect);
499         INIT_TYPE(BrightContrast);
500
501 #undef INIT_TYPE
502 }
503
504 SequenceModifierTypeInfo *BKE_sequence_modifier_type_info_get(int type)
505 {
506         if (!modifierTypesInit) {
507                 sequence_modifier_type_info_init();
508                 modifierTypesInit = TRUE;
509         }
510
511         return modifiersTypes[type];
512 }
513
514 SequenceModifierData *BKE_sequence_modifier_new(Sequence *seq, const char *name, int type)
515 {
516         SequenceModifierData *smd;
517         SequenceModifierTypeInfo *smti = BKE_sequence_modifier_type_info_get(type);
518
519         smd = MEM_callocN(smti->struct_size, "sequence modifier");
520
521         smd->type = type;
522         smd->flag |= SEQUENCE_MODIFIER_EXPANDED;
523
524         if (!name || !name[0])
525                 BLI_strncpy(smd->name, smti->name, sizeof(smd->name));
526         else
527                 BLI_strncpy(smd->name, name, sizeof(smd->name));
528
529         BLI_addtail(&seq->modifiers, smd);
530
531         BKE_sequence_modifier_unique_name(seq, smd);
532
533         if (smti->init_data)
534                 smti->init_data(smd);
535
536         return smd;
537 }
538
539 int BKE_sequence_modifier_remove(Sequence *seq, SequenceModifierData *smd)
540 {
541         if (BLI_findindex(&seq->modifiers, smd) == -1)
542                 return FALSE;
543
544         BLI_remlink(&seq->modifiers, smd);
545         BKE_sequence_modifier_free(smd);
546
547         return TRUE;
548 }
549
550 void BKE_sequence_modifier_clear(Sequence *seq)
551 {
552         SequenceModifierData *smd, *smd_next;
553
554         for (smd = seq->modifiers.first; smd; smd = smd_next) {
555                 smd_next = smd->next;
556                 BKE_sequence_modifier_free(smd);
557         }
558
559         seq->modifiers.first = seq->modifiers.last = NULL;
560 }
561
562 void BKE_sequence_modifier_free(SequenceModifierData *smd)
563 {
564         SequenceModifierTypeInfo *smti = BKE_sequence_modifier_type_info_get(smd->type);
565
566         if (smti && smti->free_data) {
567                 smti->free_data(smd);
568         }
569
570         MEM_freeN(smd);
571 }
572
573 void BKE_sequence_modifier_unique_name(Sequence *seq, SequenceModifierData *smd)
574 {
575         SequenceModifierTypeInfo *smti = BKE_sequence_modifier_type_info_get(smd->type);
576
577         BLI_uniquename(&seq->modifiers, smd, CTX_DATA_(BLF_I18NCONTEXT_ID_SEQUENCE, smti->name), '.',
578                        offsetof(SequenceModifierData, name), sizeof(smd->name));
579 }
580
581 SequenceModifierData *BKE_sequence_modifier_find_by_name(Sequence *seq, const char *name)
582 {
583         return BLI_findstring(&(seq->modifiers), name, offsetof(SequenceModifierData, name));
584 }
585
586 ImBuf *BKE_sequence_modifier_apply_stack(SeqRenderData context, Sequence *seq, ImBuf *ibuf, int cfra)
587 {
588         SequenceModifierData *smd;
589         ImBuf *processed_ibuf = ibuf;
590
591         if (seq->modifiers.first && (seq->flag & SEQ_USE_LINEAR_MODIFIERS)) {
592                 processed_ibuf = IMB_dupImBuf(ibuf);
593                 BKE_sequencer_imbuf_from_sequencer_space(context.scene, processed_ibuf);
594         }
595
596         for (smd = seq->modifiers.first; smd; smd = smd->next) {
597                 SequenceModifierTypeInfo *smti = BKE_sequence_modifier_type_info_get(smd->type);
598
599                 /* could happen if modifier is being removed or not exists in current version of blender */
600                 if (!smti)
601                         continue;
602
603                 /* modifier is muted, do nothing */
604                 if (smd->flag & SEQUENCE_MODIFIER_MUTE)
605                         continue;
606
607                 if (smti->apply) {
608                         ImBuf *mask = modifier_mask_get(smd, context, cfra, ibuf->rect_float != NULL);
609
610                         if (processed_ibuf == ibuf)
611                                 processed_ibuf = IMB_dupImBuf(ibuf);
612
613                         smti->apply(smd, processed_ibuf, mask);
614
615                         if (mask)
616                                 IMB_freeImBuf(mask);
617                 }
618         }
619
620         if (seq->modifiers.first && (seq->flag & SEQ_USE_LINEAR_MODIFIERS)) {
621                 BKE_sequencer_imbuf_to_sequencer_space(context.scene, processed_ibuf, FALSE);
622         }
623
624         return processed_ibuf;
625 }
626
627 void BKE_sequence_modifier_list_copy(Sequence *seqn, Sequence *seq)
628 {
629         SequenceModifierData *smd;
630
631         for (smd = seq->modifiers.first; smd; smd = smd->next) {
632                 SequenceModifierData *smdn;
633                 SequenceModifierTypeInfo *smti = BKE_sequence_modifier_type_info_get(smd->type);
634
635                 smdn = MEM_dupallocN(smd);
636
637                 if (smti && smti->copy_data)
638                         smti->copy_data(smdn, smd);
639
640                 smdn->next = smdn->prev = NULL;
641                 BLI_addtail(&seqn->modifiers, smdn);
642         }
643 }
644
645 int BKE_sequence_supports_modifiers(Sequence *seq)
646 {
647         return !ELEM(seq->type, SEQ_TYPE_SOUND_RAM, SEQ_TYPE_SOUND_HD);
648 }