Skip to content

Commit 0fbba73

Browse files
committed
Add audio_peak and audio_magnitude support.
1 parent 11a9d65 commit 0fbba73

File tree

2 files changed

+182
-0
lines changed

2 files changed

+182
-0
lines changed

data/examples/audio.shader

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Audio shader example showing the difference between audio_peak and audio_magnitude.
2+
// Left half uses audio_peak (red), right half uses audio_magnitude (blue).
3+
4+
uniform float intensity <
5+
string label = "Audio intensity";
6+
string widget_type = "slider";
7+
float minimum = 0.1;
8+
float maximum = 3.0;
9+
float step = 0.1;
10+
> = 1.0;
11+
12+
float4 mainImage(VertData v_in) : TARGET {
13+
float4 color = image.Sample(textureSampler, v_in.uv);
14+
15+
// Split screen based on UV coordinate
16+
if (v_in.uv.x < 0.5) {
17+
// Left half: audio_peak (instantaneous spikes, more reactive)
18+
// Tint with red to show peak activity.
19+
float peak_strength = audio_peak * intensity;
20+
float3 peak_color = color.rgb + float3(peak_strength, 0, 0);
21+
return float4(peak_color, color.a);
22+
} else {
23+
// Right half: audio_magnitude (RMS/averaged levels, smoother)
24+
// Tint with blue to show magnitude activity.
25+
float mag_strength = audio_magnitude * intensity;
26+
float3 mag_color = color.rgb + float3(0, 0, mag_strength);
27+
return float4(mag_color, color.a);
28+
}
29+
}
30+
31+
/*
32+
EXPLANATION:
33+
- audio_peak: Shows instantaneous maximum levels, very responsive to drums/percussion.
34+
- audio_magnitude: Shows RMS (Root Mean Square) levels, smoother and represents sustained audio.
35+
36+
TYPICAL BEHAVIOR:
37+
- With music containing drums: Left side (peak) will flash more dramatically on beats.
38+
- With sustained tones: Right side (magnitude) will show more consistent levels.
39+
- Peak reacts faster to sudden sounds, magnitude is more stable for smooth effects.
40+
*/

obs-shaderfilter.c

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <stdio.h>
1616
#include <time.h>
1717
#include <string.h>
18+
#include <math.h>
1819

1920
#include <util/threading.h>
2021
#ifdef _WIN32
@@ -48,6 +49,8 @@ uniform float elapsed_time_enable;\n\
4849
uniform int loops;\n\
4950
uniform float loop_second;\n\
5051
uniform float local_time;\n\
52+
uniform float audio_peak;\n\
53+
uniform float audio_magnitude;\n\
5154
\n\
5255
sampler_state textureSampler{\n\
5356
Filter = Linear;\n\
@@ -225,6 +228,8 @@ struct shader_filter_data {
225228
gs_eparam_t *param_transition_time;
226229
gs_eparam_t *param_convert_linear;
227230
gs_eparam_t *param_previous_output;
231+
gs_eparam_t *param_audio_peak;
232+
gs_eparam_t *param_audio_magnitude;
228233

229234
int expand_left;
230235
int expand_right;
@@ -247,6 +252,14 @@ struct shader_filter_data {
247252
float rand_f;
248253
float rand_instance_f;
249254
float rand_activation_f;
255+
float audio_peak;
256+
float audio_magnitude;
257+
258+
obs_source_t *audio_source;
259+
char *audio_source_name;
260+
obs_volmeter_t *volmeter;
261+
float current_audio_peak;
262+
float current_audio_magnitude;
250263

251264
DARRAY(struct effect_param_data) stored_param_list;
252265
};
@@ -351,6 +364,8 @@ static void shader_filter_clear_params(struct shader_filter_data *filter)
351364
filter->param_loops = NULL;
352365
filter->param_loop_second = NULL;
353366
filter->param_local_time = NULL;
367+
filter->param_audio_peak = NULL;
368+
filter->param_audio_magnitude = NULL;
354369
filter->param_image = NULL;
355370
filter->param_previous_image = NULL;
356371
filter->param_image_a = NULL;
@@ -612,6 +627,10 @@ static void shader_filter_reload_effect(struct shader_filter_data *filter)
612627
filter->param_loop_second = param;
613628
} else if (strcmp(info.name, "local_time") == 0) {
614629
filter->param_local_time = param;
630+
} else if (strcmp(info.name, "audio_peak") == 0) {
631+
filter->param_audio_peak = param;
632+
} else if (strcmp(info.name, "audio_magnitude") == 0) {
633+
filter->param_audio_magnitude = param;
615634
} else if (strcmp(info.name, "ViewProj") == 0) {
616635
// Nothing.
617636
} else if (strcmp(info.name, "image") == 0) {
@@ -710,6 +729,14 @@ static void *shader_filter_create(obs_data_t *settings, obs_source_t *source)
710729
filter->rand_instance_f = (float)((double)rand_interval(0, 10000) / (double)10000);
711730
filter->rand_activation_f = (float)((double)rand_interval(0, 10000) / (double)10000);
712731

732+
filter->audio_peak = 0.0f;
733+
filter->audio_magnitude = 0.0f;
734+
filter->audio_source = NULL;
735+
filter->audio_source_name = NULL;
736+
filter->volmeter = NULL;
737+
filter->current_audio_peak = 0.0f;
738+
filter->current_audio_magnitude = 0.0f;
739+
713740
da_init(filter->stored_param_list);
714741
load_output_effect(filter);
715742
obs_source_update(source, settings);
@@ -741,6 +768,13 @@ static void shader_filter_destroy(void *data)
741768

742769
dstr_free(&filter->last_path);
743770
da_free(filter->stored_param_list);
771+
772+
if (filter->volmeter)
773+
obs_volmeter_destroy(filter->volmeter);
774+
if (filter->audio_source)
775+
obs_source_release(filter->audio_source);
776+
if (filter->audio_source_name)
777+
bfree(filter->audio_source_name);
744778

745779
bfree(filter);
746780
}
@@ -2097,6 +2131,52 @@ static bool shader_filter_convert(obs_properties_t *props, obs_property_t *prope
20972131

20982132
static const char *shader_filter_texture_file_filter = "Textures (*.bmp *.tga *.png *.jpeg *.jpg *.gif);;";
20992133

2134+
#define MIN_AUDIO_THRESHOLD -60.0f
2135+
2136+
static float convert_db_to_linear(float db_value) {
2137+
if (db_value <= MIN_AUDIO_THRESHOLD || db_value > 0.0f)
2138+
return 0.0f;
2139+
2140+
return fmaxf(0.0f, fminf(1.0f, (db_value - MIN_AUDIO_THRESHOLD) / (-MIN_AUDIO_THRESHOLD)));
2141+
}
2142+
2143+
static void shader_filter_audio_callback(void *data, const float magnitude[MAX_AUDIO_CHANNELS],
2144+
const float peak[MAX_AUDIO_CHANNELS], const float input_peak[MAX_AUDIO_CHANNELS])
2145+
{
2146+
UNUSED_PARAMETER(input_peak);
2147+
struct shader_filter_data *filter = (struct shader_filter_data *)data;
2148+
2149+
float max_peak = MIN_AUDIO_THRESHOLD;
2150+
for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) {
2151+
if (peak[i] > max_peak && peak[i] != 0.0f) {
2152+
max_peak = peak[i];
2153+
}
2154+
}
2155+
2156+
float max_magnitude = MIN_AUDIO_THRESHOLD;
2157+
for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) {
2158+
if (magnitude[i] > max_magnitude && magnitude[i] != 0.0f) {
2159+
max_magnitude = magnitude[i];
2160+
}
2161+
}
2162+
2163+
filter->current_audio_peak = convert_db_to_linear(max_peak);
2164+
filter->current_audio_magnitude = convert_db_to_linear(max_magnitude);
2165+
}
2166+
2167+
static bool shader_filter_enum_audio_sources(void *data, obs_source_t *source)
2168+
{
2169+
obs_property_t *prop = (obs_property_t *)data;
2170+
uint32_t flags = obs_source_get_output_flags(source);
2171+
2172+
if ((flags & OBS_SOURCE_AUDIO) != 0) {
2173+
const char *name = obs_source_get_name(source);
2174+
obs_property_list_add_string(prop, name, name);
2175+
}
2176+
2177+
return true;
2178+
}
2179+
21002180
static obs_properties_t *shader_filter_properties(void *data)
21012181
{
21022182
struct shader_filter_data *filter = data;
@@ -2114,6 +2194,12 @@ static obs_properties_t *shader_filter_properties(void *data)
21142194
obs_properties_add_int(props, "expand_right", obs_module_text("ShaderFilter.ExpandRight"), 0, 9999, 1);
21152195
obs_properties_add_int(props, "expand_top", obs_module_text("ShaderFilter.ExpandTop"), 0, 9999, 1);
21162196
obs_properties_add_int(props, "expand_bottom", obs_module_text("ShaderFilter.ExpandBottom"), 0, 9999, 1);
2197+
2198+
obs_property_t *audio_source = obs_properties_add_list(props, "audio_source", "Audio source",
2199+
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
2200+
obs_property_list_add_string(audio_source, "None", "");
2201+
2202+
obs_enum_sources(shader_filter_enum_audio_sources, audio_source);
21172203
}
21182204

21192205
obs_properties_add_bool(props, "override_entire_effect", obs_module_text("ShaderFilter.OverrideEntireEffect"));
@@ -2386,6 +2472,48 @@ static void shader_filter_update(void *data, obs_data_t *settings)
23862472
filter->expand_top = (int)obs_data_get_int(settings, "expand_top");
23872473
filter->expand_bottom = (int)obs_data_get_int(settings, "expand_bottom");
23882474
filter->rand_activation_f = (float)((double)rand_interval(0, 10000) / (double)10000);
2475+
2476+
const char *audio_source_name = obs_data_get_string(settings, "audio_source");
2477+
2478+
if (audio_source_name && strlen(audio_source_name) > 0) {
2479+
if (filter->audio_source) {
2480+
obs_source_release(filter->audio_source);
2481+
filter->audio_source = NULL;
2482+
}
2483+
2484+
if (filter->audio_source_name)
2485+
bfree(filter->audio_source_name);
2486+
2487+
filter->audio_source_name = bstrdup(audio_source_name);
2488+
filter->audio_source = obs_get_source_by_name(audio_source_name);
2489+
} else {
2490+
if (filter->audio_source) {
2491+
obs_source_release(filter->audio_source);
2492+
filter->audio_source = NULL;
2493+
}
2494+
2495+
if (filter->audio_source_name) {
2496+
bfree(filter->audio_source_name);
2497+
filter->audio_source_name = NULL;
2498+
}
2499+
}
2500+
2501+
if (filter->volmeter) {
2502+
obs_volmeter_destroy(filter->volmeter);
2503+
filter->volmeter = NULL;
2504+
}
2505+
2506+
if (filter->audio_source) {
2507+
filter->current_audio_peak = 0.0f;
2508+
filter->current_audio_magnitude = 0.0f;
2509+
filter->volmeter = obs_volmeter_create(OBS_FADER_LOG);
2510+
2511+
obs_volmeter_attach_source(filter->volmeter, filter->audio_source);
2512+
obs_volmeter_add_callback(filter->volmeter, shader_filter_audio_callback, filter);
2513+
} else {
2514+
filter->current_audio_peak = 0.0f;
2515+
filter->current_audio_magnitude = 0.0f;
2516+
}
23892517

23902518
if (filter->reload_effect) {
23912519
filter->reload_effect = false;
@@ -2664,6 +2792,14 @@ static void shader_filter_tick(void *data, float seconds)
26642792
// undecided between this and "rand_float(1);"
26652793
filter->rand_f = (float)((double)rand_interval(0, 10000) / (double)10000);
26662794

2795+
if (filter->audio_source && filter->volmeter) {
2796+
filter->audio_peak = filter->current_audio_peak;
2797+
filter->audio_magnitude = filter->current_audio_magnitude;
2798+
} else {
2799+
filter->audio_peak = 0.0f;
2800+
filter->audio_magnitude = 0.0f;
2801+
}
2802+
26672803
filter->output_rendered = false;
26682804
filter->input_rendered = false;
26692805
}
@@ -2832,6 +2968,12 @@ void shader_filter_set_effect_params(struct shader_filter_data *filter)
28322968
if (filter->param_local_time != NULL) {
28332969
gs_effect_set_float(filter->param_local_time, filter->local_time);
28342970
}
2971+
if (filter->param_audio_peak != NULL) {
2972+
gs_effect_set_float(filter->param_audio_peak, filter->audio_peak);
2973+
}
2974+
if (filter->param_audio_magnitude != NULL) {
2975+
gs_effect_set_float(filter->param_audio_magnitude, filter->audio_magnitude);
2976+
}
28352977
if (filter->param_loops != NULL) {
28362978
gs_effect_set_int(filter->param_loops, filter->loops);
28372979
}

0 commit comments

Comments
 (0)