From 1015bed2fd89c5a8f741c5f1dd22099a9441bea0 Mon Sep 17 00:00:00 2001 From: Marcos Perez Date: Wed, 30 Aug 2023 22:36:36 +0200 Subject: [PATCH] VSE: Sound equalizer modifier The sound equalizer is using the Audaspace FFT Convolver. The blender part creates an array of descriptions of power per "band" and orders the creation of Equalizer (ISound) in the Audaspace. Modifier can be created on sound strips. It lets you define amplification or attenuation over frequency range from 30Hz to 20 kHz. The power is limited to -30 db - 30 db. This is done using curve mapping widget. Co-authored-by: menda Co-authored-by: Richard Antalik Pull Request: https://projects.blender.org/blender/blender/pulls/105613 --- build_files/cmake/Modules/FindFftw3.cmake | 22 +- extern/audaspace/blender_config.cmake | 7 +- scripts/startup/bl_ui/space_sequencer.py | 118 ++++++---- source/blender/blenkernel/BKE_colortools.h | 9 +- source/blender/blenkernel/BKE_sound.h | 7 + source/blender/blenkernel/intern/brush.cc | 10 +- .../blender/blenkernel/intern/colortools.cc | 34 ++- source/blender/blenkernel/intern/scene.cc | 2 +- source/blender/blenkernel/intern/sound.cc | 5 + .../blenloader/intern/versioning_270.cc | 2 +- .../space_sequencer/sequencer_intern.h | 1 + .../space_sequencer/sequencer_modifier.cc | 148 ++++++++++--- .../editors/space_sequencer/sequencer_ops.cc | 1 + source/blender/makesdna/DNA_color_types.h | 3 + source/blender/makesdna/DNA_sequence_types.h | 16 ++ source/blender/makesrna/RNA_enum_items.hh | 2 + .../blender/makesrna/intern/rna_sequencer.cc | 146 ++++++++++++- source/blender/sequencer/CMakeLists.txt | 4 + source/blender/sequencer/SEQ_sound.h | 39 ++++ source/blender/sequencer/intern/modifier.cc | 29 ++- source/blender/sequencer/intern/sequencer.cc | 9 + source/blender/sequencer/intern/sound.cc | 201 ++++++++++++++++++ 22 files changed, 722 insertions(+), 93 deletions(-) diff --git a/build_files/cmake/Modules/FindFftw3.cmake b/build_files/cmake/Modules/FindFftw3.cmake index 2f282a68884..4a350d60269 100644 --- a/build_files/cmake/Modules/FindFftw3.cmake +++ b/build_files/cmake/Modules/FindFftw3.cmake @@ -37,7 +37,18 @@ find_path(FFTW3_INCLUDE_DIR include ) -find_library(FFTW3_LIBRARY +set(_FFTW3_LIBRARIES) + +find_library(FFTW3_LIBRARY_F + NAMES + fftw3f + HINTS + ${_fftw3_SEARCH_DIRS} + PATH_SUFFIXES + lib64 lib + ) + +find_library(FFTW3_LIBRARY_D NAMES fftw3 HINTS @@ -46,17 +57,22 @@ find_library(FFTW3_LIBRARY lib64 lib ) +list(APPEND _FFTW3_LIBRARIES "${FFTW3_LIBRARY_F}") +list(APPEND _FFTW3_LIBRARIES "${FFTW3_LIBRARY_D}") + # handle the QUIETLY and REQUIRED arguments and set FFTW3_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Fftw3 DEFAULT_MSG - FFTW3_LIBRARY FFTW3_INCLUDE_DIR) + _FFTW3_LIBRARIES FFTW3_INCLUDE_DIR) if(FFTW3_FOUND) - set(FFTW3_LIBRARIES ${FFTW3_LIBRARY}) + set(FFTW3_LIBRARIES ${_FFTW3_LIBRARIES}) set(FFTW3_INCLUDE_DIRS ${FFTW3_INCLUDE_DIR}) endif() +unset(_FFTW3_LIBRARIES) + mark_as_advanced( FFTW3_INCLUDE_DIR FFTW3_LIBRARY diff --git a/extern/audaspace/blender_config.cmake b/extern/audaspace/blender_config.cmake index 14d7d593838..476d82f1c81 100644 --- a/extern/audaspace/blender_config.cmake +++ b/extern/audaspace/blender_config.cmake @@ -7,7 +7,12 @@ set(SHARED_LIBRARY FALSE) # "Build Shared Library" set(WITH_C TRUE) # "Build C Module" set(WITH_DOCS FALSE) # "Build C++ HTML Documentation with Doxygen" set(WITH_FFMPEG ${WITH_CODEC_FFMPEG}) # "Build With FFMPEG" -set(WITH_FFTW FALSE) # "Build With FFTW" +if(DEFINED WITH_FFTW3) # "Build With FFTW" + set(FFTW_FOUND TRUE) + set(WITH_FFTW ${WITH_FFTW3}) + set(FFTW_INCLUDE_DIR ${FFTW3_INCLUDE_DIRS}) + set(FFTW_LIBRARY ${FFTW3_LIBRARIES}) +endif() set(WITH_LIBSNDFILE ${WITH_CODEC_SNDFILE}) # "Build With LibSndFile" set(SEPARATE_C FALSE) # "Build C Binding as separate library" set(PLUGIN_COREAUDIO FALSE) # "Build CoreAudio Plugin" diff --git a/scripts/startup/bl_ui/space_sequencer.py b/scripts/startup/bl_ui/space_sequencer.py index 680a831e054..e3e46ed93a8 100644 --- a/scripts/startup/bl_ui/space_sequencer.py +++ b/scripts/startup/bl_ui/space_sequencer.py @@ -962,7 +962,11 @@ class SEQUENCER_MT_strip(Menu): if strip_type != 'SOUND': layout.separator() - layout.operator_menu_enum("sequencer.strip_modifier_add", "type", text="Add Modifier") + layout.operator_menu_enum("sequencer.strip_video_modifier_add", "type", text="Add Modifier") + layout.operator("sequencer.strip_modifier_copy", text="Copy Modifiers to Selection") + else: + layout.separator() + layout.operator_menu_enum("sequencer.strip_sound_modifier_add", "type", text="Add Modifier") layout.operator("sequencer.strip_modifier_copy", text="Copy Modifiers to Selection") if strip_type in { @@ -1103,17 +1107,19 @@ class SEQUENCER_MT_context_menu(Menu): if strip_type != 'SOUND': layout.separator() - layout.operator_menu_enum("sequencer.strip_modifier_add", "type", text="Add Modifier") + layout.operator_menu_enum("sequencer.strip_video_modifier_add", "type", text="Add Modifier") layout.operator("sequencer.strip_modifier_copy", text="Copy Modifiers to Selection") - if selected_sequences_count >= 2: layout.separator() col = layout.column() col.menu("SEQUENCER_MT_add_transitions", text="Add Transition") - - elif selected_sequences_count >= 2: + else: layout.separator() - layout.operator("sequencer.crossfade_sounds", text="Crossfade Sounds") + layout.operator_menu_enum("sequencer.strip_sound_modifier_add", "type", text="Add Modifier") + layout.operator("sequencer.strip_modifier_copy", text="Copy Modifiers to Selection") + if selected_sequences_count >= 2: + layout.separator() + layout.operator("sequencer.crossfade_sounds", text="Crossfade Sounds") if selected_sequences_count >= 1: col = layout.column() @@ -2503,8 +2509,13 @@ class SEQUENCER_PT_modifiers(SequencerButtonsPanel, Panel): strip = context.active_sequence_strip ed = context.scene.sequence_editor + if strip.type == 'SOUND': + sound = strip.sound + else: + sound = None - layout.prop(strip, "use_linear_modifiers") + if sound is None: + layout.prop(strip, "use_linear_modifiers") layout.operator_menu_enum("sequencer.strip_modifier_add", "type") layout.operator("sequencer.strip_modifier_copy") @@ -2531,45 +2542,66 @@ class SEQUENCER_PT_modifiers(SequencerButtonsPanel, Panel): row.operator("sequencer.strip_modifier_remove", text="", icon='X', emboss=False).name = mod.name if mod.show_expanded: - row = box.row() - row.prop(mod, "input_mask_type", expand=True) - - if mod.input_mask_type == 'STRIP': - sequences_object = ed - if ed.meta_stack: - sequences_object = ed.meta_stack[-1] - box.prop_search(mod, "input_mask_strip", sequences_object, "sequences", text="Mask") - else: - box.prop(mod, "input_mask_id") + if sound is None: row = box.row() - row.prop(mod, "mask_time", expand=True) + row.prop(mod, "input_mask_type", expand=True) - if mod.type == 'COLOR_BALANCE': - box.prop(mod, "color_multiply") - draw_color_balance(box, mod.color_balance) - elif mod.type == 'CURVES': - box.template_curve_mapping(mod, "curve_mapping", type='COLOR', show_tone=True) - elif mod.type == 'HUE_CORRECT': - box.template_curve_mapping(mod, "curve_mapping", type='HUE') - elif mod.type == 'BRIGHT_CONTRAST': - col = box.column() - col.prop(mod, "bright") - col.prop(mod, "contrast") - elif mod.type == 'WHITE_BALANCE': - col = box.column() - col.prop(mod, "white_value") - elif mod.type == 'TONEMAP': - col = box.column() - col.prop(mod, "tonemap_type") - if mod.tonemap_type == 'RD_PHOTORECEPTOR': - col.prop(mod, "intensity") + if mod.input_mask_type == 'STRIP': + sequences_object = ed + if ed.meta_stack: + sequences_object = ed.meta_stack[-1] + box.prop_search(mod, "input_mask_strip", sequences_object, "sequences", text="Mask") + else: + box.prop(mod, "input_mask_id") + row = box.row() + row.prop(mod, "mask_time", expand=True) + + if mod.type == 'COLOR_BALANCE': + box.prop(mod, "color_multiply") + draw_color_balance(box, mod.color_balance) + elif mod.type == 'CURVES': + box.template_curve_mapping(mod, "curve_mapping", type='COLOR', show_tone=True) + elif mod.type == 'HUE_CORRECT': + box.template_curve_mapping(mod, "curve_mapping", type='HUE') + elif mod.type == 'BRIGHT_CONTRAST': + col = box.column() + col.prop(mod, "bright") col.prop(mod, "contrast") - col.prop(mod, "adaptation") - col.prop(mod, "correction") - elif mod.tonemap_type == 'RH_SIMPLE': - col.prop(mod, "key") - col.prop(mod, "offset") - col.prop(mod, "gamma") + elif mod.type == 'WHITE_BALANCE': + col = box.column() + col.prop(mod, "white_value") + elif mod.type == 'TONEMAP': + col = box.column() + col.prop(mod, "tonemap_type") + if mod.tonemap_type == 'RD_PHOTORECEPTOR': + col.prop(mod, "intensity") + col.prop(mod, "contrast") + col.prop(mod, "adaptation") + col.prop(mod, "correction") + elif mod.tonemap_type == 'RH_SIMPLE': + col.prop(mod, "key") + col.prop(mod, "offset") + col.prop(mod, "gamma") + else: + if mod.type == 'SOUND_EQUALIZER': + eq_row = box.row() + # eq_graphs = eq_row.operator_menu_enum("sequencer.strip_modifier_equalizer_redefine", "graphs") + # eq_graphs.name = mod.name + flow = box.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) + for i in range(len(mod.graphics)): + soundEq = mod.graphics[i] + col = flow.column() + box = col.box() + split = box.split(factor=0.4) + split.label(text = "{:.2f}".format(soundEq.curve_mapping.clip_min_x)) + split.label(text = "Hz") + split.alignment = "RIGHT" + split.label(text = "{:.2f}".format(soundEq.curve_mapping.clip_max_x)) + box.template_curve_mapping(soundEq, "curve_mapping", + type='NONE', levels=False, brush=True, use_negative_slope=True, show_tone=False) + second_row = col.row() + second_row.label(text = "dB") + second_row.alignment = "CENTER" class SEQUENCER_PT_annotation(AnnotationDataPanel, SequencerButtonsPanel_Output, Panel): diff --git a/source/blender/blenkernel/BKE_colortools.h b/source/blender/blenkernel/BKE_colortools.h index 4dc304e6f69..5f731ea6cc6 100644 --- a/source/blender/blenkernel/BKE_colortools.h +++ b/source/blender/blenkernel/BKE_colortools.h @@ -24,8 +24,13 @@ struct ImBuf; struct Scopes; struct rctf; -void BKE_curvemapping_set_defaults( - struct CurveMapping *cumap, int tot, float minx, float miny, float maxx, float maxy); +void BKE_curvemapping_set_defaults(struct CurveMapping *cumap, + int tot, + float minx, + float miny, + float maxx, + float maxy, + short default_handle_type); struct CurveMapping *BKE_curvemapping_add(int tot, float minx, float miny, float maxx, float maxy); void BKE_curvemapping_free_data(struct CurveMapping *cumap); void BKE_curvemapping_free(struct CurveMapping *cumap); diff --git a/source/blender/blenkernel/BKE_sound.h b/source/blender/blenkernel/BKE_sound.h index 71f1262dcea..28355fd19e8 100644 --- a/source/blender/blenkernel/BKE_sound.h +++ b/source/blender/blenkernel/BKE_sound.h @@ -127,6 +127,7 @@ void BKE_sound_update_scene_listener(struct Scene *scene); void *BKE_sound_scene_add_scene_sound( struct Scene *scene, struct Sequence *sequence, int startframe, int endframe, int frameskip); + void *BKE_sound_scene_add_scene_sound_defaults(struct Scene *scene, struct Sequence *sequence); void *BKE_sound_add_scene_sound( @@ -145,8 +146,14 @@ void BKE_sound_move_scene_sound(const struct Scene *scene, double audio_offset); void BKE_sound_move_scene_sound_defaults(struct Scene *scene, struct Sequence *sequence); +/* Join the Sequence with the structure in Audaspace, the second parameter is a bSound */ void BKE_sound_update_scene_sound(void *handle, struct bSound *sound); +/* Join the Sequence with the structure in Audaspace, the second parameter is the AUD_Sound created + * in Audaspace previously + */ +void BKE_sound_update_sequence_handle(void *handle, void *sound_handle); + void BKE_sound_set_cfra(int cfra); void BKE_sound_set_scene_volume(struct Scene *scene, float volume); diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 478fc4fb2d1..b0209261907 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -776,7 +776,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) /* Curve. */ custom_curve = brush->gpencil_settings->curve_sensitivity; - BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); + BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f, HD_AUTO); BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_INK); @@ -813,7 +813,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) /* Curve. */ custom_curve = brush->gpencil_settings->curve_sensitivity; - BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); + BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f, HD_AUTO); BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_INKNOISE); @@ -850,7 +850,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) /* Curve. */ custom_curve = brush->gpencil_settings->curve_sensitivity; - BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); + BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f, HD_AUTO); BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 4, GPCURVE_PRESET_MARKER); @@ -886,12 +886,12 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) /* Curve. */ custom_curve = brush->gpencil_settings->curve_sensitivity; - BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); + BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f, HD_AUTO); BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_CHISEL_SENSIVITY); custom_curve = brush->gpencil_settings->curve_strength; - BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); + BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f, HD_AUTO); BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 4, GPCURVE_PRESET_CHISEL_STRENGTH); diff --git a/source/blender/blenkernel/intern/colortools.cc b/source/blender/blenkernel/intern/colortools.cc index 01ad4778777..f49efe99495 100644 --- a/source/blender/blenkernel/intern/colortools.cc +++ b/source/blender/blenkernel/intern/colortools.cc @@ -34,8 +34,13 @@ /* ***************** operations on full struct ************* */ -void BKE_curvemapping_set_defaults( - CurveMapping *cumap, int tot, float minx, float miny, float maxx, float maxy) +void BKE_curvemapping_set_defaults(CurveMapping *cumap, + int tot, + float minx, + float miny, + float maxx, + float maxy, + short default_handle_type) { int a; float clipminx, clipminy, clipmaxx, clipmaxy; @@ -57,14 +62,23 @@ void BKE_curvemapping_set_defaults( cumap->bwmul[0] = cumap->bwmul[1] = cumap->bwmul[2] = 1.0f; for (a = 0; a < tot; a++) { + if (default_handle_type == HD_VECT) { + cumap->cm[a].default_handle_type = CUMA_HANDLE_VECTOR; + } + else if (default_handle_type == HD_AUTO_ANIM) { + cumap->cm[a].default_handle_type = CUMA_HANDLE_AUTO_ANIM; + } + cumap->cm[a].totpoint = 2; cumap->cm[a].curve = static_cast( MEM_callocN(2 * sizeof(CurveMapPoint), "curve points")); cumap->cm[a].curve[0].x = minx; cumap->cm[a].curve[0].y = miny; + cumap->cm[a].curve[0].flag |= default_handle_type; cumap->cm[a].curve[1].x = maxx; cumap->cm[a].curve[1].y = maxy; + cumap->cm[a].curve[1].flag |= default_handle_type; } cumap->changed_timestamp = 0; @@ -76,7 +90,7 @@ CurveMapping *BKE_curvemapping_add(int tot, float minx, float miny, float maxx, cumap = static_cast(MEM_callocN(sizeof(CurveMapping), "new curvemap")); - BKE_curvemapping_set_defaults(cumap, tot, minx, miny, maxx, maxy); + BKE_curvemapping_set_defaults(cumap, tot, minx, miny, maxx, maxy, HD_AUTO); return cumap; } @@ -238,6 +252,7 @@ CurveMapPoint *BKE_curvemap_insert(CurveMap *cuma, float x, float y) cmp[a].x = x; cmp[a].y = y; cmp[a].flag = CUMA_SELECT; + cmp[a].flag |= cuma->default_handle_type; foundloc = true; newcmp = &cmp[a]; } @@ -266,6 +281,7 @@ void BKE_curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope switch (preset) { case CURVE_PRESET_LINE: + case CURVE_PRESET_CONSTANT_MEDIAN: cuma->totpoint = 2; break; case CURVE_PRESET_SHARP: @@ -297,6 +313,10 @@ void BKE_curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope cuma->curve = static_cast( MEM_callocN(cuma->totpoint * sizeof(CurveMapPoint), "curve points")); + for (int i = 0; i < cuma->totpoint; i++) { + cuma->curve[i].flag = cuma->default_handle_type; + } + switch (preset) { case CURVE_PRESET_LINE: cuma->curve[0].x = clipr->xmin; @@ -304,10 +324,18 @@ void BKE_curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope cuma->curve[1].x = clipr->xmax; cuma->curve[1].y = clipr->ymin; if (slope == CURVEMAP_SLOPE_POS_NEG) { + cuma->curve[0].flag &= ~CUMA_HANDLE_AUTO_ANIM; + cuma->curve[1].flag &= ~CUMA_HANDLE_AUTO_ANIM; cuma->curve[0].flag |= CUMA_HANDLE_VECTOR; cuma->curve[1].flag |= CUMA_HANDLE_VECTOR; } break; + case CURVE_PRESET_CONSTANT_MEDIAN: + cuma->curve[0].x = clipr->xmin; + cuma->curve[0].y = (clipr->ymin + clipr->ymax) / 2.0f; + cuma->curve[1].x = clipr->xmax; + cuma->curve[1].y = (clipr->ymin + clipr->ymax) / 2.0f; + break; case CURVE_PRESET_SHARP: cuma->curve[0].x = 0; cuma->curve[0].y = 1; diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 47ce4a1c2e1..b1261f2cebd 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -158,7 +158,7 @@ static void scene_init_data(ID *id) STRNCPY(scene->r.bake.filepath, U.renderdir); mblur_shutter_curve = &scene->r.mblur_shutter_curve; - BKE_curvemapping_set_defaults(mblur_shutter_curve, 1, 0.0f, 0.0f, 1.0f, 1.0f); + BKE_curvemapping_set_defaults(mblur_shutter_curve, 1, 0.0f, 0.0f, 1.0f, 1.0f, HD_AUTO); BKE_curvemapping_init(mblur_shutter_curve); BKE_curvemap_reset(mblur_shutter_curve->cm, &mblur_shutter_curve->clipr, diff --git a/source/blender/blenkernel/intern/sound.cc b/source/blender/blenkernel/intern/sound.cc index fb5cddd3a10..b79a6b9df5d 100644 --- a/source/blender/blenkernel/intern/sound.cc +++ b/source/blender/blenkernel/intern/sound.cc @@ -789,6 +789,11 @@ void BKE_sound_update_scene_sound(void *handle, bSound *sound) AUD_SequenceEntry_setSound(handle, sound->playback_handle); } +void BKE_sound_update_sequence_handle(void *handle, void *sound_handle) +{ + AUD_SequenceEntry_setSound(handle, sound_handle); +} + void BKE_sound_set_cfra(int cfra) { sound_cfra = cfra; diff --git a/source/blender/blenloader/intern/versioning_270.cc b/source/blender/blenloader/intern/versioning_270.cc index 07296992500..c9194cbe90b 100644 --- a/source/blender/blenloader/intern/versioning_270.cc +++ b/source/blender/blenloader/intern/versioning_270.cc @@ -999,7 +999,7 @@ void blo_do_versions_270(FileData *fd, Library * /*lib*/, Main *bmain) if (!DNA_struct_elem_find(fd->filesdna, "RenderData", "CurveMapping", "mblur_shutter_curve")) { LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { CurveMapping *curve_mapping = &scene->r.mblur_shutter_curve; - BKE_curvemapping_set_defaults(curve_mapping, 1, 0.0f, 0.0f, 1.0f, 1.0f); + BKE_curvemapping_set_defaults(curve_mapping, 1, 0.0f, 0.0f, 1.0f, 1.0f, HD_AUTO); BKE_curvemapping_init(curve_mapping); BKE_curvemap_reset( curve_mapping->cm, &curve_mapping->clipr, CURVE_PRESET_MAX, CURVEMAP_SLOPE_POS_NEG); diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index b9a3fc5fddd..53c6e1302a4 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -279,6 +279,7 @@ void SEQUENCER_OT_strip_modifier_add(struct wmOperatorType *ot); void SEQUENCER_OT_strip_modifier_remove(struct wmOperatorType *ot); void SEQUENCER_OT_strip_modifier_move(struct wmOperatorType *ot); void SEQUENCER_OT_strip_modifier_copy(struct wmOperatorType *ot); +void SEQUENCER_OT_strip_modifier_equalizer_redefine(struct wmOperatorType *ot); /* `sequencer_view.cc` */ diff --git a/source/blender/editors/space_sequencer/sequencer_modifier.cc b/source/blender/editors/space_sequencer/sequencer_modifier.cc index 8a41b378925..c81e9ebbafb 100644 --- a/source/blender/editors/space_sequencer/sequencer_modifier.cc +++ b/source/blender/editors/space_sequencer/sequencer_modifier.cc @@ -11,6 +11,8 @@ #include "DNA_scene_types.h" +#include "DEG_depsgraph.h" + #include "BKE_context.h" #include "WM_api.hh" @@ -24,28 +26,13 @@ #include "SEQ_relations.h" #include "SEQ_select.h" #include "SEQ_sequencer.h" +#include "SEQ_sound.h" /* Own include. */ #include "sequencer_intern.h" /*********************** Add modifier operator *************************/ -static bool strip_modifier_active_poll(bContext *C) -{ - Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene); - - if (ed) { - Sequence *seq = SEQ_select_active_get(scene); - - if (seq) { - return SEQ_sequence_supports_modifiers(seq); - } - } - - return false; -} - static int strip_modifier_add_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); @@ -60,6 +47,21 @@ static int strip_modifier_add_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static const EnumPropertyItem *filter_modifiers_by_sequence_type(bContext *C, + PointerRNA * /* ptr */, + PropertyRNA * /* prop */, + bool * /* r_free */) +{ + Scene *scene = CTX_data_scene(C); + Sequence *seq = SEQ_select_active_get(scene); + if (ELEM(seq->type, SEQ_TYPE_SOUND_RAM)) { + return rna_enum_sequence_sound_modifier_type_items; + } + else { + return rna_enum_sequence_video_modifier_type_items; + } +} + void SEQUENCER_OT_strip_modifier_add(wmOperatorType *ot) { PropertyRNA *prop; @@ -71,18 +73,17 @@ void SEQUENCER_OT_strip_modifier_add(wmOperatorType *ot) /* api callbacks */ ot->exec = strip_modifier_add_exec; - ot->poll = strip_modifier_active_poll; + + /* + * No poll because a modifier can be applied to any kind of strip + */ /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - prop = RNA_def_enum(ot->srna, - "type", - rna_enum_sequence_modifier_type_items, - seqModifierType_ColorBalance, - "Type", - ""); + prop = RNA_def_enum(ot->srna, "type", rna_enum_dummy_NULL_items, 0, "Type", ""); + RNA_def_enum_funcs(prop, filter_modifiers_by_sequence_type); ot->prop = prop; } @@ -105,7 +106,12 @@ static int strip_modifier_remove_exec(bContext *C, wmOperator *op) BLI_remlink(&seq->modifiers, smd); SEQ_modifier_free(smd); - SEQ_relations_invalidate_cache_preprocessed(scene, seq); + if (ELEM(seq->type, SEQ_TYPE_SOUND_RAM)) { + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS | ID_RECALC_AUDIO); + } + else { + SEQ_relations_invalidate_cache_preprocessed(scene, seq); + } WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_FINISHED; @@ -122,7 +128,9 @@ void SEQUENCER_OT_strip_modifier_remove(wmOperatorType *ot) /* api callbacks */ ot->exec = strip_modifier_remove_exec; - ot->poll = strip_modifier_active_poll; + /* + * No poll is needed because all kind of strips can have their modifiers erased + */ /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -168,7 +176,13 @@ static int strip_modifier_move_exec(bContext *C, wmOperator *op) } } - SEQ_relations_invalidate_cache_preprocessed(scene, seq); + if (ELEM(seq->type, SEQ_TYPE_SOUND_RAM)) { + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS | ID_RECALC_AUDIO); + } + else { + SEQ_relations_invalidate_cache_preprocessed(scene, seq); + } + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_FINISHED; @@ -191,7 +205,10 @@ void SEQUENCER_OT_strip_modifier_move(wmOperatorType *ot) /* api callbacks */ ot->exec = strip_modifier_move_exec; - ot->poll = strip_modifier_active_poll; + + /* + * No poll is needed because all strips can have modifiers + */ /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -221,11 +238,19 @@ static int strip_modifier_copy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + int isSound = ELEM(seq->type, SEQ_TYPE_SOUND_RAM); + LISTBASE_FOREACH (Sequence *, seq_iter, SEQ_active_seqbase_get(ed)) { if (seq_iter->flag & SELECT) { if (seq_iter == seq) { continue; } + int seq_iter_is_sound = ELEM(seq_iter->type, SEQ_TYPE_SOUND_RAM); + /* If original is sound, only copy to "sound" strips + * If original is not sound, only copy to "not sound" strips + */ + if (isSound != seq_iter_is_sound) + continue; if (type == SEQ_MODIFIER_COPY_REPLACE) { if (seq_iter->modifiers.first) { @@ -245,7 +270,13 @@ static int strip_modifier_copy_exec(bContext *C, wmOperator *op) } } - SEQ_relations_invalidate_cache_preprocessed(scene, seq); + if (ELEM(seq->type, SEQ_TYPE_SOUND_RAM)) { + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS | ID_RECALC_AUDIO); + } + else { + SEQ_relations_invalidate_cache_preprocessed(scene, seq); + } + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_FINISHED; @@ -271,7 +302,9 @@ void SEQUENCER_OT_strip_modifier_copy(wmOperatorType *ot) /* api callbacks */ ot->invoke = WM_menu_invoke; ot->exec = strip_modifier_copy_exec; - ot->poll = strip_modifier_active_poll; + /* + * No poll is needed because all kind of strips can have modifier + */ /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -279,3 +312,60 @@ void SEQUENCER_OT_strip_modifier_copy(wmOperatorType *ot) /* properties */ ot->prop = RNA_def_enum(ot->srna, "type", type_items, SEQ_MODIFIER_COPY_REPLACE, "Type", ""); } + +static int strip_modifier_equalizer_redefine_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Sequence *seq = SEQ_select_active_get(scene); + SequenceModifierData *smd; + char name[MAX_NAME]; + RNA_string_get(op->ptr, "name", name); + int number = RNA_enum_get(op->ptr, "graphs"); + + smd = SEQ_modifier_find_by_name(seq, name); + if (!smd) { + return OPERATOR_CANCELLED; + } + + SEQ_sound_equalizermodifier_set_graphs((SoundEqualizerModifierData *)smd, number); + + SEQ_relations_invalidate_cache_preprocessed(scene, seq); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + + return OPERATOR_FINISHED; +} + +void SEQUENCER_OT_strip_modifier_equalizer_redefine(wmOperatorType *ot) +{ + + static const EnumPropertyItem enum_modifier_equalizer_presets_items[] = { + {1, "SIMPLE", 0, "Unique", "One unique graphical definition"}, + {2, "DOUBLE", 0, "Double", "Graphical definition in 2 sections"}, + {3, "TRIPLE", 0, "Triplet", "Graphical definition in 3 sections"}, + {0, NULL, 0, NULL, NULL}, + }; + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Redefine equalizer graphs"; + ot->idname = "SEQUENCER_OT_strip_modifier_equalizer_redefine"; + ot->description = "Redefine equalizer graphs"; + + /* api callbacks */ + ot->exec = strip_modifier_equalizer_redefine_exec; + + /* + * No poll because a modifier can be applied to any kind of strip + */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_enum( + ot->srna, "graphs", enum_modifier_equalizer_presets_items, 1, "Graphs", "Number of graphs"); + ot->prop = prop; + prop = RNA_def_string( + ot->srna, "name", "Name", MAX_NAME, "Name", "Name of modifier to redefine"); + RNA_def_property_flag(prop, PROP_HIDDEN); +} diff --git a/source/blender/editors/space_sequencer/sequencer_ops.cc b/source/blender/editors/space_sequencer/sequencer_ops.cc index 231b23f141a..789d81482d4 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.cc +++ b/source/blender/editors/space_sequencer/sequencer_ops.cc @@ -104,6 +104,7 @@ void sequencer_operatortypes() WM_operatortype_append(SEQUENCER_OT_strip_modifier_remove); WM_operatortype_append(SEQUENCER_OT_strip_modifier_move); WM_operatortype_append(SEQUENCER_OT_strip_modifier_copy); + WM_operatortype_append(SEQUENCER_OT_strip_modifier_equalizer_redefine); /* sequencer_view.h */ WM_operatortype_append(SEQUENCER_OT_sample); diff --git a/source/blender/makesdna/DNA_color_types.h b/source/blender/makesdna/DNA_color_types.h index fccc3917358..bdf9278df1d 100644 --- a/source/blender/makesdna/DNA_color_types.h +++ b/source/blender/makesdna/DNA_color_types.h @@ -60,6 +60,8 @@ typedef struct CurveMap { /** For RGB curves, pre-multiplied extrapolation vector. */ float premul_ext_in[2]; float premul_ext_out[2]; + short default_handle_type; + char _pad[6]; } CurveMap; typedef struct CurveMapping { @@ -107,6 +109,7 @@ typedef enum eCurveMappingPreset { CURVE_PRESET_ROOT = 6, CURVE_PRESET_GAUSS = 7, CURVE_PRESET_BELL = 8, + CURVE_PRESET_CONSTANT_MEDIAN = 9, } eCurveMappingPreset; /** #CurveMapping.tone */ diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index a92bde26db2..4f5c938fc03 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -527,6 +527,21 @@ enum { /** \} */ +/** \name Sound Modifiers + * \{ */ + +typedef struct EQCurveMappingData { + struct EQCurveMappingData *next, *prev; + struct CurveMapping curve_mapping; +} EQCurveMappingData; + +typedef struct SoundEqualizerModifierData { + SequenceModifierData modifier; + /* EQCurveMappingData */ + ListBase graphics; +} SoundEqualizerModifierData; +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Scopes * \{ */ @@ -760,6 +775,7 @@ enum { seqModifierType_Mask = 5, seqModifierType_WhiteBalance = 6, seqModifierType_Tonemap = 7, + seqModifierType_SoundEqualizer = 8, /* Keep last. */ NUM_SEQUENCE_MODIFIER_TYPES, }; diff --git a/source/blender/makesrna/RNA_enum_items.hh b/source/blender/makesrna/RNA_enum_items.hh index 3932d6e47c5..874440df81a 100644 --- a/source/blender/makesrna/RNA_enum_items.hh +++ b/source/blender/makesrna/RNA_enum_items.hh @@ -45,6 +45,8 @@ DEF_ENUM(rna_enum_object_modifier_type_items) DEF_ENUM(rna_enum_constraint_type_items) DEF_ENUM(rna_enum_boidrule_type_items) DEF_ENUM(rna_enum_sequence_modifier_type_items) +DEF_ENUM(rna_enum_sequence_video_modifier_type_items) +DEF_ENUM(rna_enum_sequence_sound_modifier_type_items) DEF_ENUM(rna_enum_object_greasepencil_modifier_type_items) DEF_ENUM(rna_enum_object_shaderfx_type_items) diff --git a/source/blender/makesrna/intern/rna_sequencer.cc b/source/blender/makesrna/intern/rna_sequencer.cc index 145a92760db..ebc0b695050 100644 --- a/source/blender/makesrna/intern/rna_sequencer.cc +++ b/source/blender/makesrna/intern/rna_sequencer.cc @@ -72,7 +72,24 @@ const EnumPropertyItem rna_enum_sequence_modifier_type_items[] = { {seqModifierType_Mask, "MASK", ICON_NONE, "Mask", ""}, {seqModifierType_Tonemap, "TONEMAP", ICON_NONE, "Tone Map", ""}, {seqModifierType_WhiteBalance, "WHITE_BALANCE", ICON_NONE, "White Balance", ""}, - {0, nullptr, 0, nullptr, nullptr}, + {seqModifierType_SoundEqualizer, "SOUND_EQUALIZER", ICON_NONE, "Sound Equalizer", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +const EnumPropertyItem rna_enum_sequence_video_modifier_type_items[] = { + {seqModifierType_BrightContrast, "BRIGHT_CONTRAST", ICON_NONE, "Bright/Contrast", ""}, + {seqModifierType_ColorBalance, "COLOR_BALANCE", ICON_NONE, "Color Balance", ""}, + {seqModifierType_Curves, "CURVES", ICON_NONE, "Curves", ""}, + {seqModifierType_HueCorrect, "HUE_CORRECT", ICON_NONE, "Hue Correct", ""}, + {seqModifierType_Mask, "MASK", ICON_NONE, "Mask", ""}, + {seqModifierType_Tonemap, "TONEMAP", ICON_NONE, "Tone Map", ""}, + {seqModifierType_WhiteBalance, "WHITE_BALANCE", ICON_NONE, "White Balance", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +const EnumPropertyItem rna_enum_sequence_sound_modifier_type_items[] = { + {seqModifierType_SoundEqualizer, "SOUND_EQUALIZER", ICON_NONE, "Equalizer", ""}, + {0, NULL, 0, NULL, NULL}, }; const EnumPropertyItem rna_enum_strip_color_items[] = { @@ -1286,6 +1303,8 @@ static StructRNA *rna_SequenceModifier_refine(PointerRNA *ptr) return &RNA_WhiteBalanceModifier; case seqModifierType_Tonemap: return &RNA_SequencerTonemapModifierData; + case seqModifierType_SoundEqualizer: + return &RNA_SoundEqualizerModifier; default: return &RNA_SequenceModifier; } @@ -1344,14 +1363,34 @@ static void rna_SequenceModifier_name_set(PointerRNA *ptr, const char *value) } } -static void rna_SequenceModifier_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) +static void rna_SequenceModifier_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr) { /* strip from other scenes could be modified, so using active scene is not reliable */ Scene *scene = (Scene *)ptr->owner_id; Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_modifier(ed, static_cast(ptr->data)); - SEQ_relations_invalidate_cache_preprocessed(scene, seq); + if (ELEM(seq->type, SEQ_TYPE_SOUND_RAM, SEQ_TYPE_SOUND_HD)) { + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS | ID_RECALC_AUDIO); + DEG_relations_tag_update(bmain); + } + else { + SEQ_relations_invalidate_cache_preprocessed(scene, seq); + } +} + +/* + * Update of Curve in an EQ Sound Modifier + */ +static void rna_SequenceModifier_EQCurveMapping_update(Main *bmain, + Scene * /*scene*/, + PointerRNA *ptr) +{ + Scene *scene = (Scene *)ptr->owner_id; + + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS | ID_RECALC_AUDIO); + DEG_relations_tag_update(bmain); + WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL); } static bool rna_SequenceModifier_otherSequence_poll(PointerRNA *ptr, PointerRNA value) @@ -1554,6 +1593,23 @@ static char *rna_SeqTimelineChannel_path(const PointerRNA *ptr) } } +static EQCurveMappingData *rna_Sequence_SoundEqualizer_Curve_add(SoundEqualizerModifierData *semd, + bContext * /* C */, + float min_freq, + float max_freq) +{ + EQCurveMappingData *eqcmd = SEQ_sound_equalizermodifier_add_graph(semd, min_freq, max_freq); + WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL); + return eqcmd; +} + +static void rna_Sequence_SoundEqualizer_Curve_clear(SoundEqualizerModifierData *semd, + bContext * /* C */) +{ + SEQ_sound_equalizermodifier_free((SequenceModifierData *)semd); + WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL); +} + #else static void rna_def_strip_element(BlenderRNA *brna) @@ -3757,6 +3813,89 @@ static void rna_def_modifiers(BlenderRNA *brna) rna_def_tonemap_modifier(brna); } +static void rna_def_graphical_sound_equalizer(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + /* Define Sound EQ */ + srna = RNA_def_struct(brna, "EQCurveMappingData", NULL); + RNA_def_struct_sdna(srna, "EQCurveMappingData"); + RNA_def_struct_ui_text(srna, "EQCurveMappingData", "EQCurveMappingData"); + + prop = RNA_def_property(srna, "curve_mapping", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "curve_mapping"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_ui_text(prop, "Curve Mapping", ""); + RNA_def_property_update( + prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceModifier_EQCurveMapping_update"); +} + +static void rna_def_sound_equalizer_modifier(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + FunctionRNA *func; + PropertyRNA *parm; + + srna = RNA_def_struct(brna, "SoundEqualizerModifier", "SequenceModifier"); + RNA_def_struct_sdna(srna, "SoundEqualizerModifierData"); + RNA_def_struct_ui_text(srna, "SoundEqualizerModifier", "Equalize audio"); + + /* Sound Equalizers*/ + prop = RNA_def_property(srna, "graphics", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "EQCurveMappingData"); + RNA_def_property_ui_text( + prop, "Graphical definition equalization", "Graphical definition equalization"); + + /* add band*/ + func = RNA_def_function(srna, "new_graphic", "rna_Sequence_SoundEqualizer_Curve_add"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + RNA_def_function_ui_description(func, "Add a new EQ band"); + + parm = RNA_def_float(func, + "min_freq", + SOUND_EQUALIZER_DEFAULT_MIN_FREQ, + 0.0, + SOUND_EQUALIZER_DEFAULT_MAX_FREQ, /* Hard min and max */ + "Minimum Frequency", + "Minimum Frequency", + 0.0, + SOUND_EQUALIZER_DEFAULT_MAX_FREQ); /* Soft min and max */ + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + parm = RNA_def_float(func, + "max_freq", + SOUND_EQUALIZER_DEFAULT_MAX_FREQ, + 0.0, + SOUND_EQUALIZER_DEFAULT_MAX_FREQ, /* Hard min and max */ + "Maximum Frequency", + "Maximum Frequency", + 0.0, + SOUND_EQUALIZER_DEFAULT_MAX_FREQ); /* Soft min and max */ + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + + /* return type */ + parm = RNA_def_pointer(func, + "graphic_eqs", + "EQCurveMappingData", + "", + "Newly created graphical Equalizer definition"); + RNA_def_function_return(func, parm); + + /* clear all modifiers */ + func = RNA_def_function(srna, "clear_soundeqs", "rna_Sequence_SoundEqualizer_Curve_clear"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + RNA_def_function_ui_description(func, + "Remove all graphical equalizers from the Equalizer modifier"); + + rna_def_graphical_sound_equalizer(brna); +} + +static void rna_def_sound_modifiers(BlenderRNA *brna) +{ + rna_def_sound_equalizer_modifier(brna); +} + void RNA_def_sequencer(BlenderRNA *brna) { rna_def_color_balance(brna); @@ -3782,6 +3921,7 @@ void RNA_def_sequencer(BlenderRNA *brna) rna_def_effect(brna); rna_def_effects(brna); rna_def_modifiers(brna); + rna_def_sound_modifiers(brna); } #endif diff --git a/source/blender/sequencer/CMakeLists.txt b/source/blender/sequencer/CMakeLists.txt index 7e9d57af31f..a7212bc4235 100644 --- a/source/blender/sequencer/CMakeLists.txt +++ b/source/blender/sequencer/CMakeLists.txt @@ -101,6 +101,10 @@ if(WITH_AUDASPACE) ) endif() add_definitions(-DWITH_AUDASPACE) + + if(WITH_FFTW3) + add_definitions(-DWITH_CONVOLUTION) + endif() endif() blender_add_lib(bf_sequencer "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/sequencer/SEQ_sound.h b/source/blender/sequencer/SEQ_sound.h index 09049a87d50..eb12d70e53c 100644 --- a/source/blender/sequencer/SEQ_sound.h +++ b/source/blender/sequencer/SEQ_sound.h @@ -16,12 +16,51 @@ struct Main; struct Scene; struct Sequence; struct bSound; +struct SequencerSoundEqualizer; +struct BlendWriter; +struct BlendDataReader; +struct ListBase; +struct SoundEqualizerModifierData; + +#define SOUND_EQUALIZER_DEFAULT_MIN_FREQ 30.0 +#define SOUND_EQUALIZER_DEFAULT_MAX_FREQ 20000.0 +#define SOUND_EQUALIZER_DEFAULT_MAX_DB 35.0 +#define SOUND_EQUALIZER_SIZE_CONVERSION 2048 +#define SOUND_EQUALIZER_SIZE_DEFINITION 1000 void SEQ_sound_update_bounds_all(struct Scene *scene); void SEQ_sound_update_bounds(struct Scene *scene, struct Sequence *seq); void SEQ_sound_update(struct Scene *scene, struct bSound *sound); void SEQ_sound_update_length(struct Main *bmain, struct Scene *scene); float SEQ_sound_pitch_get(const struct Scene *scene, const struct Sequence *seq); +struct EQCurveMappingData *SEQ_sound_equalizer_add(struct SoundEqualizerModifierData *semd, + float minX, + float maxX); +void SEQ_sound_blend_write(struct BlendWriter *writer, struct ListBase *soundbase); +void SEQ_sound_blend_read_data(struct BlendDataReader *reader, struct ListBase *lb); + +void *SEQ_sound_modifier_recreator(struct Sequence *seq, + struct SequenceModifierData *smd, + void *sound); + +void SEQ_sound_equalizermodifier_init_data(struct SequenceModifierData *smd); +void SEQ_sound_equalizermodifier_free(struct SequenceModifierData *smd); +void SEQ_sound_equalizermodifier_copy_data(struct SequenceModifierData *target, + struct SequenceModifierData *smd); +void *SEQ_sound_equalizermodifier_recreator(struct Sequence *seq, + struct SequenceModifierData *smd, + void *sound); +void SEQ_sound_equalizermodifier_set_graphs(struct SoundEqualizerModifierData *semd, int number); +const struct SoundModifierWorkerInfo *SEQ_sound_modifier_worker_info_get(int type); +struct EQCurveMappingData *SEQ_sound_equalizermodifier_add_graph( + struct SoundEqualizerModifierData *semd, float min_freq, float max_freq); +void SEQ_sound_equalizermodifier_remove_graph(struct SoundEqualizerModifierData *semd, + struct EQCurveMappingData *gsed); + +typedef struct SoundModifierWorkerInfo { + int type; + void *(*recreator)(struct Sequence *seq, struct SequenceModifierData *smd, void *sound); +} SoundModifierWorkerInfo; #ifdef __cplusplus } diff --git a/source/blender/sequencer/intern/modifier.cc b/source/blender/sequencer/intern/modifier.cc index 0122fdfb1de..f6bf74f679c 100644 --- a/source/blender/sequencer/intern/modifier.cc +++ b/source/blender/sequencer/intern/modifier.cc @@ -30,6 +30,7 @@ #include "SEQ_modifier.h" #include "SEQ_render.h" +#include "SEQ_sound.h" #include "BLO_read_write.hh" @@ -741,7 +742,7 @@ static void curves_init_data(SequenceModifierData *smd) { CurvesModifierData *cmd = (CurvesModifierData *)smd; - BKE_curvemapping_set_defaults(&cmd->curve_mapping, 4, 0.0f, 0.0f, 1.0f, 1.0f); + BKE_curvemapping_set_defaults(&cmd->curve_mapping, 4, 0.0f, 0.0f, 1.0f, 1.0f, HD_AUTO); } static void curves_free_data(SequenceModifierData *smd) @@ -860,7 +861,7 @@ static void hue_correct_init_data(SequenceModifierData *smd) HueCorrectModifierData *hcmd = (HueCorrectModifierData *)smd; int c; - BKE_curvemapping_set_defaults(&hcmd->curve_mapping, 1, 0.0f, 0.0f, 1.0f, 1.0f); + BKE_curvemapping_set_defaults(&hcmd->curve_mapping, 1, 0.0f, 0.0f, 1.0f, 1.0f, HD_AUTO); hcmd->curve_mapping.preset = CURVE_PRESET_MID9; for (c = 0; c < 3; c++) { @@ -1371,6 +1372,15 @@ static SequenceModifierTypeInfo seqModifier_Tonemap = { /*apply*/ tonemapmodifier_apply, }; +static SequenceModifierTypeInfo seqModifier_SoundEqualizer = { + CTX_N_(BLT_I18NCONTEXT_ID_SEQUENCE, "Equalizer"), /* name */ + "SoundEqualizerModifierData", /* struct_name */ + sizeof(SoundEqualizerModifierData), /* struct_size */ + SEQ_sound_equalizermodifier_init_data, /* init_data */ + SEQ_sound_equalizermodifier_free, /* free_data */ + SEQ_sound_equalizermodifier_copy_data, /* copy_data */ + NULL, /* apply */ +}; /** \} */ /* -------------------------------------------------------------------- */ @@ -1388,6 +1398,7 @@ static void sequence_modifier_type_info_init() INIT_TYPE(Mask); INIT_TYPE(WhiteBalance); INIT_TYPE(Tonemap); + INIT_TYPE(SoundEqualizer); #undef INIT_TYPE } @@ -1591,6 +1602,13 @@ void SEQ_modifier_blend_write(BlendWriter *writer, ListBase *modbase) BKE_curvemapping_blend_write(writer, &hcmd->curve_mapping); } + else if (smd->type == seqModifierType_SoundEqualizer) { + SoundEqualizerModifierData *semd = (SoundEqualizerModifierData *)smd; + LISTBASE_FOREACH (EQCurveMappingData *, eqcmd, &semd->graphics) { + BLO_write_struct_by_name(writer, "EQCurveMappingData", eqcmd); + BKE_curvemapping_blend_write(writer, &eqcmd->curve_mapping); + } + } } else { BLO_write_struct(writer, SequenceModifierData, smd); @@ -1617,6 +1635,13 @@ void SEQ_modifier_blend_read_data(BlendDataReader *reader, ListBase *lb) BKE_curvemapping_blend_read(reader, &hcmd->curve_mapping); } + else if (smd->type == seqModifierType_SoundEqualizer) { + SoundEqualizerModifierData *semd = (SoundEqualizerModifierData *)smd; + BLO_read_list(reader, &semd->graphics); + LISTBASE_FOREACH (EQCurveMappingData *, eqcmd, &semd->graphics) { + BKE_curvemapping_blend_read(reader, &eqcmd->curve_mapping); + } + } } } diff --git a/source/blender/sequencer/intern/sequencer.cc b/source/blender/sequencer/intern/sequencer.cc index 1664d7b21d0..c6a86585576 100644 --- a/source/blender/sequencer/intern/sequencer.cc +++ b/source/blender/sequencer/intern/sequencer.cc @@ -918,6 +918,15 @@ static bool seq_update_seq_cb(Sequence *seq, void *user_data) if (scene->id.recalc & ID_RECALC_AUDIO || seq->sound->id.recalc & ID_RECALC_AUDIO) { BKE_sound_update_scene_sound(seq->scene_sound, seq->sound); } + + void *sound = seq->sound->playback_handle; + + if (!BLI_listbase_is_empty(&seq->modifiers)) { + LISTBASE_FOREACH (SequenceModifierData *, smd, &seq->modifiers) { + sound = SEQ_sound_modifier_recreator(seq, smd, sound); + } + } + BKE_sound_update_sequence_handle(seq->scene_sound, sound); } BKE_sound_set_scene_sound_volume( seq->scene_sound, seq->volume, (seq->flag & SEQ_AUDIO_VOLUME_ANIMATED) != 0); diff --git a/source/blender/sequencer/intern/sound.cc b/source/blender/sequencer/intern/sound.cc index 66ddee628d7..d7af260f992 100644 --- a/source/blender/sequencer/intern/sound.cc +++ b/source/blender/sequencer/intern/sound.cc @@ -9,7 +9,12 @@ */ #include +#include +#include +#include "MEM_guardedalloc.h" + +#include "DNA_curve_types.h" #include "DNA_scene_types.h" #include "DNA_sequence_types.h" #include "DNA_sound_types.h" @@ -17,10 +22,15 @@ #include "BLI_listbase.h" #include "BLI_utildefines.h" +#include "BLO_read_write.hh" + +#include "BKE_colortools.h" #include "BKE_main.h" #include "BKE_scene.h" #include "BKE_sound.h" +#include "AUD_Sound.h" + #include "SEQ_sound.h" #include "SEQ_time.h" @@ -29,6 +39,9 @@ /* Unlike _update_sound_ functions, * these ones take info from audaspace to update sequence length! */ +const SoundModifierWorkerInfo workersSoundModifiers[] = { + {seqModifierType_SoundEqualizer, SEQ_sound_equalizermodifier_recreator}, {0, NULL}}; + #ifdef WITH_AUDASPACE static bool sequencer_refresh_sound_length_recursive(Main *bmain, Scene *scene, ListBase *seqbase) { @@ -140,3 +153,191 @@ float SEQ_sound_pitch_get(const Scene *scene, const Sequence *seq) } return seq->speed_factor; } + +struct EQCurveMappingData *SEQ_sound_equalizer_add(SoundEqualizerModifierData *semd, + float minX, + float maxX) +{ + EQCurveMappingData *eqcmd; + + if (maxX < 0) + maxX = SOUND_EQUALIZER_DEFAULT_MAX_FREQ; + if (minX < 0) + minX = 0.0; + /* It's the same as BKE_curvemapping_add , but changing the name */ + eqcmd = MEM_cnew("Equalizer"); + BKE_curvemapping_set_defaults(&eqcmd->curve_mapping, + 1, /* tot*/ + minX, + -SOUND_EQUALIZER_DEFAULT_MAX_DB, /* Min x, y */ + maxX, + SOUND_EQUALIZER_DEFAULT_MAX_DB, /* Max x, y */ + HD_AUTO_ANIM); + + eqcmd->curve_mapping.preset = CURVE_PRESET_CONSTANT_MEDIAN; + + rctf clipr; + clipr.xmin = minX; + clipr.xmax = maxX; + clipr.ymin = 0.0; + clipr.ymax = 0.0; + + BKE_curvemap_reset(&eqcmd->curve_mapping.cm[0], &clipr, CURVE_PRESET_CONSTANT_MEDIAN, 0); + + BLI_addtail(&semd->graphics, eqcmd); + + return eqcmd; +} + +void SEQ_sound_equalizermodifier_set_graphs(struct SoundEqualizerModifierData *semd, int number) +{ + SEQ_sound_equalizermodifier_free((SequenceModifierData *)semd); + if (number == 1) { + SEQ_sound_equalizer_add( + semd, SOUND_EQUALIZER_DEFAULT_MIN_FREQ, SOUND_EQUALIZER_DEFAULT_MAX_FREQ); + } + else if (number == 2) { + SEQ_sound_equalizer_add(semd, 30.0, 2000.0); + SEQ_sound_equalizer_add(semd, 2000.1, 20000.0); + } + else if (number == 3) { + SEQ_sound_equalizer_add(semd, 30.0, 1000.0); + SEQ_sound_equalizer_add(semd, 1000.1, 5000.0); + SEQ_sound_equalizer_add(semd, 5000.1, 20000.0); + } +} + +EQCurveMappingData *SEQ_sound_equalizermodifier_add_graph(struct SoundEqualizerModifierData *semd, + float min_freq, + float max_freq) +{ + if (min_freq < 0.0) + return NULL; + if (max_freq < 0.0) + return NULL; + if (max_freq <= min_freq) + return NULL; + return SEQ_sound_equalizer_add(semd, min_freq, max_freq); +} + +void SEQ_sound_equalizermodifier_remove_graph(struct SoundEqualizerModifierData *semd, + struct EQCurveMappingData *eqcmd) +{ + BLI_remlink_safe(&semd->graphics, eqcmd); + MEM_freeN(eqcmd); +} + +void SEQ_sound_equalizermodifier_init_data(SequenceModifierData *smd) +{ + SoundEqualizerModifierData *semd = (SoundEqualizerModifierData *)smd; + + SEQ_sound_equalizer_add( + semd, SOUND_EQUALIZER_DEFAULT_MIN_FREQ, SOUND_EQUALIZER_DEFAULT_MAX_FREQ); +} + +void SEQ_sound_equalizermodifier_free(SequenceModifierData *smd) +{ + SoundEqualizerModifierData *semd = (SoundEqualizerModifierData *)smd; + LISTBASE_FOREACH_MUTABLE (EQCurveMappingData *, eqcmd, &semd->graphics) { + BKE_curvemapping_free_data(&eqcmd->curve_mapping); + MEM_freeN(eqcmd); + } + BLI_listbase_clear(&semd->graphics); +} + +void SEQ_sound_equalizermodifier_copy_data(struct SequenceModifierData *target, + struct SequenceModifierData *smd) +{ + SoundEqualizerModifierData *semd = (SoundEqualizerModifierData *)smd; + SoundEqualizerModifierData *semd_target = (SoundEqualizerModifierData *)target; + EQCurveMappingData *eqcmd_n; + + BLI_listbase_clear(&semd_target->graphics); + + LISTBASE_FOREACH (EQCurveMappingData *, eqcmd, &semd->graphics) { + eqcmd_n = static_cast(MEM_dupallocN(eqcmd)); + BKE_curvemapping_copy_data(&eqcmd_n->curve_mapping, &eqcmd->curve_mapping); + + eqcmd_n->next = eqcmd_n->prev = NULL; + BLI_addtail(&semd_target->graphics, eqcmd_n); + } +} + +void *SEQ_sound_equalizermodifier_recreator(struct Sequence *seq, + struct SequenceModifierData *smd, + void *sound) +{ + UNUSED_VARS(seq); + + SoundEqualizerModifierData *semd = (SoundEqualizerModifierData *)smd; + + // No Equalizer definition + if (BLI_listbase_is_empty(&semd->graphics)) { + return sound; + } + + float *buf = (float *)MEM_callocN(sizeof(float) * SOUND_EQUALIZER_SIZE_DEFINITION, + "eqrecreator"); + + CurveMapping *eq_mapping; + CurveMap *cm; + float minX; + float maxX; + float interval = SOUND_EQUALIZER_DEFAULT_MAX_FREQ / (float)SOUND_EQUALIZER_SIZE_DEFINITION; + + // Visit all equalizer definitions + LISTBASE_FOREACH (EQCurveMappingData *, mapping, &semd->graphics) { + eq_mapping = &mapping->curve_mapping; + BKE_curvemapping_init(eq_mapping); + cm = eq_mapping->cm; + minX = eq_mapping->curr.xmin; + maxX = eq_mapping->curr.xmax; + int idx = (int)ceil(minX / interval); + int i = idx; + for (; i * interval <= maxX && i < SOUND_EQUALIZER_SIZE_DEFINITION; i++) { + float freq = i * interval; + float val = BKE_curvemap_evaluateF(eq_mapping, cm, freq); + if (fabs(val) > SOUND_EQUALIZER_DEFAULT_MAX_DB) + val = (val / fabs(val)) * SOUND_EQUALIZER_DEFAULT_MAX_DB; + buf[i] = val; + /* To soften lower limit, but not the first position which is the constant value */ + if (i == idx && i > 2) { + buf[i - 1] = 0.5 * (buf[i] + buf[i - 1]); + } + } + /* To soften higher limit */ + if (i < SOUND_EQUALIZER_SIZE_DEFINITION) + buf[i] = 0.5 * (buf[i] + buf[i - 1]); + } + + AUD_Sound *equ = AUD_Sound_equalize(sound, + buf, + SOUND_EQUALIZER_SIZE_DEFINITION, + SOUND_EQUALIZER_DEFAULT_MAX_FREQ, + SOUND_EQUALIZER_SIZE_CONVERSION); + + MEM_freeN(buf); + + return equ; +} + +const struct SoundModifierWorkerInfo *SEQ_sound_modifier_worker_info_get(int type) +{ + for (int i = 0; workersSoundModifiers[i].type > 0; i++) { + if (workersSoundModifiers[i].type == type) + return &workersSoundModifiers[i]; + } + return NULL; +} + +void *SEQ_sound_modifier_recreator(struct Sequence *seq, + struct SequenceModifierData *smd, + void *sound) +{ + + if (!(smd->flag & SEQUENCE_MODIFIER_MUTE)) { + const SoundModifierWorkerInfo *smwi = SEQ_sound_modifier_worker_info_get(smd->type); + return smwi->recreator(seq, smd, sound); + } + return sound; +}