diff --git a/README.md b/README.md index 2123151..a6d4ee1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # OBS Image Reaction Plugin -Image that reacts to sound source. It change picture from one to another based on volume. +Image that reacts to sound source. ## Installing binaries Download binaries from [Releases](https://github.com/scaledteam/obs-image-reaction/releases/). @@ -18,6 +18,7 @@ cmake .. make mkdir -p ~/.config/obs-studio/plugins/libimage-reaction/bin/64bit cp libimage-reaction.so ~/.config/obs-studio/plugins/libimage-reaction/bin/64bit/ +cp -r ../data ~/.config/obs-studio/plugins/libimage-reaction/ ``` ## Building for Windows from GNU/Linux: diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini new file mode 100644 index 0000000..95e7fbc --- /dev/null +++ b/data/locale/en-US.ini @@ -0,0 +1,10 @@ +ImageReactionSource="Image Reaction" +Reaction1="Image when silence" +Reaction2="Image when sound" +AnimReset1="Reset animation of the first image after reaction" +AnimReset2="Reset animation of the second image after reaction" +UnloadWhenNotShowing="Unload image when not showing" +LinearAlpha="Apply alpha in linear space" +Threshold="Reaction threshold" +Smoothness="Smoothness" +AudioSource="Audio source" diff --git a/data/locale/ru-RU.ini b/data/locale/ru-RU.ini new file mode 100644 index 0000000..f93f00d --- /dev/null +++ b/data/locale/ru-RU.ini @@ -0,0 +1,10 @@ +ImageReactionSource="Реактивная картинка" +Reaction1="Изображение при тишине" +Reaction2="Изображение при звуке" +AnimReset1="Сбрасывать анимацию первой картинки после реакции" +AnimReset2="Сбрасывать анимацию второй картинки после реакции" +UnloadWhenNotShowing="Выгружать изображения, которые не показываются" +LinearAlpha="Применить альфа канал в линейном пространстве" +Threshold="Порог срабатывания" +Smoothness="Плавность" +AudioSource="Источник звука" diff --git a/image-reaction.c b/image-reaction.c index eabe740..4adca02 100644 --- a/image-reaction.c +++ b/image-reaction.c @@ -37,6 +37,13 @@ struct image_reaction_source { float threshold; float smoothness; float average; + + uint64_t last_time; + + bool animReset1; + bool animReset2; + bool loudOld; + bool animResetTrigger; }; /*int MAX(int a, int b) { @@ -49,7 +56,7 @@ struct image_reaction_source { static const char *image_reaction_source_get_name(void *unused) { UNUSED_PARAMETER(unused); - return obs_module_text("Image Reaction Source"); + return obs_module_text("ImageReactionSource"); } static void image_reaction_source_load(struct image_reaction_source *context) @@ -92,7 +99,6 @@ static void audio_capture(void *param, obs_source_t *src, const struct audio_dat { struct image_reaction_source *context = param; - context->loud = false; uint32_t samplesCount = data->frames; float* samples = (float*)data->data[0]; @@ -104,10 +110,11 @@ static void audio_capture(void *param, obs_source_t *src, const struct audio_dat } context->average += context->smoothness * (averageLocal - context->average); - if (context->average > context->threshold) { - context->loud = true; - } - //printf("%f\n", average); + context->loudOld = context->loud; + context->loud = context->average > context->threshold; + + if (context->loud != context->loudOld) + context->animResetTrigger = true; } static void image_reaction_source_update(void *data, obs_data_t *settings) @@ -115,6 +122,8 @@ static void image_reaction_source_update(void *data, obs_data_t *settings) struct image_reaction_source *context = data; const char *file1 = obs_data_get_string(settings, "file1"); const char *file2 = obs_data_get_string(settings, "file2"); + const bool anim_reset_1 = obs_data_get_bool(settings, "anim_reset_1"); + const bool anim_reset_2 = obs_data_get_bool(settings, "anim_reset_2"); const bool unload = obs_data_get_bool(settings, "unload"); const bool linear_alpha = obs_data_get_bool(settings, "linear_alpha"); const double threshold = obs_data_get_double(settings, "threshold"); @@ -128,6 +137,9 @@ static void image_reaction_source_update(void *data, obs_data_t *settings) bfree(context->file2); context->file2 = bstrdup(file2); + context->animReset1 = anim_reset_1; + context->animReset2 = anim_reset_2; + context->persistent = !unload; context->linear_alpha = linear_alpha; context->threshold = db_to_mul(threshold); @@ -260,6 +272,67 @@ static void image_reaction_source_render(void *data, gs_effect_t *effect) gs_enable_framebuffer_srgb(previous); } +static void image_reaction_tick(void *data, float seconds) +{ + struct image_reaction_source *context = data; + uint64_t frame_time = obs_get_video_frame_time(); + + for (int i = 0; i <=1; i++) { + gs_image_file3_t *if3 = i == 0 ? &context->if31 : &context->if32; + bool animReset = i == 0 ? context->animReset1 : context->animReset2; + + if (obs_source_active(context->source)) { + if (!context->active) { + if (if3->image2.image.is_animated_gif) + context->last_time = frame_time; + context->active = true; + } + + } else { + if (context->active) { + if (if3->image2.image.is_animated_gif) { + if3->image2.image.cur_frame = 0; + if3->image2.image.cur_loop = 0; + if3->image2.image.cur_time = 0; + + obs_enter_graphics(); + gs_image_file3_update_texture(if3); + obs_leave_graphics(); + } + + context->active = false; + } + + //return; + } + + if (context->last_time && if3->image2.image.is_animated_gif) { + if (animReset && context->animResetTrigger) { + if3->image2.image.cur_frame = 0; + if3->image2.image.cur_loop = 0; + if3->image2.image.cur_time = 0; + + obs_enter_graphics(); + gs_image_file3_update_texture(if3); + obs_leave_graphics(); + } + else { + uint64_t elapsed = frame_time - context->last_time; + bool updated = gs_image_file3_tick(if3, elapsed); + + if (updated) { + obs_enter_graphics(); + gs_image_file3_update_texture(if3); + obs_leave_graphics(); + } + } + } + } + context->animResetTrigger = false; + + context->last_time = frame_time; +} + static const char *image_filter = "All formats (*.bmp *.tga *.png *.jpeg *.jpg *.gif *.psd *.webp);;" "BMP Files (*.bmp);;" @@ -309,8 +382,12 @@ static obs_properties_t *image_reaction_source_properties(void *data) obs_properties_add_path(props, "file1", obs_module_text("Reaction1"), OBS_PATH_FILE, image_filter, path.array); + obs_properties_add_bool(props, "anim_reset_1", + obs_module_text("AnimReset1")); obs_properties_add_path(props, "file2", obs_module_text("Reaction2"), OBS_PATH_FILE, image_filter, path.array); + obs_properties_add_bool(props, "anim_reset_2", + obs_module_text("AnimReset2")); dstr_free(&path); obs_properties_add_bool(props, "unload", @@ -366,12 +443,13 @@ static struct obs_source_info image_reaction_source_info = { .get_width = image_reaction_source_getwidth, .get_height = image_reaction_source_getheight, .video_render = image_reaction_source_render, + .video_tick = image_reaction_tick, .get_properties = image_reaction_source_properties, .icon_type = OBS_ICON_TYPE_IMAGE, }; OBS_DECLARE_MODULE() -OBS_MODULE_USE_DEFAULT_LOCALE("image-source", "en-US") +OBS_MODULE_USE_DEFAULT_LOCALE("image-reaction", "en-US") MODULE_EXPORT const char *obs_module_description(void) { return "Image reaction source";