diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 92a3812e4686c5..68cb1024970cab 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -55,6 +55,7 @@ if(OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0) add_obs_plugin(mac-syphon PLATFORMS MACOS) add_obs_plugin(mac-videotoolbox PLATFORMS MACOS) add_obs_plugin(mac-virtualcam PLATFORMS MACOS) + add_obs_plugin(nv-filters PLATFORMS WINDOWS) check_obs_browser() @@ -114,6 +115,7 @@ if(OS_WINDOWS) add_subdirectory(obs-text) add_subdirectory(vlc-video) add_subdirectory(obs-vst) + add_subdirectory(nv-filters) check_obs_browser() elseif(OS_MACOS) diff --git a/plugins/nv-filters/CMakeLists.txt b/plugins/nv-filters/CMakeLists.txt new file mode 100644 index 00000000000000..e74fb755809401 --- /dev/null +++ b/plugins/nv-filters/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.22...3.25) +if(OS_WINDOWS) + add_library(nv-filters MODULE) + add_library(OBS::nv-filters ALIAS nv-filters) + + option(ENABLE_NVAFX "Enable building with NVIDIA Audio Effects SDK (requires redistributable to be installed)" ON) + option(ENABLE_NVVFX "Enable building with NVIDIA Video Effects SDK (requires redistributable to be installed)" ON) + + if(ENABLE_NVAFX) + target_enable_feature(nv-filters "NVIDIA Audio FX support" LIBNVAFX_ENABLED HAS_NOISEREDUCTION) + target_sources(nv-filters PRIVATE nvidia-audiofx-filter.c) + else() + target_disable_feature(nv-filters "NVIDIA Audio FX support") + endif() + + if(ENABLE_NVVFX) + target_enable_feature(nv-filters "NVIDIA Video FX support" LIBNVVFX_ENABLED) + target_sources(nv-filters PRIVATE nvidia-greenscreen-filter.c) + else() + target_disable_feature(nv-filters "NVIDIA Video FX support") + endif() + + configure_file(cmake/windows/obs-module.rc.in nv-filters.rc) + target_sources(nv-filters PRIVATE nv-filters.rc) + target_sources(nv-filters PRIVATE nv-filters.c) + + target_link_libraries(nv-filters PRIVATE OBS::libobs $<$:OBS::w32-pthreads>) + + # cmake-format: off + set_target_properties_obs(nv-filters PROPERTIES FOLDER plugins PREFIX "") + # cmake-format: on +endif() diff --git a/plugins/nv-filters/cmake/windows/obs-module.rc.in b/plugins/nv-filters/cmake/windows/obs-module.rc.in new file mode 100644 index 00000000000000..4081e0a8ce2670 --- /dev/null +++ b/plugins/nv-filters/cmake/windows/obs-module.rc.in @@ -0,0 +1,24 @@ +1 VERSIONINFO +FILEVERSION ${OBS_VERSION_MAJOR},${OBS_VERSION_MINOR},${OBS_VERSION_PATCH},0 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "${OBS_COMPANY_NAME}" + VALUE "FileDescription", "NVIDIA A/V Filters" + VALUE "FileVersion", "${OBS_VERSION_CANONICAL}" + VALUE "ProductName", "${OBS_PRODUCT_NAME}" + VALUE "ProductVersion", "${OBS_VERSION_CANONICAL}" + VALUE "Comments", "${OBS_COMMENTS}" + VALUE "LegalCopyright", "${OBS_LEGAL_COPYRIGHT}" + VALUE "InternalName", "nv-filters" + VALUE "OriginalFilename", "nv-filters" + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04B0 + END +END diff --git a/plugins/nv-filters/data/color.effect b/plugins/nv-filters/data/color.effect new file mode 100644 index 00000000000000..5de529424a6e41 --- /dev/null +++ b/plugins/nv-filters/data/color.effect @@ -0,0 +1,95 @@ +float srgb_linear_to_nonlinear_channel(float u) +{ + return (u <= 0.0031308) ? (12.92 * u) : ((1.055 * pow(u, 1. / 2.4)) - 0.055); +} + +float3 srgb_linear_to_nonlinear(float3 v) +{ + return float3(srgb_linear_to_nonlinear_channel(v.r), srgb_linear_to_nonlinear_channel(v.g), srgb_linear_to_nonlinear_channel(v.b)); +} + +float srgb_nonlinear_to_linear_channel(float u) +{ + return (u <= 0.04045) ? (u / 12.92) : pow((u + 0.055) / 1.055, 2.4); +} + +float3 srgb_nonlinear_to_linear(float3 v) +{ + return float3(srgb_nonlinear_to_linear_channel(v.r), srgb_nonlinear_to_linear_channel(v.g), srgb_nonlinear_to_linear_channel(v.b)); +} + +float3 rec709_to_rec2020(float3 v) +{ + float r = dot(v, float3(0.62740389593469903, 0.32928303837788370, 0.043313065687417225)); + float g = dot(v, float3(0.069097289358232075, 0.91954039507545871, 0.011362315566309178)); + float b = dot(v, float3(0.016391438875150280, 0.088013307877225749, 0.89559525324762401)); + return float3(r, g, b); +} + +float3 rec2020_to_rec709(float3 v) +{ + float r = dot(v, float3(1.6604910021084345, -0.58764113878854951, -0.072849863319884883)); + float g = dot(v, float3(-0.12455047452159074, 1.1328998971259603, -0.0083494226043694768)); + float b = dot(v, float3(-0.018150763354905303, -0.10057889800800739, 1.1187296613629127)); + return float3(r, g, b); +} + +float3 reinhard(float3 rgb) +{ + rgb /= rgb + float3(1., 1., 1.); + rgb = saturate(rgb); + rgb = pow(rgb, float3(1. / 2.4, 1. / 2.4, 1. / 2.4)); + rgb = srgb_nonlinear_to_linear(rgb); + return rgb; +} + +float linear_to_st2084_channel(float x) +{ + float c = pow(abs(x), 0.1593017578); + return pow((0.8359375 + 18.8515625 * c) / (1. + 18.6875 * c), 78.84375); +} + +float st2084_to_linear_channel(float u) +{ + float c = pow(abs(u), 1. / 78.84375); + return pow(abs(max(c - 0.8359375, 0.) / (18.8515625 - 18.6875 * c)), 1. / 0.1593017578); +} + +float eetf_0_Lmax(float maxRGB1_pq, float Lw, float Lmax) +{ + float Lw_pq = linear_to_st2084_channel(Lw / 10000.); + float E1 = saturate(maxRGB1_pq / Lw_pq); // Ensure normalization in case Lw is a lie + float maxLum = linear_to_st2084_channel(Lmax / 10000.) / Lw_pq; + float KS = (1.5 * maxLum) - 0.5; + float E2 = E1; + if (E1 > KS) + { + float T = (E1 - KS) / (1. - KS); + float Tsquared = T * T; + float Tcubed = Tsquared * T; + float P = (2. * Tcubed - 3. * Tsquared + 1.) * KS + (Tcubed - 2. * Tsquared + T) * (1. - KS) + (-2. * Tcubed + 3. * Tsquared) * maxLum; + E2 = P; + } + float E3 = E2; + float E4 = E3 * Lw_pq; + return E4; +} + +float3 maxRGB_eetf_internal(float3 rgb_linear, float maxRGB1_linear, float maxRGB1_pq, float Lw, float Lmax) +{ + float maxRGB2_pq = eetf_0_Lmax(maxRGB1_pq, Lw, Lmax); + float maxRGB2_linear = st2084_to_linear_channel(maxRGB2_pq); + + // avoid divide-by-zero possibility + maxRGB1_linear = max(6.10352e-5, maxRGB1_linear); + + rgb_linear *= maxRGB2_linear / maxRGB1_linear; + return rgb_linear; +} + +float3 maxRGB_eetf_linear_to_linear(float3 rgb_linear, float Lw, float Lmax) +{ + float maxRGB1_linear = max(max(rgb_linear.r, rgb_linear.g), rgb_linear.b); + float maxRGB1_pq = linear_to_st2084_channel(maxRGB1_linear); + return maxRGB_eetf_internal(rgb_linear, maxRGB1_linear, maxRGB1_pq, Lw, Lmax); +} diff --git a/plugins/nv-filters/data/locale/en-US.ini b/plugins/nv-filters/data/locale/en-US.ini new file mode 100644 index 00000000000000..e1da7f429e06ab --- /dev/null +++ b/plugins/nv-filters/data/locale/en-US.ini @@ -0,0 +1,16 @@ +Nvafx="NVIDIA Audio Effects" +Nvafx.SuppressLevel="Suppression Level" +Nvafx.Intensity="Suppression Intensity" +Nvafx.Method="Method" +Nvafx.Method.Denoiser="NVIDIA Noise Removal" +Nvafx.Method.Dereverb="NVIDIA Room Echo Removal" +Nvafx.Method.DenoiserPlusDereverb="NVIDIA Noise Removal + Room Echo Removal" +Nvafx.OutdatedSDK="WARNING: Please upgrade both NVIDIA Video & Audio SDK. Your current version of Audio SDK is outdated." +Nvvfx.Method.Greenscreen="NVIDIA Background Removal" +Nvvfx.Method.Greenscreen.Mode="Mode" +Nvvfx.Method.Greenscreen.Quality="Quality (higher GPU usage, better quality)" +Nvvfx.Method.Greenscreen.Performance="Performance (lower GPU usage, good quality)" +Nvvfx.Method.Greenscreen.Threshold="Threshold" +Nvvfx.OutdatedSDK="WARNING: Please upgrade both NVIDIA Video & Audio SDK. Your current version of Video SDK is outdated." +Nvvfx.Method.Greenscreen.Processing="Mask refresh frequency in frames" +Nvvfx.Method.Greenscreen.Processing.Hint="This alleviates GPU load by generating a mask every N frames only (2 on default)." diff --git a/plugins/nv-filters/data/rtx_greenscreen.effect b/plugins/nv-filters/data/rtx_greenscreen.effect new file mode 100644 index 00000000000000..fc945d2327f4b4 --- /dev/null +++ b/plugins/nv-filters/data/rtx_greenscreen.effect @@ -0,0 +1,183 @@ +#include "color.effect" + +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float multiplier; + +uniform texture2d mask; +uniform float threshold; + +sampler_state texSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +struct VertInOut { + float2 uv : TEXCOORD0; + float4 pos : POSITION; +}; + +struct FragData { + float2 uv : TEXCOORD0; +}; + +struct FragPos { + float4 pos : POSITION; +}; + +VertInOut VSDefault(VertData v_in) +{ + VertInOut v_out; + v_out.uv = v_in.uv; + v_out.pos = mul(float4(v_in.pos.xyz, 1.), ViewProj); + return v_out; +} + +FragPos VSConvertUnorm(uint id : VERTEXID) +{ + float idHigh = float(id >> 1); + float idLow = float(id & uint(1)); + + float x = idHigh * 4.0 - 1.0; + float y = idLow * 4.0 - 1.0; + + FragPos vert_out; + vert_out.pos = float4(x, y, 0.0, 1.0); + return vert_out; +} + +float4 Mask(FragData f_in) +{ + float4 rgba = image.Sample(texSampler, f_in.uv); + rgba *= smoothstep(threshold - 0.1,threshold,mask.Sample(texSampler, f_in.uv).a); + return rgba; +} + +float4 PSMask(FragData f_in) : TARGET +{ + float4 rgba = Mask(f_in); + return rgba; +} + +float4 PSMaskMultiply(FragData f_in) : TARGET +{ + float4 rgba = Mask(f_in); + rgba.rgb *= multiplier; + return rgba; +} + +float4 PSMaskTonemap(FragData f_in) : TARGET +{ + float4 rgba = Mask(f_in); + rgba.rgb = rec709_to_rec2020(rgba.rgb); + rgba.rgb = reinhard(rgba.rgb); + rgba.rgb = rec2020_to_rec709(rgba.rgb); + return rgba; +} + +float4 PSMaskMultiplyTonemap(FragData f_in) : TARGET +{ + float4 rgba = Mask(f_in); + rgba.rgb *= multiplier; + rgba.rgb = rec709_to_rec2020(rgba.rgb); + rgba.rgb = reinhard(rgba.rgb); + rgba.rgb = rec2020_to_rec709(rgba.rgb); + return rgba; +} + +float4 PSConvertUnorm(FragPos f_in) : TARGET +{ + float4 rgba = image.Load(int3(f_in.pos.xy, 0)); + rgba.rgb = srgb_linear_to_nonlinear(rgba.rgb); + return rgba; +} + +float4 PSConvertUnormTonemap(FragPos f_in) : TARGET +{ + float4 rgba = image.Load(int3(f_in.pos.xy, 0)); + rgba.rgb = rec709_to_rec2020(rgba.rgb); + rgba.rgb = reinhard(rgba.rgb); + rgba.rgb = rec2020_to_rec709(rgba.rgb); + rgba.rgb = srgb_linear_to_nonlinear(rgba.rgb); + return rgba; +} + +float4 PSConvertUnormMultiplyTonemap(FragPos f_in) : TARGET +{ + float4 rgba = image.Load(int3(f_in.pos.xy, 0)); + rgba.rgb *= multiplier; + rgba.rgb = rec709_to_rec2020(rgba.rgb); + rgba.rgb = reinhard(rgba.rgb); + rgba.rgb = rec2020_to_rec709(rgba.rgb); + rgba.rgb = srgb_linear_to_nonlinear(rgba.rgb); + return rgba; +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSMask(f_in); + } +} + +technique DrawMultiply +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSMaskMultiply(f_in); + } +} + +technique DrawTonemap +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSMaskTonemap(f_in); + } +} + +technique DrawMultiplyTonemap +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSMaskMultiplyTonemap(f_in); + } +} + +technique ConvertUnorm +{ + pass + { + vertex_shader = VSConvertUnorm(id); + pixel_shader = PSConvertUnorm(f_in); + } +} + +technique ConvertUnormTonemap +{ + pass + { + vertex_shader = VSConvertUnorm(id); + pixel_shader = PSConvertUnormTonemap(f_in); + } +} + +technique ConvertUnormMultiplyTonemap +{ + pass + { + vertex_shader = VSConvertUnorm(id); + pixel_shader = PSConvertUnormMultiplyTonemap(f_in); + } +} diff --git a/plugins/nv-filters/nv-filters.c b/plugins/nv-filters/nv-filters.c new file mode 100644 index 00000000000000..a9f590357bab13 --- /dev/null +++ b/plugins/nv-filters/nv-filters.c @@ -0,0 +1,46 @@ +#include + +OBS_DECLARE_MODULE() +OBS_MODULE_USE_DEFAULT_LOCALE("nv-filters", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "NVIDIA filters"; +} + +#ifdef LIBNVAFX_ENABLED +extern struct obs_source_info nvidia_audiofx_filter; +extern bool load_nvidia_afx(void); +extern void unload_nvidia_afx(void); +#endif +#ifdef LIBNVVFX_ENABLED +extern struct obs_source_info nvidia_greenscreen_filter_info; +extern bool load_nvidia_vfx(void); +extern void unload_nvidia_vfx(void); +#endif + +bool obs_module_load(void) +{ +#ifdef LIBNVAFX_ENABLED + /* load nvidia audio fx dll */ + if (load_nvidia_afx()) + obs_register_source(&nvidia_audiofx_filter); +#endif +#ifdef LIBNVVFX_ENABLED + obs_enter_graphics(); + const bool direct3d = gs_get_device_type() == GS_DEVICE_DIRECT3D_11; + obs_leave_graphics(); + if (direct3d && load_nvidia_vfx()) + obs_register_source(&nvidia_greenscreen_filter_info); +#endif + return true; +} + +void obs_module_unload(void) +{ +#ifdef LIBNVAFX_ENABLED + unload_nvidia_afx(); +#endif +#ifdef LIBNVVFX_ENABLED + unload_nvidia_vfx(); +#endif +} diff --git a/plugins/nv-filters/nv_sdk_versions.h b/plugins/nv-filters/nv_sdk_versions.h new file mode 100644 index 00000000000000..bf93deaebbd9e8 --- /dev/null +++ b/plugins/nv-filters/nv_sdk_versions.h @@ -0,0 +1,2 @@ +#define MIN_VFX_SDK_VERSION (0 << 24 | 7 << 16 | 2 << 8 | 0 << 0) +#define MIN_AFX_SDK_VERSION (1 << 24 | 3 << 16 | 0 << 0) diff --git a/plugins/nv-filters/nvafx-load.h b/plugins/nv-filters/nvafx-load.h new file mode 100644 index 00000000000000..8cd4eecdabb63e --- /dev/null +++ b/plugins/nv-filters/nvafx-load.h @@ -0,0 +1,339 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include "nv_sdk_versions.h" + +#define NVAFX_API + +#ifdef LIBNVAFX_ENABLED +static HMODULE nv_audiofx = NULL; +static HMODULE nv_cuda = NULL; + +/** Effects @ref NvAFX_EffectSelector */ +#define NVAFX_EFFECT_DENOISER "denoiser" +#define NVAFX_EFFECT_DEREVERB "dereverb" +#define NVAFX_EFFECT_DEREVERB_DENOISER "dereverb_denoiser" +#define NVAFX_EFFECT_AEC "aec" +#define NVAFX_EFFECT_SUPERRES "superres" + +/** Model paths */ +#define NVAFX_EFFECT_DENOISER_MODEL "\\models\\denoiser_48k.trtpkg" +#define NVAFX_EFFECT_DEREVERB_MODEL "\\models\\dereverb_48k.trtpkg" +#define NVAFX_EFFECT_DEREVERB_DENOISER_MODEL \ + "\\models\\dereverb_denoiser_48k.trtpkg" + +#define NVAFX_CHAINED_EFFECT_DENOISER_16k_SUPERRES_16k_TO_48k \ + "denoiser16k_superres16kto48k" +#define NVAFX_CHAINED_EFFECT_DEREVERB_16k_SUPERRES_16k_TO_48k \ + "dereverb16k_superres16kto48k" +#define NVAFX_CHAINED_EFFECT_DEREVERB_DENOISER_16k_SUPERRES_16k_TO_48k \ + "dereverb_denoiser16k_superres16kto48k" +#define NVAFX_CHAINED_EFFECT_SUPERRES_8k_TO_16k_DENOISER_16k \ + "superres8kto16k_denoiser16k" +#define NVAFX_CHAINED_EFFECT_SUPERRES_8k_TO_16k_DEREVERB_16k \ + "superres8kto16k_dereverb16k" +#define NVAFX_CHAINED_EFFECT_SUPERRES_8k_TO_16k_DEREVERB_DENOISER_16k \ + "superres8kto16k_dereverb_denoiser16k" + +/** Parameter selectors */ + +#define NVAFX_PARAM_NUM_STREAMS "num_streams" +#define NVAFX_PARAM_USE_DEFAULT_GPU "use_default_gpu" +#define NVAFX_PARAM_USER_CUDA_CONTEXT "user_cuda_context" +#define NVAFX_PARAM_DISABLE_CUDA_GRAPH "disable_cuda_graph" +#define NVAFX_PARAM_ENABLE_VAD "enable_vad" +/** Effect parameters. @ref NvAFX_ParameterSelector */ +#define NVAFX_PARAM_MODEL_PATH "model_path" +#define NVAFX_PARAM_INPUT_SAMPLE_RATE "input_sample_rate" +#define NVAFX_PARAM_OUTPUT_SAMPLE_RATE "output_sample_rate" +#define NVAFX_PARAM_NUM_INPUT_SAMPLES_PER_FRAME "num_input_samples_per_frame" +#define NVAFX_PARAM_NUM_OUTPUT_SAMPLES_PER_FRAME "num_output_samples_per_frame" +#define NVAFX_PARAM_NUM_INPUT_CHANNELS "num_input_channels" +#define NVAFX_PARAM_NUM_OUTPUT_CHANNELS "num_output_channels" +#define NVAFX_PARAM_INTENSITY_RATIO "intensity_ratio" + +#pragma deprecated(NVAFX_PARAM_DENOISER_MODEL_PATH) +#define NVAFX_PARAM_DENOISER_MODEL_PATH NVAFX_PARAM_MODEL_PATH +#pragma deprecated(NVAFX_PARAM_DENOISER_SAMPLE_RATE) +#define NVAFX_PARAM_DENOISER_SAMPLE_RATE NVAFX_PARAM_SAMPLE_RATE +#pragma deprecated(NVAFX_PARAM_DENOISER_NUM_SAMPLES_PER_FRAME) +#define NVAFX_PARAM_DENOISER_NUM_SAMPLES_PER_FRAME \ + NVAFX_PARAM_NUM_SAMPLES_PER_FRAME +#pragma deprecated(NVAFX_PARAM_DENOISER_NUM_CHANNELS) +#define NVAFX_PARAM_DENOISER_NUM_CHANNELS NVAFX_PARAM_NUM_CHANNELS +#pragma deprecated(NVAFX_PARAM_DENOISER_INTENSITY_RATIO) +#define NVAFX_PARAM_DENOISER_INTENSITY_RATIO NVAFX_PARAM_INTENSITY_RATIO +/** Number of audio channels **/ +#pragma deprecated(NVAFX_PARAM_NUM_CHANNELS) +#define NVAFX_PARAM_NUM_CHANNELS "num_channels" +/** Sample rate (unsigned int). Currently supported sample rate(s): 48000, 16000 */ +#pragma deprecated(NVAFX_PARAM_SAMPLE_RATE) +#define NVAFX_PARAM_SAMPLE_RATE "sample_rate" +/** Number of samples per frame (unsigned int). This is immutable parameter */ +#pragma deprecated(NVAFX_PARAM_NUM_SAMPLES_PER_FRAME) +#define NVAFX_PARAM_NUM_SAMPLES_PER_FRAME "num_samples_per_frame" + +typedef enum { + /** Success */ + NVAFX_STATUS_SUCCESS = 0, + /** Failure */ + NVAFX_STATUS_FAILED = 1, + /** Handle invalid */ + NVAFX_STATUS_INVALID_HANDLE = 2, + /** Parameter value invalid */ + NVAFX_STATUS_INVALID_PARAM = 3, + /** Parameter value immutable */ + NVAFX_STATUS_IMMUTABLE_PARAM = 4, + /** Insufficient data to process */ + NVAFX_STATUS_INSUFFICIENT_DATA = 5, + /** Effect not supported */ + NVAFX_STATUS_EFFECT_NOT_AVAILABLE = 6, + /** Given buffer length too small to hold requested data */ + NVAFX_STATUS_OUTPUT_BUFFER_TOO_SMALL = 7, + /** Model file could not be loaded */ + NVAFX_STATUS_MODEL_LOAD_FAILED = 8, + + /** (32 bit SDK only) COM server was not registered, please see user manual for details */ + NVAFX_STATUS_32_SERVER_NOT_REGISTERED = 9, + /** (32 bit SDK only) COM operation failed */ + NVAFX_STATUS_32_COM_ERROR = 10, + /** GPU supported. The SDK requires Turing and above GPU with Tensor cores */ + NVAFX_STATUS_GPU_UNSUPPORTED = 11, +} NvAFX_Status; + +#define NVAFX_TRUE 1 +#define NVAFX_FALSE 0 +typedef char NvAFX_Bool; + +typedef const char *NvAFX_EffectSelector; +typedef const char *NvAFX_ParameterSelector; +typedef void *NvAFX_Handle; + +typedef NvAFX_Status + NVAFX_API (*NvAFX_GetEffectList_t)(int *num_effects, + NvAFX_EffectSelector *effects[]); +typedef NvAFX_Status + NVAFX_API (*NvAFX_CreateEffect_t)(NvAFX_EffectSelector code, + NvAFX_Handle *effect); +typedef NvAFX_Status + NVAFX_API (*NvAFX_CreateChainedEffect_t)(NvAFX_EffectSelector code, + NvAFX_Handle *effect); +typedef NvAFX_Status NVAFX_API (*NvAFX_DestroyEffect_t)(NvAFX_Handle effect); +typedef NvAFX_Status + NVAFX_API (*NvAFX_SetU32_t)(NvAFX_Handle effect, + NvAFX_ParameterSelector param_name, + unsigned int val); +typedef NvAFX_Status + NVAFX_API (*NvAFX_SetU32List_t)(NvAFX_Handle effect, + NvAFX_ParameterSelector param_name, + unsigned int *val, unsigned int size); +typedef NvAFX_Status + NVAFX_API (*NvAFX_SetString_t)(NvAFX_Handle effect, + NvAFX_ParameterSelector param_name, + const char *val); +typedef NvAFX_Status + NVAFX_API (*NvAFX_SetStringList_t)(NvAFX_Handle effect, + NvAFX_ParameterSelector param_name, + const char **val, unsigned int size); +typedef NvAFX_Status NVAFX_API (*NvAFX_SetFloat_t)( + NvAFX_Handle effect, NvAFX_ParameterSelector param_name, float val); +typedef NvAFX_Status + NVAFX_API (*NvAFX_SetFloatList_t)(NvAFX_Handle effect, + NvAFX_ParameterSelector param_name, + float *val, unsigned int size); +typedef NvAFX_Status + NVAFX_API (*NvAFX_GetU32_t)(NvAFX_Handle effect, + NvAFX_ParameterSelector param_name, + unsigned int *val); +typedef NvAFX_Status + NVAFX_API (*NvAFX_GetString_t)(NvAFX_Handle effect, + NvAFX_ParameterSelector param_name, + char *val, int max_length); +typedef NvAFX_Status NVAFX_API (*NvAFX_GetStringList_t)( + NvAFX_Handle effect, NvAFX_ParameterSelector param_name, char **val, + int *max_length, unsigned int size); +typedef NvAFX_Status NVAFX_API (*NvAFX_GetFloat_t)( + NvAFX_Handle effect, NvAFX_ParameterSelector param_name, float *val); +typedef NvAFX_Status + NVAFX_API (*NvAFX_GetFloatList_t)(NvAFX_Handle effect, + NvAFX_ParameterSelector param_name, + float *val, unsigned int size); +typedef NvAFX_Status NVAFX_API (*NvAFX_Load_t)(NvAFX_Handle effect); +typedef NvAFX_Status + NVAFX_API (*NvAFX_GetSupportedDevices_t)(NvAFX_Handle effect, int *num, + int *devices); +typedef NvAFX_Status NVAFX_API (*NvAFX_Run_t)(NvAFX_Handle effect, + const float **input, + float **output, + unsigned num_samples, + unsigned num_channels); +typedef NvAFX_Status NVAFX_API (*NvAFX_Reset_t)(NvAFX_Handle effect); + +/* cuda */ +typedef enum cudaError_enum { + CUDA_SUCCESS = 0, + CUDA_ERROR_INVALID_VALUE = 1, + CUDA_ERROR_OUT_OF_MEMORY = 2, + CUDA_ERROR_NOT_INITIALIZED = 3, + CUDA_ERROR_DEINITIALIZED = 4, + CUDA_ERROR_PROFILER_DISABLED = 5, + CUDA_ERROR_PROFILER_NOT_INITIALIZED = 6, + CUDA_ERROR_PROFILER_ALREADY_STARTED = 7, + CUDA_ERROR_PROFILER_ALREADY_STOPPED = 8, + CUDA_ERROR_NO_DEVICE = 100, + CUDA_ERROR_INVALID_DEVICE = 101, + CUDA_ERROR_INVALID_IMAGE = 200, + CUDA_ERROR_INVALID_CONTEXT = 201, + CUDA_ERROR_CONTEXT_ALREADY_CURRENT = 202, + CUDA_ERROR_MAP_FAILED = 205, + CUDA_ERROR_UNMAP_FAILED = 206, + CUDA_ERROR_ARRAY_IS_MAPPED = 207, + CUDA_ERROR_ALREADY_MAPPED = 208, + CUDA_ERROR_NO_BINARY_FOR_GPU = 209, + CUDA_ERROR_ALREADY_ACQUIRED = 210, + CUDA_ERROR_NOT_MAPPED = 211, + CUDA_ERROR_NOT_MAPPED_AS_ARRAY = 212, + CUDA_ERROR_NOT_MAPPED_AS_POINTER = 213, + CUDA_ERROR_ECC_UNCORRECTABLE = 214, + CUDA_ERROR_UNSUPPORTED_LIMIT = 215, + CUDA_ERROR_CONTEXT_ALREADY_IN_USE = 216, + CUDA_ERROR_INVALID_SOURCE = 300, + CUDA_ERROR_FILE_NOT_FOUND = 301, + CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND = 302, + CUDA_ERROR_SHARED_OBJECT_INIT_FAILED = 303, + CUDA_ERROR_OPERATING_SYSTEM = 304, + CUDA_ERROR_INVALID_HANDLE = 400, + CUDA_ERROR_NOT_FOUND = 500, + CUDA_ERROR_NOT_READY = 600, + CUDA_ERROR_LAUNCH_FAILED = 700, + CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES = 701, + CUDA_ERROR_LAUNCH_TIMEOUT = 702, + CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING = 703, + CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED = 704, + CUDA_ERROR_PEER_ACCESS_NOT_ENABLED = 705, + CUDA_ERROR_PRIMARY_CONTEXT_ACTIVE = 708, + CUDA_ERROR_CONTEXT_IS_DESTROYED = 709, + CUDA_ERROR_ASSERT = 710, + CUDA_ERROR_TOO_MANY_PEERS = 711, + CUDA_ERROR_HOST_MEMORY_ALREADY_REGISTERED = 712, + CUDA_ERROR_HOST_MEMORY_NOT_REGISTERED = 713, + CUDA_ERROR_UNKNOWN = 999 +} CUresult; +typedef struct CUctx_st *CUcontext; +typedef CUresult(__stdcall *cuCtxGetCurrent_t)(CUcontext *pctx); +typedef CUresult(__stdcall *cuCtxPopCurrent_t)(CUcontext *pctx); +typedef CUresult(__stdcall *cuInit_t)(unsigned int Flags); + +static NvAFX_GetEffectList_t NvAFX_GetEffectList = NULL; +static NvAFX_CreateEffect_t NvAFX_CreateEffect = NULL; +static NvAFX_CreateChainedEffect_t NvAFX_CreateChainedEffect = NULL; +static NvAFX_DestroyEffect_t NvAFX_DestroyEffect = NULL; +static NvAFX_SetU32_t NvAFX_SetU32 = NULL; +static NvAFX_SetU32List_t NvAFX_SetU32List = NULL; +static NvAFX_SetString_t NvAFX_SetString = NULL; +static NvAFX_SetStringList_t NvAFX_SetStringList = NULL; +static NvAFX_SetFloat_t NvAFX_SetFloat = NULL; +static NvAFX_SetFloatList_t NvAFX_SetFloatList = NULL; +static NvAFX_GetU32_t NvAFX_GetU32 = NULL; +static NvAFX_GetString_t NvAFX_GetString = NULL; +static NvAFX_GetStringList_t NvAFX_GetStringList = NULL; +static NvAFX_GetFloat_t NvAFX_GetFloat = NULL; +static NvAFX_GetFloatList_t NvAFX_GetFloatList = NULL; +static NvAFX_Load_t NvAFX_Load = NULL; +static NvAFX_GetSupportedDevices_t NvAFX_GetSupportedDevices = NULL; +static NvAFX_Run_t NvAFX_Run = NULL; +static NvAFX_Reset_t NvAFX_Reset; +/* cuda */ +static cuCtxGetCurrent_t cuCtxGetCurrent = NULL; +static cuCtxPopCurrent_t cuCtxPopCurrent = NULL; +static cuInit_t cuInit = NULL; + +void release_lib(void) +{ + NvAFX_GetEffectList = NULL; + NvAFX_CreateEffect = NULL; + NvAFX_CreateChainedEffect = NULL; + NvAFX_DestroyEffect = NULL; + NvAFX_SetU32 = NULL; + NvAFX_SetU32List = NULL; + NvAFX_SetString = NULL; + NvAFX_SetStringList = NULL; + NvAFX_SetFloat = NULL; + NvAFX_SetFloatList = NULL; + NvAFX_GetU32 = NULL; + NvAFX_GetString = NULL; + NvAFX_GetStringList = NULL; + NvAFX_GetFloat = NULL; + NvAFX_GetFloatList = NULL; + NvAFX_Load = NULL; + NvAFX_GetSupportedDevices = NULL; + NvAFX_Run = NULL; + NvAFX_Reset = NULL; + if (nv_audiofx) { + FreeLibrary(nv_audiofx); + nv_audiofx = NULL; + } + cuCtxGetCurrent = NULL; + cuCtxPopCurrent = NULL; + cuInit = NULL; + if (nv_cuda) { + FreeLibrary(nv_cuda); + nv_cuda = NULL; + } +} + +static bool nvafx_get_sdk_path(char *buffer, const size_t len) +{ + DWORD ret = + GetEnvironmentVariableA("NVAFX_SDK_DIR", buffer, (DWORD)len); + + if (!ret || ret >= len - 1) + return false; + + return true; +} + +static bool load_lib(void) +{ + char path[MAX_PATH]; + if (!nvafx_get_sdk_path(path, sizeof(path))) + return false; + + SetDllDirectoryA(path); + nv_audiofx = LoadLibrary(L"NVAudioEffects.dll"); + SetDllDirectoryA(NULL); + nv_cuda = LoadLibrary(L"nvcuda.dll"); + return !!nv_audiofx && !!nv_cuda; +} + +static unsigned int get_lib_version(void) +{ + static unsigned int version = 0; + static bool version_checked = false; + + if (version_checked) + return version; + + version_checked = true; + + char path[MAX_PATH]; + if (!nvafx_get_sdk_path(path, sizeof(path))) + return 0; + + SetDllDirectoryA(path); + + struct win_version_info nto_ver = {0}; + if (get_dll_ver(L"NVAudioEffects.dll", &nto_ver)) + version = nto_ver.major << 24 | nto_ver.minor << 16 | + nto_ver.build << 8 | nto_ver.revis << 0; + + SetDllDirectoryA(NULL); + return version; +} + +#endif diff --git a/plugins/nv-filters/nvidia-audiofx-filter.c b/plugins/nv-filters/nvidia-audiofx-filter.c new file mode 100644 index 00000000000000..c63e687f612e1f --- /dev/null +++ b/plugins/nv-filters/nvidia-audiofx-filter.c @@ -0,0 +1,936 @@ +#include +#include +#include +#include +#include +#include +#include "nvafx-load.h" +#include + +/* -------------------------------------------------------- */ +#define do_log(level, format, ...) \ + blog(level, "[NVIDIA Audio Effects: '%s'] " format, \ + obs_source_get_name(ng->context), ##__VA_ARGS__) + +#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) +#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__) + +#ifdef _DEBUG +#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) +#else +#define debug(format, ...) +#endif +/* -------------------------------------------------------- */ + +#define S_NVAFX_INTENSITY "intensity" +#define S_METHOD "method" +#define S_METHOD_NVAFX_DENOISER NVAFX_EFFECT_DENOISER +#define S_METHOD_NVAFX_DEREVERB NVAFX_EFFECT_DEREVERB +#define S_METHOD_NVAFX_DEREVERB_DENOISER NVAFX_EFFECT_DEREVERB_DENOISER + +#define MT_ obs_module_text +#define TEXT_NVAFX_INTENSITY MT_("Nvafx.Intensity") +#define TEXT_METHOD MT_("Nvafx.Method") +#define TEXT_METHOD_NVAFX_DENOISER MT_("Nvafx.Method.Denoiser") +#define TEXT_METHOD_NVAFX_DEREVERB MT_("Nvafx.Method.Dereverb") +#define TEXT_METHOD_NVAFX_DEREVERB_DENOISER \ + MT_("Nvafx.Method.DenoiserPlusDereverb") +#define TEXT_METHOD_NVAFX_DEPRECATION MT_("Nvafx.OutdatedSDK") + +#define MAX_PREPROC_CHANNELS 8 +#define BUFFER_SIZE_MSEC 10 + +/* NVAFX constants, these can't be changed */ +#define NVAFX_SAMPLE_RATE 48000 +/* The SDK does not explicitly set this as a constant though it relies on it.*/ +#define NVAFX_FRAME_SIZE 480 + +#ifdef _MSC_VER +#define ssize_t intptr_t +#endif + +struct nvidia_audio_data { + obs_source_t *context; + + uint64_t last_timestamp; + uint64_t latency; + + size_t frames; + size_t channels; + + struct deque info_buffer; + struct deque input_buffers[MAX_PREPROC_CHANNELS]; + struct deque output_buffers[MAX_PREPROC_CHANNELS]; + + /* This bool is quite important but it is easy to get lost. So let's + * explain how it's used. One big issue is that the NVIDIA FX takes + * ages to load an FX; so its initialization is deferred to a separate + * thread. + * First stage (creation): + * - use_nvafx is set to true at creation of the filter, IF the SDK dir + * path is set. + * - if initialization fails, the bool is set to false & the filter is + * destroyed. + * Later stages (running or updating of the FX): + * - they are executed ONLY if initialization was successful; + * - if at any step, there's an FX failure, the bool is updated to false + * & the filter is destroyed. + */ + bool use_nvafx; + + /* this tracks if the SDK is found */ + bool nvidia_sdk_dir_found; + + bool has_mono_src; + volatile bool reinit_done; + + /* NVAFX handle, one per audio channel */ + NvAFX_Handle handle[MAX_PREPROC_CHANNELS]; + + uint32_t sample_rate; + float intensity_ratio; + unsigned int num_samples_per_frame, num_channels; + char *model; + bool nvafx_initialized; + const char *fx; + char *sdk_path; + + /* Resampler */ + audio_resampler_t *nvafx_resampler; + audio_resampler_t *nvafx_resampler_back; + + /* We load the DLL in a separate thread because its loading is very + * long and unnecessarily blocks OBS initial loading. + * This bool is true once the thread which side loads the FX DLL is started */ + bool nvafx_loading; + pthread_t nvafx_thread; + pthread_mutex_t nvafx_mutex; + + /* PCM buffers */ + float *copy_buffers[MAX_PREPROC_CHANNELS]; + float *nvafx_segment_buffers[MAX_PREPROC_CHANNELS]; + + /* output data */ + struct obs_audio_data output_audio; + DARRAY(float) output_data; +}; + +static const char *nvidia_audio_name(void *unused) +{ + UNUSED_PARAMETER(unused); + return obs_module_text("Nvafx"); +} + +static void nvidia_audio_destroy(void *data) +{ + struct nvidia_audio_data *ng = data; + + if (ng->nvidia_sdk_dir_found) + pthread_mutex_lock(&ng->nvafx_mutex); + + for (size_t i = 0; i < ng->channels; i++) { + if (ng->handle[0]) { + if (NvAFX_DestroyEffect) { + NvAFX_Status err = + NvAFX_DestroyEffect(ng->handle[i]); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "NvAFX_Release() failed"); + } + } + } + deque_free(&ng->input_buffers[i]); + deque_free(&ng->output_buffers[i]); + } + + bfree(ng->nvafx_segment_buffers[0]); + + if (ng->nvafx_resampler) { + audio_resampler_destroy(ng->nvafx_resampler); + audio_resampler_destroy(ng->nvafx_resampler_back); + } + bfree(ng->model); + bfree(ng->sdk_path); + bfree((void *)ng->fx); + if (ng->nvidia_sdk_dir_found) { + pthread_join(ng->nvafx_thread, NULL); + pthread_mutex_unlock(&ng->nvafx_mutex); + pthread_mutex_destroy(&ng->nvafx_mutex); + } + + bfree(ng->copy_buffers[0]); + deque_free(&ng->info_buffer); + da_free(ng->output_data); + bfree(ng); +} + +bool nvidia_afx_initializer_mutex_initialized; +pthread_mutex_t nvidia_afx_initializer_mutex; +bool nvidia_afx_loaded = false; +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4706) +#endif +void release_afxlib(void) +{ + NvAFX_GetEffectList = NULL; + NvAFX_CreateEffect = NULL; + NvAFX_CreateChainedEffect = NULL; + NvAFX_DestroyEffect = NULL; + NvAFX_SetU32 = NULL; + NvAFX_SetU32List = NULL; + NvAFX_SetString = NULL; + NvAFX_SetStringList = NULL; + NvAFX_SetFloat = NULL; + NvAFX_SetFloatList = NULL; + NvAFX_GetU32 = NULL; + NvAFX_GetString = NULL; + NvAFX_GetStringList = NULL; + NvAFX_GetFloat = NULL; + NvAFX_GetFloatList = NULL; + NvAFX_Load = NULL; + NvAFX_GetSupportedDevices = NULL; + NvAFX_Run = NULL; + NvAFX_Reset = NULL; + if (nv_audiofx) { + FreeLibrary(nv_audiofx); + nv_audiofx = NULL; + } + cuCtxGetCurrent = NULL; + cuCtxPopCurrent = NULL; + cuInit = NULL; + if (nv_cuda) { + FreeLibrary(nv_cuda); + nv_cuda = NULL; + } +} + +bool load_nvidia_afx(void) +{ + unsigned int version = get_lib_version(); + uint8_t major = (version >> 24) & 0xff; + uint8_t minor = (version >> 16) & 0x00ff; + uint8_t build = (version >> 8) & 0x0000ff; + uint8_t revision = (version >> 0) & 0x000000ff; + if (version) { + blog(LOG_INFO, "[NVIDIA Audio Effects:] version: %i.%i.%i.%i", + major, minor, build, revision); + if (version < MIN_AFX_SDK_VERSION) { + blog(LOG_INFO, + "[NVIDIA Audio Effects:]: SDK is outdated. Please update both audio & video SDK.\nRequired SDK versions, audio: %i.%i.%i; video: %i.%i.%i", + (MIN_AFX_SDK_VERSION >> 24) & 0xff, + (MIN_AFX_SDK_VERSION >> 16) & 0x00ff, + (MIN_AFX_SDK_VERSION >> 8) & 0x0000ff, + (MIN_VFX_SDK_VERSION >> 24) & 0xff, + (MIN_VFX_SDK_VERSION >> 16) & 0x00ff, + (MIN_VFX_SDK_VERSION >> 8) & 0x0000ff); + } + } + if (!load_lib()) { + blog(LOG_INFO, + "[NVIDIA Audio Effects:] NVIDIA denoiser disabled, redistributable not found or could not be loaded."); + return false; + } + + nvidia_afx_initializer_mutex_initialized = + pthread_mutex_init(&nvidia_afx_initializer_mutex, NULL) == 0; + +#define LOAD_SYM_FROM_LIB(sym, lib, dll) \ + if (!(sym = (sym##_t)GetProcAddress(lib, #sym))) { \ + DWORD err = GetLastError(); \ + printf("[noise suppress]: Couldn't load " #sym " from " dll \ + ": %lu (0x%lx)", \ + err, err); \ + goto unload_everything; \ + } + +#define LOAD_SYM(sym) LOAD_SYM_FROM_LIB(sym, nv_audiofx, "NVAudioEffects.dll") + LOAD_SYM(NvAFX_GetEffectList); + LOAD_SYM(NvAFX_CreateEffect); + LOAD_SYM(NvAFX_CreateChainedEffect); + LOAD_SYM(NvAFX_DestroyEffect); + LOAD_SYM(NvAFX_SetU32); + LOAD_SYM(NvAFX_SetU32List); + LOAD_SYM(NvAFX_SetString); + LOAD_SYM(NvAFX_SetStringList); + LOAD_SYM(NvAFX_SetFloat); + LOAD_SYM(NvAFX_SetFloatList); + LOAD_SYM(NvAFX_GetU32); + LOAD_SYM(NvAFX_GetString); + LOAD_SYM(NvAFX_GetStringList); + LOAD_SYM(NvAFX_GetFloat); + LOAD_SYM(NvAFX_GetFloatList); + LOAD_SYM(NvAFX_Load); + LOAD_SYM(NvAFX_GetSupportedDevices); + LOAD_SYM(NvAFX_Run); + LOAD_SYM(NvAFX_Reset); +#undef LOAD_SYM +#define LOAD_SYM(sym) LOAD_SYM_FROM_LIB(sym, nv_cuda, "nvcuda.dll") + LOAD_SYM(cuCtxGetCurrent); + LOAD_SYM(cuCtxPopCurrent); + LOAD_SYM(cuInit); +#undef LOAD_SYM + + NvAFX_Status err; + CUresult cudaerr; + + NvAFX_Handle h = NULL; + + cudaerr = cuInit(0); + if (cudaerr != CUDA_SUCCESS) { + goto cuda_errors; + } + CUcontext old = {0}; + CUcontext curr = {0}; + cudaerr = cuCtxGetCurrent(&old); + if (cudaerr != CUDA_SUCCESS) { + goto cuda_errors; + } + + err = NvAFX_CreateEffect(NVAFX_EFFECT_DENOISER, &h); + cudaerr = cuCtxGetCurrent(&curr); + if (cudaerr != CUDA_SUCCESS) { + goto cuda_errors; + } + + if (curr != old) { + cudaerr = cuCtxPopCurrent(NULL); + if (cudaerr != CUDA_SUCCESS) + goto cuda_errors; + } + + if (err != NVAFX_STATUS_SUCCESS) { + if (err == NVAFX_STATUS_GPU_UNSUPPORTED) { + blog(LOG_INFO, + "[NVIDIA Audio Effects:] disabled: unsupported GPU"); + } else { + blog(LOG_ERROR, + "[NVIDIA Audio Effects:] disabled, error %i", err); + } + goto unload_everything; + } + + err = NvAFX_DestroyEffect(h); + if (err != NVAFX_STATUS_SUCCESS) { + blog(LOG_ERROR, "[NVIDIA Audio Effects:]: disabled, error %i", + err); + goto unload_everything; + } + + nvidia_afx_loaded = true; + blog(LOG_INFO, "[NVIDIA Audio Effects:] enabled"); + return true; + +cuda_errors: + blog(LOG_ERROR, "[NVIDIA Audio Effects:] disabled, CUDA error %i", + cudaerr); +unload_everything: + release_afxlib(); + + return false; +} +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +void unload_nvidia_afx(void) +{ + release_afxlib(); + + if (nvidia_afx_initializer_mutex_initialized) { + pthread_mutex_destroy(&nvidia_afx_initializer_mutex); + nvidia_afx_initializer_mutex_initialized = false; + } +} + +static bool nvidia_audio_initialize_internal(void *data) +{ + struct nvidia_audio_data *ng = data; + NvAFX_Status err; + + if (!ng->handle[0]) { + ng->sample_rate = NVAFX_SAMPLE_RATE; + for (size_t i = 0; i < ng->channels; i++) { + // Create FX + CUcontext old = {0}; + CUcontext curr = {0}; + if (cuCtxGetCurrent(&old) != CUDA_SUCCESS) { + goto failure; + } + err = NvAFX_CreateEffect(ng->fx, &ng->handle[i]); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "%s FX creation failed, error %i", + ng->fx, err); + goto failure; + } + if (cuCtxGetCurrent(&curr) != CUDA_SUCCESS) { + goto failure; + } + if (curr != old) { + cuCtxPopCurrent(NULL); + } + // Set sample rate of FX + err = NvAFX_SetU32(ng->handle[i], + NVAFX_PARAM_INPUT_SAMPLE_RATE, + ng->sample_rate); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "NvAFX_SetU32(Sample Rate: %u) failed, error %i", + ng->sample_rate, err); + goto failure; + } + + // Set intensity of FX + err = NvAFX_SetFloat(ng->handle[i], + NVAFX_PARAM_INTENSITY_RATIO, + ng->intensity_ratio); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "NvAFX_SetFloat(Intensity Ratio: %f) failed, error %i", + ng->intensity_ratio, err); + goto failure; + } + + // Set AI models path + err = NvAFX_SetString(ng->handle[i], + NVAFX_PARAM_MODEL_PATH, + ng->model); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "NvAFX_SetString() failed, error %i", + err); + goto failure; + } + + // Load FX (this is a very long step, about 2 seconds) + err = NvAFX_Load(ng->handle[i]); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "NvAFX_Load() failed with error %i", + err); + goto failure; + } + os_atomic_set_bool(&ng->reinit_done, true); + } + } + return true; + +failure: + ng->use_nvafx = false; + return false; +} + +static void *nvidia_audio_initialize(void *data) +{ + struct nvidia_audio_data *ng = data; + NvAFX_Status err; + + if (!ng->use_nvafx && !nvidia_afx_loaded) { + return NULL; + } + pthread_mutex_lock(&ng->nvafx_mutex); + pthread_mutex_lock(&nvidia_afx_initializer_mutex); + if (!nvidia_audio_initialize_internal(data)) { + goto failure; + } + if (ng->use_nvafx) { + err = NvAFX_GetU32(ng->handle[0], + NVAFX_PARAM_NUM_INPUT_CHANNELS, + &ng->num_channels); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "NvAFX_GetU32() failed to get the number of channels, error %i", + err); + goto failure; + } + if (ng->num_channels != 1) { + do_log(LOG_ERROR, + "The number of channels is not 1 in the sdk any more ==> update code"); + goto failure; + } + NvAFX_Status err = NvAFX_GetU32( + ng->handle[0], NVAFX_PARAM_NUM_INPUT_SAMPLES_PER_FRAME, + &ng->num_samples_per_frame); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "NvAFX_GetU32() failed to get the number of samples per frame, error %i", + err); + goto failure; + } + if (ng->num_samples_per_frame != NVAFX_FRAME_SIZE) { + do_log(LOG_ERROR, + "The number of samples per frame has changed from 480 (= 10 ms) ==> update code"); + goto failure; + } + } + ng->nvafx_initialized = true; + ng->nvafx_loading = false; + pthread_mutex_unlock(&nvidia_afx_initializer_mutex); + pthread_mutex_unlock(&ng->nvafx_mutex); + return NULL; + +failure: + ng->use_nvafx = false; + pthread_mutex_unlock(&nvidia_afx_initializer_mutex); + pthread_mutex_unlock(&ng->nvafx_mutex); + nvidia_audio_destroy(ng); + return NULL; +} + +static inline enum speaker_layout nv_convert_speaker_layout(uint8_t channels) +{ + switch (channels) { + case 0: + return SPEAKERS_UNKNOWN; + case 1: + return SPEAKERS_MONO; + case 2: + return SPEAKERS_STEREO; + case 3: + return SPEAKERS_2POINT1; + case 4: + return SPEAKERS_4POINT0; + case 5: + return SPEAKERS_4POINT1; + case 6: + return SPEAKERS_5POINT1; + case 8: + return SPEAKERS_7POINT1; + default: + return SPEAKERS_UNKNOWN; + } +} + +static void set_nv_model(void *data, const char *method) +{ + struct nvidia_audio_data *ng = data; + const char *file; + + if (strcmp(NVAFX_EFFECT_DEREVERB, method) == 0) + file = NVAFX_EFFECT_DEREVERB_MODEL; + else if (strcmp(NVAFX_EFFECT_DEREVERB_DENOISER, method) == 0) + file = NVAFX_EFFECT_DEREVERB_DENOISER_MODEL; + else + file = NVAFX_EFFECT_DENOISER_MODEL; + + size_t size = strlen(ng->sdk_path) + strlen(file) + 1; + char *buffer = (char *)bmalloc(size); + + strcpy(buffer, ng->sdk_path); + strcat(buffer, file); + ng->model = buffer; +} + +static void nvidia_audio_update(void *data, obs_data_t *s) +{ + struct nvidia_audio_data *ng = data; + + if (!ng->use_nvafx) + return; + + const char *method = obs_data_get_string(s, S_METHOD); + ng->latency = 1000000000LL / (1000 / BUFFER_SIZE_MSEC); + + float intensity = (float)obs_data_get_double(s, S_NVAFX_INTENSITY); + /*-------------------------------------------------------------------*/ + /* STAGE 1 : the following is run only when the filter is created. */ + + /* If the DLL hasn't been loaded & isn't loading, start the side loading. */ + if (!ng->nvafx_initialized && !ng->nvafx_loading) { + ng->intensity_ratio = intensity; + ng->nvafx_loading = true; + pthread_create(&ng->nvafx_thread, NULL, nvidia_audio_initialize, + ng); + } + + /*-------------------------------------------------------------------*/ + /* STAGE 2 : this is executed only after the FX has been initialized */ + if (ng->nvafx_initialized) { + /* updating the intensity of the FX */ + if (intensity != ng->intensity_ratio && + (strcmp(ng->fx, method) == 0)) { + NvAFX_Status err; + ng->intensity_ratio = intensity; + pthread_mutex_lock(&ng->nvafx_mutex); + for (size_t i = 0; i < ng->channels; i++) { + err = NvAFX_SetFloat( + ng->handle[i], + NVAFX_PARAM_INTENSITY_RATIO, + ng->intensity_ratio); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "NvAFX_SetFloat(Intensity Ratio: %f) failed, error %i", + ng->intensity_ratio, err); + nvidia_audio_destroy(ng); + } + } + pthread_mutex_unlock(&ng->nvafx_mutex); + } + /* swapping to a new FX requires a reinitialization */ + if ((strcmp(ng->fx, method) != 0)) { + pthread_mutex_lock(&ng->nvafx_mutex); + bfree((void *)ng->fx); + ng->fx = bstrdup(method); + ng->intensity_ratio = intensity; + set_nv_model(ng, method); + os_atomic_set_bool(&ng->reinit_done, false); + for (int i = 0; i < (int)ng->channels; i++) { + /* Destroy previous FX */ + if (NvAFX_DestroyEffect(ng->handle[i]) != + NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "FX failed to be destroyed."); + nvidia_audio_destroy(ng); + } else { + ng->handle[i] = NULL; + } + } + if (!nvidia_audio_initialize_internal(data)) + nvidia_audio_destroy(ng); + + pthread_mutex_unlock(&ng->nvafx_mutex); + } + } +} + +static void *nvidia_audio_create(obs_data_t *settings, obs_source_t *filter) +{ + struct nvidia_audio_data *ng = + bzalloc(sizeof(struct nvidia_audio_data)); + + ng->context = filter; + + char sdk_path[MAX_PATH]; + + /* find SDK */ + if (!nvafx_get_sdk_path(sdk_path, sizeof(sdk_path))) { + ng->nvidia_sdk_dir_found = false; + do_log(LOG_ERROR, "NVAFX redist is not installed."); + nvidia_audio_destroy(ng); + return NULL; + } else { + size_t size = sizeof(sdk_path) + 1; + ng->sdk_path = bmalloc(size); + strcpy(ng->sdk_path, sdk_path); + ng->nvidia_sdk_dir_found = true; + ng->nvafx_initialized = false; + ng->nvafx_loading = false; + ng->fx = NULL; + + pthread_mutex_init(&ng->nvafx_mutex, NULL); + + info("NVAFX SDK redist path was found here %s", sdk_path); + // set FX + const char *method = obs_data_get_string(settings, S_METHOD); + set_nv_model(ng, method); + ng->fx = bstrdup(method); + ng->use_nvafx = true; + } + + /* Process 10 millisecond segments to keep latency low. */ + /* At 48kHz, NVAFX processes 480 samples which corresponds to 10 ms.*/ + uint32_t sample_rate = audio_output_get_sample_rate(obs_get_audio()); + size_t channels = audio_output_get_channels(obs_get_audio()); + size_t frames = (size_t)sample_rate / (1000 / BUFFER_SIZE_MSEC); + ng->frames = frames; + ng->channels = channels; + + /* allocate buffers */ + ng->copy_buffers[0] = bmalloc(frames * channels * sizeof(float)); + ng->nvafx_segment_buffers[0] = + bmalloc(NVAFX_FRAME_SIZE * channels * sizeof(float)); + for (size_t c = 1; c < channels; ++c) { + ng->copy_buffers[c] = ng->copy_buffers[c - 1] + frames; + ng->nvafx_segment_buffers[c] = + ng->nvafx_segment_buffers[c - 1] + NVAFX_FRAME_SIZE; + } + + /* reserve circular buffers */ + for (size_t i = 0; i < channels; i++) { + deque_reserve(&ng->input_buffers[i], frames * sizeof(float)); + deque_reserve(&ng->output_buffers[i], frames * sizeof(float)); + } + + /* create resampler if the source is not at 48 kHz */ + if (sample_rate == NVAFX_SAMPLE_RATE) { + ng->nvafx_resampler = NULL; + ng->nvafx_resampler_back = NULL; + } else { + struct resample_info src, dst; + src.samples_per_sec = sample_rate; + src.format = AUDIO_FORMAT_FLOAT_PLANAR; + src.speakers = nv_convert_speaker_layout((uint8_t)channels); + + dst.samples_per_sec = NVAFX_SAMPLE_RATE; + dst.format = AUDIO_FORMAT_FLOAT_PLANAR; + dst.speakers = nv_convert_speaker_layout((uint8_t)channels); + + ng->nvafx_resampler = audio_resampler_create(&dst, &src); + ng->nvafx_resampler_back = audio_resampler_create(&src, &dst); + } + + nvidia_audio_update(ng, settings); + return ng; +} + +static inline void process_fx(struct nvidia_audio_data *ng) +{ + /* Resample if necessary */ + if (ng->nvafx_resampler) { + float *output[MAX_PREPROC_CHANNELS]; + uint32_t out_frames; + uint64_t ts_offset; + audio_resampler_resample(ng->nvafx_resampler, + (uint8_t **)output, &out_frames, + &ts_offset, + (const uint8_t **)ng->copy_buffers, + (uint32_t)ng->frames); + + for (size_t i = 0; i < ng->channels; i++) { + for (ssize_t j = 0, + k = (ssize_t)out_frames - NVAFX_FRAME_SIZE; + j < NVAFX_FRAME_SIZE; ++j, ++k) { + if (k >= 0) { + ng->nvafx_segment_buffers[i][j] = + output[i][k]; + } else { + ng->nvafx_segment_buffers[i][j] = 0; + } + } + } + } else { + for (size_t i = 0; i < ng->channels; i++) { + for (size_t j = 0; j < NVAFX_FRAME_SIZE; ++j) { + ng->nvafx_segment_buffers[i][j] = + ng->copy_buffers[i][j]; + } + } + } + + /* Execute */ + size_t runs = ng->has_mono_src ? 1 : ng->channels; + if (ng->reinit_done) { + pthread_mutex_lock(&ng->nvafx_mutex); + for (size_t i = 0; i < runs; i++) { + NvAFX_Status err = NvAFX_Run( + ng->handle[i], &ng->nvafx_segment_buffers[i], + &ng->nvafx_segment_buffers[i], + ng->num_samples_per_frame, ng->num_channels); + if (err != NVAFX_STATUS_SUCCESS) { + if (err == NVAFX_STATUS_FAILED) { + do_log(LOG_DEBUG, + "NvAFX_Run() failed, error NVAFX_STATUS_FAILED.\n" + "This can occur when changing the FX and is not consequential."); + // stop all processing; this will be reset at new init + os_atomic_set_bool(&ng->reinit_done, + false); + } else { + do_log(LOG_ERROR, + "NvAFX_Run() failed, error %i.\n", + err); + } + } + } + pthread_mutex_unlock(&ng->nvafx_mutex); + } + if (ng->has_mono_src) { + memcpy(ng->nvafx_segment_buffers[1], + ng->nvafx_segment_buffers[0], + NVAFX_FRAME_SIZE * sizeof(float)); + } + /* Revert signal level adjustment, resample back if necessary */ + if (ng->nvafx_resampler) { + float *output[MAX_PREPROC_CHANNELS]; + uint32_t out_frames; + uint64_t ts_offset; + audio_resampler_resample( + ng->nvafx_resampler_back, (uint8_t **)output, + &out_frames, &ts_offset, + (const uint8_t **)ng->nvafx_segment_buffers, + NVAFX_FRAME_SIZE); + + for (size_t i = 0; i < ng->channels; i++) { + for (ssize_t j = 0, + k = (ssize_t)out_frames - ng->frames; + j < (ssize_t)ng->frames; ++j, ++k) { + if (k >= 0) { + ng->copy_buffers[i][j] = output[i][k]; + } else { + ng->copy_buffers[i][j] = 0; + } + } + } + } else { + for (size_t i = 0; i < ng->channels; i++) { + for (size_t j = 0; j < NVAFX_FRAME_SIZE; ++j) { + ng->copy_buffers[i][j] = + ng->nvafx_segment_buffers[i][j]; + } + } + } +} + +static inline void process(struct nvidia_audio_data *ng) +{ + /* Pop from input deque */ + for (size_t i = 0; i < ng->channels; i++) + deque_pop_front(&ng->input_buffers[i], ng->copy_buffers[i], + ng->frames * sizeof(float)); + + if (ng->use_nvafx && nvidia_afx_loaded && ng->nvafx_initialized) { + process_fx(ng); + } + + /* Push to output deque */ + for (size_t i = 0; i < ng->channels; i++) + deque_push_back(&ng->output_buffers[i], ng->copy_buffers[i], + ng->frames * sizeof(float)); +} + +struct nv_audio_info { + uint32_t frames; + uint64_t timestamp; +}; + +static inline void clear_deque(struct deque *buf) +{ + deque_pop_front(buf, NULL, buf->size); +} + +static void reset_data(struct nvidia_audio_data *ng) +{ + for (size_t i = 0; i < ng->channels; i++) { + clear_deque(&ng->input_buffers[i]); + clear_deque(&ng->output_buffers[i]); + } + + clear_deque(&ng->info_buffer); +} + +static struct obs_audio_data * +nvidia_audio_filter_audio(void *data, struct obs_audio_data *audio) +{ + struct nvidia_audio_data *ng = data; + struct nv_audio_info info; + size_t segment_size = ng->frames * sizeof(float); + size_t out_size; + obs_source_t *parent = obs_filter_get_parent(ng->context); + if (!parent) + return NULL; + enum speaker_layout layout = obs_source_get_speaker_layout(parent); + ng->has_mono_src = layout == SPEAKERS_MONO && ng->channels == 2; + + /* ----------------------------------------------- + * If timestamp has dramatically changed, consider it a new stream of + * audio data. Clear all circular buffers to prevent old audio data + * from being processed as part of the new data. */ + if (ng->last_timestamp) { + int64_t diff = llabs((int64_t)ng->last_timestamp - + (int64_t)audio->timestamp); + + if (diff > 1000000000LL) + reset_data(ng); + } + + ng->last_timestamp = audio->timestamp; + + /* ----------------------------------------------- + * push audio packet info (timestamp/frame count) to info deque */ + info.frames = audio->frames; + info.timestamp = audio->timestamp; + deque_push_back(&ng->info_buffer, &info, sizeof(info)); + + /* ----------------------------------------------- + * push back current audio data to input deque */ + for (size_t i = 0; i < ng->channels; i++) + deque_push_back(&ng->input_buffers[i], audio->data[i], + audio->frames * sizeof(float)); + + /* ----------------------------------------------- + * pop/process each 10ms segments, push back to output deque */ + while (ng->input_buffers[0].size >= segment_size) + process(ng); + + /* ----------------------------------------------- + * peek front of info deque, check to see if we have enough to + * pop the expected packet size, if not, return null */ + memset(&info, 0, sizeof(info)); + deque_peek_front(&ng->info_buffer, &info, sizeof(info)); + out_size = info.frames * sizeof(float); + + if (ng->output_buffers[0].size < out_size) + return NULL; + + /* ----------------------------------------------- + * if there's enough audio data buffered in the output deque, + * pop and return a packet */ + deque_pop_front(&ng->info_buffer, NULL, sizeof(info)); + da_resize(ng->output_data, out_size * ng->channels); + + for (size_t i = 0; i < ng->channels; i++) { + ng->output_audio.data[i] = + (uint8_t *)&ng->output_data.array[i * out_size]; + + deque_pop_front(&ng->output_buffers[i], + ng->output_audio.data[i], out_size); + } + + ng->output_audio.frames = info.frames; + ng->output_audio.timestamp = info.timestamp - ng->latency; + return &ng->output_audio; +} + +static void nvidia_audio_defaults(obs_data_t *s) +{ + obs_data_set_default_double(s, S_NVAFX_INTENSITY, 1.0); + obs_data_set_default_string(s, S_METHOD, S_METHOD_NVAFX_DENOISER); +} + +static obs_properties_t *nvidia_audio_properties(void *data) +{ + obs_properties_t *ppts = obs_properties_create(); + struct nvidia_audio_data *ng = (struct nvidia_audio_data *)data; + obs_property_t *method = obs_properties_add_list( + ppts, S_METHOD, TEXT_METHOD, OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + if (ng->nvidia_sdk_dir_found) { + obs_property_list_add_string(method, TEXT_METHOD_NVAFX_DENOISER, + S_METHOD_NVAFX_DENOISER); + obs_property_list_add_string(method, TEXT_METHOD_NVAFX_DEREVERB, + S_METHOD_NVAFX_DEREVERB); + obs_property_list_add_string( + method, TEXT_METHOD_NVAFX_DEREVERB_DENOISER, + S_METHOD_NVAFX_DEREVERB_DENOISER); + obs_property_t *slider = obs_properties_add_float_slider( + ppts, S_NVAFX_INTENSITY, TEXT_NVAFX_INTENSITY, 0.0f, + 1.0f, 0.01f); + + unsigned int version = get_lib_version(); + obs_property_t *warning = obs_properties_add_text( + ppts, "deprecation", NULL, OBS_TEXT_INFO); + if (version && version < MIN_AFX_SDK_VERSION) { + obs_property_text_set_info_type(warning, + OBS_TEXT_INFO_WARNING); + obs_property_set_long_description( + warning, TEXT_METHOD_NVAFX_DEPRECATION); + } else { + obs_property_set_visible(warning, 0); + } + } + + return ppts; +} + +struct obs_source_info nvidia_audiofx_filter = { + .id = "nvidia_audiofx_filter", + .type = OBS_SOURCE_TYPE_FILTER, + .output_flags = OBS_SOURCE_AUDIO, + .get_name = nvidia_audio_name, + .create = nvidia_audio_create, + .destroy = nvidia_audio_destroy, + .update = nvidia_audio_update, + .filter_audio = nvidia_audio_filter_audio, + .get_defaults = nvidia_audio_defaults, + .get_properties = nvidia_audio_properties, +}; diff --git a/plugins/obs-filters/nvidia-greenscreen-filter.c b/plugins/nv-filters/nvidia-greenscreen-filter.c similarity index 98% rename from plugins/obs-filters/nvidia-greenscreen-filter.c rename to plugins/nv-filters/nvidia-greenscreen-filter.c index 93f833234acc8a..646c3ad5bcb1d4 100644 --- a/plugins/obs-filters/nvidia-greenscreen-filter.c +++ b/plugins/nv-filters/nvidia-greenscreen-filter.c @@ -30,13 +30,13 @@ #define S_PROCESSING "processing_interval" #define MT_ obs_module_text -#define TEXT_MODE MT_("Greenscreen.Mode") -#define TEXT_MODE_QUALITY MT_("Greenscreen.Quality") -#define TEXT_MODE_PERF MT_("Greenscreen.Performance") -#define TEXT_MODE_THRESHOLD MT_("Greenscreen.Threshold") -#define TEXT_DEPRECATION MT_("Greenscreen.Deprecation") -#define TEXT_PROCESSING MT_("Greenscreen.Processing") -#define TEXT_PROCESSING_HINT MT_("Greenscreen.Processing.Hint") +#define TEXT_MODE MT_("Nvvfx.Method.Greenscreen.Mode") +#define TEXT_MODE_QUALITY MT_("Nvvfx.Method.Greenscreen.Quality") +#define TEXT_MODE_PERF MT_("Nvvfx.Method.Greenscreen.Performance") +#define TEXT_MODE_THRESHOLD MT_("Nvvfx.Method.Greenscreen.Threshold") +#define TEXT_DEPRECATION MT_("Nvvfx.OutdatedSDK") +#define TEXT_PROCESSING MT_("Nvvfx.Method.Greenscreen.Processing") +#define TEXT_PROCESSING_HINT MT_("Nvvfx.Method.Greenscreen.Processing.Hint") bool nvvfx_loaded = false; bool nvvfx_new_sdk = false; @@ -87,7 +87,7 @@ struct nv_greenscreen_data { static const char *nv_greenscreen_filter_name(void *unused) { UNUSED_PARAMETER(unused); - return obs_module_text("NvidiaGreenscreenFilter"); + return obs_module_text("Nvvfx.Method.Greenscreen"); } static void nv_greenscreen_filter_update(void *data, obs_data_t *settings) @@ -883,7 +883,7 @@ static void nv_greenscreen_filter_render(void *data, gs_effect_t *effect) UNUSED_PARAMETER(effect); } -bool load_nvvfx(void) +bool load_nvidia_vfx(void) { bool old_sdk_loaded = false; unsigned int version = get_lib_version(); @@ -1032,7 +1032,7 @@ bool load_nvvfx(void) } #ifdef LIBNVVFX_ENABLED -void unload_nvvfx(void) +void unload_nvidia_vfx(void) { release_nv_vfx(); } diff --git a/plugins/obs-filters/nvvfx-load.h b/plugins/nv-filters/nvvfx-load.h similarity index 99% rename from plugins/obs-filters/nvvfx-load.h rename to plugins/nv-filters/nvvfx-load.h index 96e8e62a3e43dd..3ef4c0587844df 100644 --- a/plugins/obs-filters/nvvfx-load.h +++ b/plugins/nv-filters/nvvfx-load.h @@ -7,6 +7,7 @@ #include #include #include +#include "nv_sdk_versions.h" #ifdef __cplusplus extern "C" { @@ -39,7 +40,6 @@ extern "C" { #define CUDARTAPI #ifdef LIBNVVFX_ENABLED -#define MIN_VFX_SDK_VERSION (0 << 24 | 7 << 16 | 2 << 8 | 0 << 0) static HMODULE nv_videofx = NULL; static HMODULE nv_cvimage = NULL; static HMODULE nv_cudart = NULL; diff --git a/plugins/obs-filters/CMakeLists.txt b/plugins/obs-filters/CMakeLists.txt index 697b4985afee23..0a9aeaa7d09938 100644 --- a/plugins/obs-filters/CMakeLists.txt +++ b/plugins/obs-filters/CMakeLists.txt @@ -5,6 +5,10 @@ legacy_check() add_library(obs-filters MODULE) add_library(OBS::filters ALIAS obs-filters) +if(OS_WINDOWS) + target_enable_feature(obs-filters "NVIDIA Audio FX support" LIBNVAFX_ENABLED HAS_NOISEREDUCTION) +endif() + target_sources( obs-filters PRIVATE # cmake-format: sortable @@ -36,8 +40,6 @@ include(cmake/speexdsp.cmake) include(cmake/rnnoise.cmake) if(OS_WINDOWS) - include(cmake/nvidia.cmake) - configure_file(cmake/windows/obs-module.rc.in obs-filters.rc) target_sources(obs-filters PRIVATE obs-filters.rc) endif() diff --git a/plugins/obs-filters/cmake/nvidia.cmake b/plugins/obs-filters/cmake/nvidia.cmake deleted file mode 100644 index 22d1a003bb4b75..00000000000000 --- a/plugins/obs-filters/cmake/nvidia.cmake +++ /dev/null @@ -1,15 +0,0 @@ -option(ENABLE_NVAFX "Enable building with NVIDIA Audio Effects SDK (requires redistributable to be installed)" ON) -option(ENABLE_NVVFX "Enable building with NVIDIA Video Effects SDK (requires redistributable to be installed)" ON) - -if(ENABLE_NVAFX) - target_enable_feature(obs-filters "NVIDIA Audio FX support" LIBNVAFX_ENABLED HAS_NOISEREDUCTION) -else() - target_disable_feature(obs-filters "NVIDIA Audio FX support") -endif() - -if(ENABLE_NVVFX) - target_enable_feature(obs-filters "NVIDIA Video FX support" LIBNVVFX_ENABLED) - target_sources(obs-filters PRIVATE nvidia-greenscreen-filter.c) -else() - target_disable_feature(obs-filters "NVIDIA Video FX support") -endif() diff --git a/plugins/obs-filters/data/locale/en-US.ini b/plugins/obs-filters/data/locale/en-US.ini index c4de876bc6b6c2..4ebf3f34e66e20 100644 --- a/plugins/obs-filters/data/locale/en-US.ini +++ b/plugins/obs-filters/data/locale/en-US.ini @@ -90,7 +90,7 @@ NoiseSuppress.Method.RNNoise="RNNoise (good quality, more CPU usage)" NoiseSuppress.Method.Nvafx.Denoiser="NVIDIA Noise Removal" NoiseSuppress.Method.Nvafx.Dereverb="NVIDIA Room Echo Removal" NoiseSuppress.Method.Nvafx.DenoiserPlusDereverb="NVIDIA Noise Removal + Room Echo Removal" -NoiseSuppress.Method.Nvafx.Deprecation="WARNING: Please upgrade both NVIDIA Video & Audio SDK. Your current version of Audio SDK is outdated." +NoiseSuppress.Method.Nvafx.Deprecation2="WARNING: NVIDIA Audio Effects will be automatically migrated to a new dedicated filter 'NVIDIA Audio Effects' once the source is enabled." Saturation="Saturation" HueShift="Hue Shift" Amount="Amount" diff --git a/plugins/obs-filters/noise-suppress-filter.c b/plugins/obs-filters/noise-suppress-filter.c index 22e363d5314832..cde1c7b9b55eb5 100644 --- a/plugins/obs-filters/noise-suppress-filter.c +++ b/plugins/obs-filters/noise-suppress-filter.c @@ -61,6 +61,8 @@ bool nvafx_loaded = false; MT_("NoiseSuppress.Method.Nvafx.DenoiserPlusDereverb") #define TEXT_METHOD_NVAFX_DEPRECATION \ MT_("NoiseSuppress.Method.Nvafx.Deprecation") +#define TEXT_METHOD_NVAFX_DEPRECATION2 \ + MT_("NoiseSuppress.Method.Nvafx.Deprecation2") #define MAX_PREPROC_CHANNELS 8 @@ -93,8 +95,11 @@ struct noise_suppress_data { struct deque output_buffers[MAX_PREPROC_CHANNELS]; bool use_rnnoise; - bool use_nvafx; bool nvafx_enabled; + bool nvafx_migrated; +#ifdef LIBNVAFX_ENABLED + obs_source_t *migrated_filter; +#endif bool has_mono_src; volatile bool reinit_done; #ifdef LIBSPEEXDSP_ENABLED @@ -110,28 +115,6 @@ struct noise_suppress_data { audio_resampler_t *rnn_resampler; audio_resampler_t *rnn_resampler_back; #endif - -#ifdef LIBNVAFX_ENABLED - /* NVAFX handle, one per audio channel */ - NvAFX_Handle handle[MAX_PREPROC_CHANNELS]; - - uint32_t sample_rate; - float intensity_ratio; - unsigned int num_samples_per_frame, num_channels; - char *model; - bool nvafx_initialized; - const char *fx; - char *sdk_path; - - /* Resampler */ - audio_resampler_t *nvafx_resampler; - audio_resampler_t *nvafx_resampler_back; - - /* Initialization */ - bool nvafx_loading; - pthread_t nvafx_thread; - pthread_mutex_t nvafx_mutex; -#endif /* PCM buffers */ float *copy_buffers[MAX_PREPROC_CHANNELS]; #ifdef LIBSPEEXDSP_ENABLED @@ -140,21 +123,11 @@ struct noise_suppress_data { #ifdef LIBRNNOISE_ENABLED float *rnn_segment_buffers[MAX_PREPROC_CHANNELS]; #endif -#ifdef LIBNVAFX_ENABLED - float *nvafx_segment_buffers[MAX_PREPROC_CHANNELS]; -#endif - /* output data */ struct obs_audio_data output_audio; DARRAY(float) output_data; }; -#ifdef LIBNVAFX_ENABLED -/* global mutex for nvafx load functions since they aren't thread-safe */ -bool nvafx_initializer_mutex_initialized; -pthread_mutex_t nvafx_initializer_mutex; -#endif - /* -------------------------------------------------------- */ #define SUP_MIN -60 @@ -177,25 +150,12 @@ static void noise_suppress_destroy(void *data) { struct noise_suppress_data *ng = data; -#ifdef LIBNVAFX_ENABLED - if (ng->nvafx_enabled) - pthread_mutex_lock(&ng->nvafx_mutex); -#endif - for (size_t i = 0; i < ng->channels; i++) { #ifdef LIBSPEEXDSP_ENABLED speex_preprocess_state_destroy(ng->spx_states[i]); #endif #ifdef LIBRNNOISE_ENABLED rnnoise_destroy(ng->rnn_states[i]); -#endif -#ifdef LIBNVAFX_ENABLED - if (ng->handle[0]) { - if (NvAFX_DestroyEffect(ng->handle[i]) != - NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, "NvAFX_Release() failed"); - } - } #endif deque_free(&ng->input_buffers[i]); deque_free(&ng->output_buffers[i]); @@ -212,175 +172,12 @@ static void noise_suppress_destroy(void *data) audio_resampler_destroy(ng->rnn_resampler_back); } #endif -#ifdef LIBNVAFX_ENABLED - bfree(ng->nvafx_segment_buffers[0]); - - if (ng->nvafx_resampler) { - audio_resampler_destroy(ng->nvafx_resampler); - audio_resampler_destroy(ng->nvafx_resampler_back); - } - bfree(ng->model); - bfree(ng->sdk_path); - bfree((void *)ng->fx); - if (ng->nvafx_enabled) { - if (ng->use_nvafx) - pthread_join(ng->nvafx_thread, NULL); - pthread_mutex_unlock(&ng->nvafx_mutex); - pthread_mutex_destroy(&ng->nvafx_mutex); - } -#endif - bfree(ng->copy_buffers[0]); deque_free(&ng->info_buffer); da_free(ng->output_data); bfree(ng); } -#ifdef LIBNVAFX_ENABLED -static bool nvafx_initialize_internal(void *data) -{ - struct noise_suppress_data *ng = data; - NvAFX_Status err; - - if (!ng->handle[0]) { - ng->sample_rate = NVAFX_SAMPLE_RATE; - for (size_t i = 0; i < ng->channels; i++) { - // Create FX - CUcontext old = {0}; - CUcontext curr = {0}; - if (cuCtxGetCurrent(&old) != CUDA_SUCCESS) { - goto failure; - } - // if initialization was with rnnoise or speex - if (strcmp(ng->fx, S_METHOD_NVAFX_DENOISER) != 0 && - strcmp(ng->fx, S_METHOD_NVAFX_DEREVERB) != 0 && - strcmp(ng->fx, S_METHOD_NVAFX_DEREVERB_DENOISER) != - 0) { - ng->fx = bstrdup(S_METHOD_NVAFX_DENOISER); - } - err = NvAFX_CreateEffect(ng->fx, &ng->handle[i]); - if (err != NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "%s FX creation failed, error %i", - ng->fx, err); - goto failure; - } - if (cuCtxGetCurrent(&curr) != CUDA_SUCCESS) { - goto failure; - } - if (curr != old) { - cuCtxPopCurrent(NULL); - } - // Set sample rate of FX - err = NvAFX_SetU32(ng->handle[i], - NVAFX_PARAM_INPUT_SAMPLE_RATE, - ng->sample_rate); - if (err != NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "NvAFX_SetU32(Sample Rate: %u) failed, error %i", - ng->sample_rate, err); - goto failure; - } - - // Set intensity of FX - err = NvAFX_SetFloat(ng->handle[i], - NVAFX_PARAM_INTENSITY_RATIO, - ng->intensity_ratio); - if (err != NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "NvAFX_SetFloat(Intensity Ratio: %f) failed, error %i", - ng->intensity_ratio, err); - goto failure; - } - - // Set AI models path - err = NvAFX_SetString(ng->handle[i], - NVAFX_PARAM_MODEL_PATH, - ng->model); - if (err != NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "NvAFX_SetString() failed, error %i", - err); - goto failure; - } - - // Load FX - err = NvAFX_Load(ng->handle[i]); - if (err != NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "NvAFX_Load() failed with error %i", - err); - goto failure; - } - os_atomic_set_bool(&ng->reinit_done, true); - } - } - return true; - -failure: - ng->use_nvafx = false; - return false; -} -#endif - -#ifdef LIBNVAFX_ENABLED -static void *nvafx_initialize(void *data) -{ - struct noise_suppress_data *ng = data; - NvAFX_Status err; - - if (!ng->use_nvafx || !nvafx_loaded) { - return NULL; - } - pthread_mutex_lock(&ng->nvafx_mutex); - pthread_mutex_lock(&nvafx_initializer_mutex); - if (!nvafx_initialize_internal(data)) { - goto failure; - } - if (ng->use_nvafx) { - err = NvAFX_GetU32(ng->handle[0], - NVAFX_PARAM_NUM_INPUT_CHANNELS, - &ng->num_channels); - if (err != NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "NvAFX_GetU32() failed to get the number of channels, error %i", - err); - goto failure; - } - if (ng->num_channels != 1) { - do_log(LOG_ERROR, - "The number of channels is not 1 in the sdk any more ==> update code"); - goto failure; - } - NvAFX_Status err = NvAFX_GetU32( - ng->handle[0], NVAFX_PARAM_NUM_INPUT_SAMPLES_PER_FRAME, - &ng->num_samples_per_frame); - if (err != NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "NvAFX_GetU32() failed to get the number of samples per frame, error %i", - err); - goto failure; - } - if (ng->num_samples_per_frame != NVAFX_FRAME_SIZE) { - do_log(LOG_ERROR, - "The number of samples per frame has changed from 480 (= 10 ms) ==> update code"); - goto failure; - } - } - ng->nvafx_initialized = true; - ng->nvafx_loading = false; - pthread_mutex_unlock(&nvafx_initializer_mutex); - pthread_mutex_unlock(&ng->nvafx_mutex); - return NULL; - -failure: - ng->use_nvafx = false; - pthread_mutex_unlock(&nvafx_initializer_mutex); - pthread_mutex_unlock(&ng->nvafx_mutex); - return NULL; -} -#endif - static inline void alloc_channel(struct noise_suppress_data *ng, uint32_t sample_rate, size_t channel, size_t frames) @@ -421,25 +218,7 @@ static inline enum speaker_layout convert_speaker_layout(uint8_t channels) return SPEAKERS_UNKNOWN; } } -#ifdef LIBNVAFX_ENABLED -static void set_model(void *data, const char *method) -{ - struct noise_suppress_data *ng = data; - const char *file; - if (strcmp(NVAFX_EFFECT_DEREVERB, method) == 0) - file = NVAFX_EFFECT_DEREVERB_MODEL; - else if (strcmp(NVAFX_EFFECT_DEREVERB_DENOISER, method) == 0) - file = NVAFX_EFFECT_DEREVERB_DENOISER_MODEL; - else - file = NVAFX_EFFECT_DENOISER_MODEL; - size_t size = strlen(ng->sdk_path) + strlen(file) + 1; - char *buffer = (char *)bmalloc(size); - - strcpy(buffer, ng->sdk_path); - strcat(buffer, file); - ng->model = buffer; -} -#endif + static void noise_suppress_update(void *data, obs_data_t *s) { struct noise_suppress_data *ng = data; @@ -453,79 +232,14 @@ static void noise_suppress_update(void *data, obs_data_t *s) ng->latency = 1000000000LL / (1000 / BUFFER_SIZE_MSEC); ng->use_rnnoise = strcmp(method, S_METHOD_RNN) == 0; - bool nvafx_requested = - strcmp(method, S_METHOD_NVAFX_DENOISER) == 0 || - strcmp(method, S_METHOD_NVAFX_DEREVERB) == 0 || - strcmp(method, S_METHOD_NVAFX_DEREVERB_DENOISER) == 0; -#ifdef LIBNVAFX_ENABLED - if (nvafx_requested && ng->nvafx_enabled) - set_model(ng, method); - float intensity = (float)obs_data_get_double(s, S_NVAFX_INTENSITY); - if (ng->use_nvafx && ng->nvafx_initialized) { - if (intensity != ng->intensity_ratio && - (strcmp(ng->fx, method) == 0)) { - NvAFX_Status err; - ng->intensity_ratio = intensity; - pthread_mutex_lock(&ng->nvafx_mutex); - for (size_t i = 0; i < ng->channels; i++) { - err = NvAFX_SetFloat( - ng->handle[i], - NVAFX_PARAM_INTENSITY_RATIO, - ng->intensity_ratio); - if (err != NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "NvAFX_SetFloat(Intensity Ratio: %f) failed, error %i", - ng->intensity_ratio, err); - ng->use_nvafx = false; - } - } - pthread_mutex_unlock(&ng->nvafx_mutex); - } - if ((strcmp(ng->fx, method) != 0)) { - pthread_mutex_lock(&ng->nvafx_mutex); - bfree((void *)ng->fx); - ng->fx = bstrdup(method); - ng->intensity_ratio = intensity; - set_model(ng, method); - os_atomic_set_bool(&ng->reinit_done, false); - for (int i = 0; i < (int)ng->channels; i++) { - /* Destroy previous FX */ - if (NvAFX_DestroyEffect(ng->handle[i]) != - NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "FX failed to be destroyed."); - ng->use_nvafx = false; - } else - ng->handle[i] = NULL; - } - if (ng->use_nvafx) { - nvafx_initialize_internal(data); - } - pthread_mutex_unlock(&ng->nvafx_mutex); - } - } else { - ng->fx = bstrdup(method); - } - -#endif - ng->use_nvafx = ng->nvafx_enabled && nvafx_requested; - /* Process 10 millisecond segments to keep latency low. */ /* Also RNNoise only supports buffers of this exact size. */ - /* At 48kHz, NVAFX processes 480 samples which corresponds to 10 ms.*/ ng->frames = frames; ng->channels = channels; -#ifdef LIBNVAFX_ENABLED - -#endif /* Ignore if already allocated */ #if defined(LIBSPEEXDSP_ENABLED) - if (!ng->use_rnnoise && !ng->use_nvafx && ng->spx_states[0]) - return; -#endif -#ifdef LIBNVAFX_ENABLED - if (ng->use_nvafx && (ng->nvafx_initialized || ng->nvafx_loading)) + if (!ng->use_rnnoise && ng->spx_states[0]) return; #endif #ifdef LIBRNNOISE_ENABLED @@ -541,10 +255,6 @@ static void noise_suppress_update(void *data, obs_data_t *s) #ifdef LIBRNNOISE_ENABLED ng->rnn_segment_buffers[0] = bmalloc(RNNOISE_FRAME_SIZE * channels * sizeof(float)); -#endif -#ifdef LIBNVAFX_ENABLED - ng->nvafx_segment_buffers[0] = - bmalloc(NVAFX_FRAME_SIZE * channels * sizeof(float)); #endif for (size_t c = 1; c < channels; ++c) { ng->copy_buffers[c] = ng->copy_buffers[c - 1] + frames; @@ -555,20 +265,8 @@ static void noise_suppress_update(void *data, obs_data_t *s) #ifdef LIBRNNOISE_ENABLED ng->rnn_segment_buffers[c] = ng->rnn_segment_buffers[c - 1] + RNNOISE_FRAME_SIZE; -#endif -#ifdef LIBNVAFX_ENABLED - ng->nvafx_segment_buffers[c] = - ng->nvafx_segment_buffers[c - 1] + NVAFX_FRAME_SIZE; #endif } - -#ifdef LIBNVAFX_ENABLED - if (!ng->nvafx_initialized && ng->use_nvafx && !ng->nvafx_loading) { - ng->intensity_ratio = intensity; - ng->nvafx_loading = true; - pthread_create(&ng->nvafx_thread, NULL, nvafx_initialize, ng); - } -#endif for (size_t i = 0; i < channels; i++) alloc_channel(ng, sample_rate, i, frames); @@ -590,168 +288,7 @@ static void noise_suppress_update(void *data, obs_data_t *s) ng->rnn_resampler_back = audio_resampler_create(&src, &dst); } #endif -#ifdef LIBNVAFX_ENABLED - if (sample_rate == NVAFX_SAMPLE_RATE) { - ng->nvafx_resampler = NULL; - ng->nvafx_resampler_back = NULL; - } else { - struct resample_info src, dst; - src.samples_per_sec = sample_rate; - src.format = AUDIO_FORMAT_FLOAT_PLANAR; - src.speakers = convert_speaker_layout((uint8_t)channels); - - dst.samples_per_sec = NVAFX_SAMPLE_RATE; - dst.format = AUDIO_FORMAT_FLOAT_PLANAR; - dst.speakers = convert_speaker_layout((uint8_t)channels); - - ng->nvafx_resampler = audio_resampler_create(&dst, &src); - ng->nvafx_resampler_back = audio_resampler_create(&src, &dst); - } -#endif -} - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4706) -#endif -bool load_nvafx(void) -{ -#ifdef LIBNVAFX_ENABLED - unsigned int version = get_lib_version(); - uint8_t major = (version >> 24) & 0xff; - uint8_t minor = (version >> 16) & 0x00ff; - uint8_t build = (version >> 8) & 0x0000ff; - uint8_t revision = (version >> 0) & 0x000000ff; - if (version) { - blog(LOG_INFO, - "[noise suppress]: NVIDIA AUDIO FX version: %i.%i.%i.%i", - major, minor, build, revision); - if (version < MIN_AFX_SDK_VERSION) { - blog(LOG_INFO, - "[noise suppress]: NVIDIA AUDIO Effects SDK is outdated. Please update both audio & video SDK."); - } - } - if (!load_lib()) { - blog(LOG_INFO, - "[noise suppress]: NVIDIA denoiser disabled, redistributable not found or could not be loaded."); - return false; - } - - nvafx_initializer_mutex_initialized = - pthread_mutex_init(&nvafx_initializer_mutex, NULL) == 0; - -#define LOAD_SYM_FROM_LIB(sym, lib, dll) \ - if (!(sym = (sym##_t)GetProcAddress(lib, #sym))) { \ - DWORD err = GetLastError(); \ - printf("[noise suppress]: Couldn't load " #sym " from " dll \ - ": %lu (0x%lx)", \ - err, err); \ - goto unload_everything; \ - } - -#define LOAD_SYM(sym) LOAD_SYM_FROM_LIB(sym, nv_audiofx, "NVAudioEffects.dll") - LOAD_SYM(NvAFX_GetEffectList); - LOAD_SYM(NvAFX_CreateEffect); - LOAD_SYM(NvAFX_CreateChainedEffect); - LOAD_SYM(NvAFX_DestroyEffect); - LOAD_SYM(NvAFX_SetU32); - LOAD_SYM(NvAFX_SetU32List); - LOAD_SYM(NvAFX_SetString); - LOAD_SYM(NvAFX_SetStringList); - LOAD_SYM(NvAFX_SetFloat); - LOAD_SYM(NvAFX_SetFloatList); - LOAD_SYM(NvAFX_GetU32); - LOAD_SYM(NvAFX_GetString); - LOAD_SYM(NvAFX_GetStringList); - LOAD_SYM(NvAFX_GetFloat); - LOAD_SYM(NvAFX_GetFloatList); - LOAD_SYM(NvAFX_Load); - LOAD_SYM(NvAFX_GetSupportedDevices); - LOAD_SYM(NvAFX_Run); - LOAD_SYM(NvAFX_Reset); -#undef LOAD_SYM -#define LOAD_SYM(sym) LOAD_SYM_FROM_LIB(sym, nv_cuda, "nvcuda.dll") - LOAD_SYM(cuCtxGetCurrent); - LOAD_SYM(cuCtxPopCurrent); - LOAD_SYM(cuInit); -#undef LOAD_SYM - - NvAFX_Status err; - CUresult cudaerr; - - NvAFX_Handle h = NULL; - - cudaerr = cuInit(0); - if (cudaerr != CUDA_SUCCESS) { - goto cuda_errors; - } - CUcontext old = {0}; - CUcontext curr = {0}; - cudaerr = cuCtxGetCurrent(&old); - if (cudaerr != CUDA_SUCCESS) { - goto cuda_errors; - } - - err = NvAFX_CreateEffect(NVAFX_EFFECT_DENOISER, &h); - cudaerr = cuCtxGetCurrent(&curr); - if (cudaerr != CUDA_SUCCESS) { - goto cuda_errors; - } - - if (curr != old) { - cudaerr = cuCtxPopCurrent(NULL); - if (cudaerr != CUDA_SUCCESS) - goto cuda_errors; - } - - if (err != NVAFX_STATUS_SUCCESS) { - if (err == NVAFX_STATUS_GPU_UNSUPPORTED) { - blog(LOG_INFO, - "[noise suppress]: NVIDIA AUDIO FX disabled: unsupported GPU"); - } else { - blog(LOG_ERROR, - "[noise suppress]: NVIDIA AUDIO FX disabled, error %i", - err); - } - goto unload_everything; - } - - err = NvAFX_DestroyEffect(h); - if (err != NVAFX_STATUS_SUCCESS) { - blog(LOG_ERROR, - "[noise suppress]: NVIDIA AUDIO FX disabled, error %i", - err); - goto unload_everything; - } - - nvafx_loaded = true; - blog(LOG_INFO, "[noise suppress]: NVIDIA AUDIO FX enabled"); - return true; - -cuda_errors: - blog(LOG_ERROR, - "[noise suppress]: NVIDIA AUDIO FX disabled, CUDA error %i", - cudaerr); -unload_everything: - release_lib(); -#endif - return false; } -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#ifdef LIBNVAFX_ENABLED -void unload_nvafx(void) -{ - release_lib(); - - if (nvafx_initializer_mutex_initialized) { - pthread_mutex_destroy(&nvafx_initializer_mutex); - nvafx_initializer_mutex_initialized = false; - } -} -#endif static void *noise_suppress_create(obs_data_t *settings, obs_source_t *filter) { @@ -759,25 +296,31 @@ static void *noise_suppress_create(obs_data_t *settings, obs_source_t *filter) bzalloc(sizeof(struct noise_suppress_data)); ng->context = filter; - + ng->nvafx_enabled = false; + ng->nvafx_migrated = false; #ifdef LIBNVAFX_ENABLED - char sdk_path[MAX_PATH]; - - if (!nvafx_get_sdk_path(sdk_path, sizeof(sdk_path))) { - ng->nvafx_enabled = false; - do_log(LOG_ERROR, "NVAFX redist is not installed."); - } else { - size_t size = sizeof(sdk_path) + 1; - ng->sdk_path = bmalloc(size); - strcpy(ng->sdk_path, sdk_path); - ng->nvafx_enabled = true; - ng->nvafx_initialized = false; - ng->nvafx_loading = false; - ng->fx = NULL; - - pthread_mutex_init(&ng->nvafx_mutex, NULL); - - info("NVAFX SDK redist path was found here %s", sdk_path); + ng->migrated_filter = NULL; + // If a NVAFX entry is detected, create a new instance of NVAFX filter. + const char *method = obs_data_get_string(settings, S_METHOD); + ng->nvafx_enabled = strcmp(method, S_METHOD_NVAFX_DENOISER) == 0 || + strcmp(method, S_METHOD_NVAFX_DEREVERB) == 0 || + strcmp(method, S_METHOD_NVAFX_DEREVERB_DENOISER) == + 0; + if (ng->nvafx_enabled) { + const char *str1 = obs_source_get_name(filter); + char *str2 = "_ported"; + char *new_name = + (char *)malloc(1 + strlen(str1) + strlen(str2)); + strcpy(new_name, str1); + strcat(new_name, str2); + obs_data_t *new_settings = obs_data_create(); + obs_data_set_string(new_settings, S_METHOD, method); + double intensity = + obs_data_get_double(settings, S_NVAFX_INTENSITY); + obs_data_set_double(new_settings, S_NVAFX_INTENSITY, intensity); + ng->migrated_filter = obs_source_create( + "nvidia_audiofx_filter", new_name, new_settings, NULL); + obs_data_release(new_settings); } #endif noise_suppress_update(ng, settings); @@ -898,115 +441,10 @@ static inline void process_rnnoise(struct noise_suppress_data *ng) #endif } -static inline void process_nvafx(struct noise_suppress_data *ng) -{ -#ifdef LIBNVAFX_ENABLED - if (nvafx_loaded && ng->use_nvafx && ng->nvafx_initialized) { - /* Resample if necessary */ - if (ng->nvafx_resampler) { - float *output[MAX_PREPROC_CHANNELS]; - uint32_t out_frames; - uint64_t ts_offset; - audio_resampler_resample( - ng->nvafx_resampler, (uint8_t **)output, - &out_frames, &ts_offset, - (const uint8_t **)ng->copy_buffers, - (uint32_t)ng->frames); - - for (size_t i = 0; i < ng->channels; i++) { - for (ssize_t j = 0, k = (ssize_t)out_frames - - NVAFX_FRAME_SIZE; - j < NVAFX_FRAME_SIZE; ++j, ++k) { - if (k >= 0) { - ng->nvafx_segment_buffers[i][j] = - output[i][k]; - } else { - ng->nvafx_segment_buffers[i][j] = - 0; - } - } - } - } else { - for (size_t i = 0; i < ng->channels; i++) { - for (size_t j = 0; j < NVAFX_FRAME_SIZE; ++j) { - ng->nvafx_segment_buffers[i][j] = - ng->copy_buffers[i][j]; - } - } - } - - /* Execute */ - size_t runs = ng->has_mono_src ? 1 : ng->channels; - if (ng->reinit_done) { - pthread_mutex_lock(&ng->nvafx_mutex); - for (size_t i = 0; i < runs; i++) { - NvAFX_Status err = - NvAFX_Run(ng->handle[i], - &ng->nvafx_segment_buffers[i], - &ng->nvafx_segment_buffers[i], - ng->num_samples_per_frame, - ng->num_channels); - if (err != NVAFX_STATUS_SUCCESS) { - if (err == NVAFX_STATUS_FAILED) { - do_log(LOG_DEBUG, - "NvAFX_Run() failed, error NVAFX_STATUS_FAILED.\n" - "This can occur when changing the FX and is not consequential."); - os_atomic_set_bool( - &ng->reinit_done, - false); // stop all processing; this will be reset at new init - } else { - do_log(LOG_ERROR, - "NvAFX_Run() failed, error %i.\n", - err); - } - } - } - pthread_mutex_unlock(&ng->nvafx_mutex); - } - if (ng->has_mono_src) { - memcpy(ng->nvafx_segment_buffers[1], - ng->nvafx_segment_buffers[0], - NVAFX_FRAME_SIZE * sizeof(float)); - } - /* Revert signal level adjustment, resample back if necessary */ - if (ng->nvafx_resampler) { - float *output[MAX_PREPROC_CHANNELS]; - uint32_t out_frames; - uint64_t ts_offset; - audio_resampler_resample( - ng->nvafx_resampler_back, (uint8_t **)output, - &out_frames, &ts_offset, - (const uint8_t **)ng->nvafx_segment_buffers, - NVAFX_FRAME_SIZE); - - for (size_t i = 0; i < ng->channels; i++) { - for (ssize_t j = 0, k = (ssize_t)out_frames - - ng->frames; - j < (ssize_t)ng->frames; ++j, ++k) { - if (k >= 0) { - ng->copy_buffers[i][j] = - output[i][k]; - } else { - ng->copy_buffers[i][j] = 0; - } - } - } - } else { - for (size_t i = 0; i < ng->channels; i++) { - for (size_t j = 0; j < NVAFX_FRAME_SIZE; ++j) { - ng->copy_buffers[i][j] = - ng->nvafx_segment_buffers[i][j]; - } - } - } - } -#else - UNUSED_PARAMETER(ng); -#endif -} - static inline void process(struct noise_suppress_data *ng) { + if (ng->nvafx_enabled) + return; /* Pop from input deque */ for (size_t i = 0; i < ng->channels; i++) deque_pop_front(&ng->input_buffers[i], ng->copy_buffers[i], @@ -1014,10 +452,6 @@ static inline void process(struct noise_suppress_data *ng) if (ng->use_rnnoise) { process_rnnoise(ng); - } else if (ng->use_nvafx) { - if (nvafx_loaded) { - process_nvafx(ng); - } } else { process_speexdsp(ng); } @@ -1058,7 +492,18 @@ noise_suppress_filter_audio(void *data, struct obs_audio_data *audio) obs_source_t *parent = obs_filter_get_parent(ng->context); enum speaker_layout layout = obs_source_get_speaker_layout(parent); ng->has_mono_src = layout == SPEAKERS_MONO && ng->channels == 2; - +#ifdef LIBNVAFX_ENABLED + /* Migrate nvafx to new filter. */ + if (ng->nvafx_enabled) { + if (!ng->nvafx_migrated) { + obs_source_filter_add(parent, ng->migrated_filter); + obs_source_set_enabled(ng->migrated_filter, true); + obs_source_filter_remove(parent, ng->context); + ng->nvafx_migrated = true; + } + return audio; + } +#endif #ifdef LIBSPEEXDSP_ENABLED if (!ng->spx_states[0]) return audio; @@ -1200,6 +645,9 @@ static obs_properties_t *noise_suppress_properties(void *data) obs_property_list_add_string( method, TEXT_METHOD_NVAFX_DEREVERB_DENOISER, S_METHOD_NVAFX_DEREVERB_DENOISER); + obs_property_list_item_disable(method, 2, true); + obs_property_list_item_disable(method, 3, true); + obs_property_list_item_disable(method, 4, true); } #endif @@ -1219,15 +667,14 @@ static obs_properties_t *noise_suppress_properties(void *data) obs_properties_add_float_slider(ppts, S_NVAFX_INTENSITY, TEXT_NVAFX_INTENSITY, 0.0f, 1.0f, 0.01f); - } - unsigned int version = get_lib_version(); - if (version && version < MIN_AFX_SDK_VERSION) { - obs_property_t *warning = obs_properties_add_text( - ppts, "deprecation", NULL, OBS_TEXT_INFO); - obs_property_text_set_info_type(warning, OBS_TEXT_INFO_WARNING); + obs_property_t *warning2 = obs_properties_add_text( + ppts, "deprecation2", NULL, OBS_TEXT_INFO); + obs_property_text_set_info_type(warning2, + OBS_TEXT_INFO_WARNING); obs_property_set_long_description( - warning, TEXT_METHOD_NVAFX_DEPRECATION); + warning2, TEXT_METHOD_NVAFX_DEPRECATION2); } + #if defined(LIBRNNOISE_ENABLED) && defined(LIBSPEEXDSP_ENABLED) if (!nvafx_loaded) { obs_property_list_item_disable(method, 2, true); diff --git a/plugins/obs-filters/obs-filters.c b/plugins/obs-filters/obs-filters.c index 7966f6788f9c33..3d4244895a616f 100644 --- a/plugins/obs-filters/obs-filters.c +++ b/plugins/obs-filters/obs-filters.c @@ -29,8 +29,6 @@ extern struct obs_source_info async_delay_filter; #if defined(HAS_NOISEREDUCTION) extern struct obs_source_info noise_suppress_filter; extern struct obs_source_info noise_suppress_filter_v2; -extern bool load_nvafx(void); -extern void unload_nvafx(void); #endif extern struct obs_source_info invert_polarity_filter; extern struct obs_source_info noise_gate_filter; @@ -40,11 +38,6 @@ extern struct obs_source_info expander_filter; extern struct obs_source_info upward_compressor_filter; extern struct obs_source_info luma_key_filter; extern struct obs_source_info luma_key_filter_v2; -#ifdef LIBNVVFX_ENABLED -extern struct obs_source_info nvidia_greenscreen_filter_info; -extern bool load_nvvfx(void); -extern void unload_nvvfx(void); -#endif bool obs_module_load(void) { @@ -68,10 +61,6 @@ bool obs_module_load(void) obs_register_source(&chroma_key_filter_v2); obs_register_source(&async_delay_filter); #if defined(HAS_NOISEREDUCTION) -#ifdef LIBNVAFX_ENABLED - /* load nvidia audio fx dll */ - load_nvafx(); -#endif obs_register_source(&noise_suppress_filter); obs_register_source(&noise_suppress_filter_v2); #endif @@ -83,22 +72,5 @@ bool obs_module_load(void) obs_register_source(&upward_compressor_filter); obs_register_source(&luma_key_filter); obs_register_source(&luma_key_filter_v2); -#ifdef LIBNVVFX_ENABLED - obs_enter_graphics(); - const bool direct3d = gs_get_device_type() == GS_DEVICE_DIRECT3D_11; - obs_leave_graphics(); - if (direct3d && load_nvvfx()) - obs_register_source(&nvidia_greenscreen_filter_info); -#endif return true; } - -void obs_module_unload(void) -{ -#ifdef LIBNVAFX_ENABLED - unload_nvafx(); -#endif -#ifdef LIBNVVFX_ENABLED - unload_nvvfx(); -#endif -}