0
0
mirror of https://github.com/obsproject/obs-studio.git synced 2024-09-19 20:32:15 +02:00

obs-filters: Move NVIDIA filters in their own project

This commit does the following:
1. Factor out NVIDIA Audio Effects from Noise Suppression filter.
2. Move NVIDIA Audio Effects to a new filter in a new nv-filters
project.
3. Migrate Noise Suppression filter settings to the new filter when
NVIDIA Audio effects were used.
4. Migrate NVIDIA AI Greenscreen to the new nv-filters project for
easier maintainance of all NVIDIA Maxine effects.

Context:
 Currently, the three NVIDIA Audio Effects (noise suppression, room
echo removal, noise suppression + room echo removal combined) are part
of the noise suppression filter.
Historically, it's because a lot of code was shared between speex,
rnnoise & NVIDIA noise suppression.
But the NVIDIA code has become bulkier & cumbersome due to:
- addition of other effects;
- addition of a deferred loading thread.
The factorisation makes the code very difficult to maintain for
(un)readability reasons.
This will make it easier to add other audio effects, should we wish to.
Developers life will be easier too when debugging.
The code has been reorganized and comments added.
I also added a mutex in the process_fx function to avoid a crash when
swapping effects.

Signed-off-by: pkv <pkv@obsproject.com>
This commit is contained in:
pkv 2023-05-18 22:38:49 +02:00 committed by Ryan Foster
parent 37b40184a0
commit 00c495b203
17 changed files with 1745 additions and 664 deletions

View File

@ -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)

View File

@ -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 $<$<PLATFORM_ID:Windows>:OBS::w32-pthreads>)
# cmake-format: off
set_target_properties_obs(nv-filters PROPERTIES FOLDER plugins PREFIX "")
# cmake-format: on
endif()

View File

@ -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

View File

@ -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);
}

View File

@ -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)."

View File

@ -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);
}
}

View File

@ -0,0 +1,46 @@
#include <obs-module.h>
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
}

View File

@ -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)

View File

@ -0,0 +1,339 @@
#pragma once
#include <Windows.h>
#include <stdio.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <util/platform.h>
#include <util/windows/win-version.h>
#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

View File

@ -0,0 +1,936 @@
#include <stdint.h>
#include <inttypes.h>
#include <obs-module.h>
#include <util/deque.h>
#include <util/threading.h>
#include <media-io/audio-resampler.h>
#include "nvafx-load.h"
#include <pthread.h>
/* -------------------------------------------------------- */
#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,
};

View File

@ -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 @@ unload_everything:
}
#ifdef LIBNVVFX_ENABLED
void unload_nvvfx(void)
void unload_nvidia_vfx(void)
{
release_nv_vfx();
}

View File

@ -7,6 +7,7 @@
#include <util/platform.h>
#include <dxgitype.h>
#include <util/windows/win-version.h>
#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;

View File

@ -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()

View File

@ -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()

View File

@ -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"

View File

@ -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,194 +288,39 @@ 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)
{
struct noise_suppress_data *ng =
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);

View File

@ -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
}