Browse Source

obs-filters: Add RTX denoiser

This adds RTX denoiser filter to be used with RTX GPUs.
(from Nvidia Audio Effects SDK)
pkv 5 years ago
parent
commit
54892b27db

+ 20 - 2
plugins/obs-filters/CMakeLists.txt

@@ -28,9 +28,26 @@ if(NOT LIBRNNOISE_FOUND)
 	set(LIBRNNOISE_FOUND TRUE)
 endif()
 
-if(LIBSPEEXDSP_FOUND OR LIBRNNOISE_FOUND)
+if (WIN32)
+	if(DISABLE_NVAFX)
+		message(STATUS "NVidia Audio FX support disabled")
+		set(LIBNVAFX_FOUND FALSE)
+	else()
+		message(STATUS "NVidia Audio FX support enabled; requires redist to be installed by end-user")
+		add_definitions(-DLIBNVAFX_ENABLED)
+		set(LIBNVAFX_FOUND TRUE)
+	endif()
+endif()
+
+if(LIBSPEEXDSP_FOUND OR LIBRNNOISE_FOUND OR LIBNVAFX_FOUND)
 	set(obs-filters_NOISEREDUCTION_SOURCES
 		noise-suppress-filter.c)
+	if(LIBNVAFX_FOUND)
+		set(obs-filters_NOISEREDUCTION_HEADERS
+			nvafx-load.h)
+	else()
+		set(obs-filters_NOISEREDUCTION_HEADERS)
+	endif()
 	set(obs-filters_NOISEREDUCTION_LIBRARIES
 		${LIBSPEEXDSP_LIBRARIES} ${LIBRNNOISE_LIBRARIES})
 	set(NOISEREDUCTION_ENABLED TRUE)
@@ -83,7 +100,8 @@ add_library(obs-filters MODULE
 	${rnnoise_SOURCES}
 	${obs-filters_SOURCES}
 	${obs-filters_config_HEADERS}
-	${obs-filters_NOISEREDUCTION_SOURCES})
+	${obs-filters_NOISEREDUCTION_SOURCES}
+	${obs-filters_NOISEREDUCTION_HEADERS})
 target_link_libraries(obs-filters
 	libobs
 	${obs-filters_PLATFORM_DEPS}

+ 2 - 0
plugins/obs-filters/data/locale/en-US.ini

@@ -69,9 +69,11 @@ ScaleFiltering.Bicubic="Bicubic"
 ScaleFiltering.Lanczos="Lanczos"
 ScaleFiltering.Area="Area"
 NoiseSuppress.SuppressLevel="Suppression Level"
+NoiseSuppress.Intensity="Suppression Intensity"
 NoiseSuppress.Method="Method"
 NoiseSuppress.Method.Speex="Speex (lower CPU usage)"
 NoiseSuppress.Method.RNNoise="RNNoise (higher quality)"
+NoiseSuppress.Method.nvafx="NVIDIA Noise Removal (highest quality, requires RTX GPU)"
 Saturation="Saturation"
 HueShift="Hue Shift"
 Amount="Amount"

+ 352 - 3
plugins/obs-filters/noise-suppress-filter.c

@@ -16,6 +16,11 @@
 #include <media-io/audio-resampler.h>
 #endif
 
+bool nvafx_loaded = false;
+#ifdef LIBNVAFX_ENABLED
+#include "nvafx-load.h"
+#endif
+
 /* -------------------------------------------------------- */
 
 #define do_log(level, format, ...)                    \
@@ -34,15 +39,19 @@
 /* -------------------------------------------------------- */
 
 #define S_SUPPRESS_LEVEL "suppress_level"
+#define S_NVAFX_INTENSITY "intensity"
 #define S_METHOD "method"
 #define S_METHOD_SPEEX "speex"
 #define S_METHOD_RNN "rnnoise"
+#define S_METHOD_NVAFX "nvafx"
 
 #define MT_ obs_module_text
 #define TEXT_SUPPRESS_LEVEL MT_("NoiseSuppress.SuppressLevel")
+#define TEXT_NVAFX_INTENSITY MT_("NoiseSuppress.Intensity")
 #define TEXT_METHOD MT_("NoiseSuppress.Method")
 #define TEXT_METHOD_SPEEX MT_("NoiseSuppress.Method.Speex")
 #define TEXT_METHOD_RNN MT_("NoiseSuppress.Method.RNNoise")
+#define TEXT_METHOD_NVAFX MT_("NoiseSuppress.Method.nvafx")
 
 #define MAX_PREPROC_CHANNELS 8
 
@@ -50,6 +59,11 @@
 #define RNNOISE_SAMPLE_RATE 48000
 #define RNNOISE_FRAME_SIZE 480
 
+/* nvafx constants, these can't be changed */
+#define NVAFX_SAMPLE_RATE 48000
+#define NVAFX_FRAME_SIZE \
+	480 /* the sdk does not explicitly set this as a constant though it relies on it*/
+
 /* If the following constant changes, RNNoise breaks */
 #define BUFFER_SIZE_MSEC 10
 
@@ -70,6 +84,8 @@ struct noise_suppress_data {
 	struct circlebuf output_buffers[MAX_PREPROC_CHANNELS];
 
 	bool use_rnnoise;
+	bool use_nvafx;
+	bool nvafx_enabled;
 
 #ifdef LIBSPEEXDSP_ENABLED
 	/* Speex preprocessor state */
@@ -85,6 +101,20 @@ struct noise_suppress_data {
 	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;
+	const char *sdk_path;
+	const char *model;
+
+	/* Resampler */
+	audio_resampler_t *nvafx_resampler;
+	audio_resampler_t *nvafx_resampler_back;
+#endif
 	/* PCM buffers */
 	float *copy_buffers[MAX_PREPROC_CHANNELS];
 #ifdef LIBSPEEXDSP_ENABLED
@@ -93,6 +123,9 @@ 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;
@@ -125,6 +158,14 @@ static void noise_suppress_destroy(void *data)
 #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
 		circlebuf_free(&ng->input_buffers[i]);
 		circlebuf_free(&ng->output_buffers[i]);
@@ -141,6 +182,15 @@ 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);
+#endif
 
 	bfree(ng->copy_buffers[0]);
 	circlebuf_free(&ng->info_buffer);
@@ -158,6 +208,80 @@ static inline void alloc_channel(struct noise_suppress_data *ng,
 #endif
 #ifdef LIBRNNOISE_ENABLED
 	ng->rnn_states[channel] = rnnoise_create(NULL);
+#endif
+#ifdef LIBNVAFX_ENABLED
+	int err;
+	if (!ng->handle[0] && ng->use_nvafx && nvafx_loaded) {
+		ng->sample_rate = NVAFX_SAMPLE_RATE;
+
+		for (int i = 0; i < MAX_PREPROC_CHANNELS; i++) {
+			err = NvAFX_CreateEffect(NVAFX_EFFECT_DENOISER,
+						 &ng->handle[i]);
+			if (err != NVAFX_STATUS_SUCCESS) {
+				do_log(LOG_ERROR,
+				       "NvAFX_CreateEffect() failed, error %i",
+				       err);
+				ng->use_nvafx = false;
+			}
+			err = NvAFX_SetU32(ng->handle[i],
+					   NVAFX_PARAM_DENOISER_SAMPLE_RATE,
+					   ng->sample_rate);
+			if (err != NVAFX_STATUS_SUCCESS) {
+				do_log(LOG_ERROR,
+				       "NvAFX_SetU32(Sample Rate: %d) failed, error %i",
+				       ng->sample_rate, err);
+				ng->use_nvafx = false;
+			}
+			err = NvAFX_SetString(ng->handle[i],
+					      NVAFX_PARAM_DENOISER_MODEL_PATH,
+					      ng->model);
+			if (err != NVAFX_STATUS_SUCCESS) {
+				do_log(LOG_ERROR,
+				       "NvAFX_SetString() failed, error %i",
+				       err);
+				ng->use_nvafx = false;
+			}
+			err = NvAFX_Load(ng->handle[i]);
+			if (err != NVAFX_STATUS_SUCCESS) {
+				do_log(LOG_ERROR,
+				       "NvAFX_Load() failed with error %i",
+				       err);
+				ng->use_nvafx = false;
+			}
+		}
+		if (ng->use_nvafx) {
+			err = NvAFX_GetU32(ng->handle[0],
+					   NVAFX_PARAM_DENOISER_NUM_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);
+				ng->use_nvafx = false;
+			}
+			if (ng->num_channels != 1) {
+				do_log(LOG_ERROR,
+				       "The number of channels is not 1 in the sdk any more ==> update code");
+				ng->use_nvafx = false;
+			}
+			NvAFX_Status err = NvAFX_GetU32(
+				ng->handle[0],
+				NVAFX_PARAM_DENOISER_NUM_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);
+				ng->use_nvafx = false;
+			}
+			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");
+				ng->use_nvafx = false;
+			}
+		}
+	}
+
 #endif
 	circlebuf_reserve(&ng->input_buffers[channel], frames * sizeof(float));
 	circlebuf_reserve(&ng->output_buffers[channel], frames * sizeof(float));
@@ -200,8 +324,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;
 
+	ng->use_nvafx = ng->nvafx_enabled &&
+			strcmp(method, S_METHOD_NVAFX) == 0;
+#ifdef LIBNVAFX_ENABLED
+	ng->intensity_ratio = (float)obs_data_get_double(s, S_NVAFX_INTENSITY);
+#endif
 	/* 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;
 
@@ -209,10 +339,15 @@ static void noise_suppress_update(void *data, obs_data_t *s)
 #if defined(LIBSPEEXDSP_ENABLED)
 	if (ng->spx_states[0])
 		return;
-#else
+#endif
+#ifdef LIBRNNOISE_ENABLED
 	if (ng->rnn_states[0])
 		return;
 #endif
+#ifdef LIBNVAFX_ENABLED
+	if (ng->handle[0])
+		return;
+#endif
 
 	/* One speex/rnnoise state for each channel (limit 2) */
 	ng->copy_buffers[0] = bmalloc(frames * channels * sizeof(float));
@@ -223,6 +358,10 @@ 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;
@@ -233,6 +372,10 @@ 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
 	}
 
@@ -257,7 +400,74 @@ 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
+	if (!load_lib()) {
+		blog(LOG_INFO,
+		     "[noise suppress: Nvidia RTX denoiser disabled, redistributable not found]");
+		return false;
+	} else {
+		blog(LOG_INFO, "[noise suppress: Nvidia RTX denoiser enabled]");
+	}
+
+#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_DestroyEffect);
+	LOAD_SYM(NvAFX_SetU32);
+	LOAD_SYM(NvAFX_SetString);
+	LOAD_SYM(NvAFX_SetFloat);
+	LOAD_SYM(NvAFX_GetU32);
+	LOAD_SYM(NvAFX_GetString);
+	LOAD_SYM(NvAFX_GetFloat);
+	LOAD_SYM(NvAFX_Load);
+	LOAD_SYM(NvAFX_Run);
+#undef LOAD_SYM
+	nvafx_loaded = true;
+	return true;
+
+unload_everything:
+	release_lib();
+#endif
+	return false;
 }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
 
 static void *noise_suppress_create(obs_data_t *settings, obs_source_t *filter)
 {
@@ -265,6 +475,27 @@ static void *noise_suppress_create(obs_data_t *settings, obs_source_t *filter)
 		bzalloc(sizeof(struct noise_suppress_data));
 
 	ng->context = filter;
+
+#ifdef LIBNVAFX_ENABLED
+	char sdk_path[MAX_PATH];
+
+	if (!nvafx_get_sdk_path(sdk_path)) {
+		ng->nvafx_enabled = false;
+		do_log(LOG_ERROR, "NVAFX redist is not installed.");
+	} else {
+		const char *file = "\\models\\denoiser_48k.trtpkg";
+		size_t size = strlen(sdk_path) + strlen(file) + 1;
+		char *buffer = (char *)bmalloc(size);
+
+		ng->sdk_path = sdk_path;
+		strcpy(buffer, sdk_path);
+		strcat(buffer, file);
+		ng->model = buffer;
+		ng->nvafx_enabled = true;
+
+		info("NVAFX SDK redist path was found here %s", ng->sdk_path);
+	}
+#endif
 	noise_suppress_update(ng, settings);
 	return ng;
 }
@@ -381,6 +612,101 @@ static inline void process_rnnoise(struct noise_suppress_data *ng)
 #endif
 }
 
+static inline void process_nvafx(struct noise_suppress_data *ng)
+{
+	if (nvafx_loaded) {
+#ifdef LIBNVAFX_ENABLED
+		for (int i = 0; i < MAX_PREPROC_CHANNELS; i++) {
+			if (NvAFX_SetFloat(ng->handle[i],
+					   NVAFX_PARAM_DENOISER_INTENSITY_RATIO,
+					   ng->intensity_ratio) !=
+			    NVAFX_STATUS_SUCCESS) {
+				do_log(LOG_ERROR,
+				       "NvAFX_SetFloat(Intensity Ratio: %d) failed",
+				       ng->intensity_ratio);
+				ng->use_nvafx = false;
+			}
+		}
+		/* 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 */
+		for (size_t i = 0; i < ng->channels; 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)
+				do_log(LOG_ERROR, "NvAFX_Run() failed");
+		}
+
+		/* 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)
 {
 	/* Pop from input circlebuf */
@@ -390,6 +716,10 @@ 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);
 	}
@@ -431,7 +761,8 @@ noise_suppress_filter_audio(void *data, struct obs_audio_data *audio)
 #ifdef LIBSPEEXDSP_ENABLED
 	if (!ng->spx_states[0])
 		return audio;
-#else
+#endif
+#ifdef LIBRNNOISE_ENABLED
 	if (!ng->rnn_states[0])
 		return audio;
 #endif
@@ -502,10 +833,14 @@ static bool noise_suppress_method_modified(obs_properties_t *props,
 {
 	obs_property_t *p_suppress_level =
 		obs_properties_get(props, S_SUPPRESS_LEVEL);
+	obs_property_t *p_navfx_intensity =
+		obs_properties_get(props, S_NVAFX_INTENSITY);
 	const char *method = obs_data_get_string(settings, S_METHOD);
 	bool enable_level = strcmp(method, S_METHOD_SPEEX) == 0;
+	bool enable_intensity = strcmp(method, S_METHOD_NVAFX) == 0;
 
 	obs_property_set_visible(p_suppress_level, enable_level);
+	obs_property_set_visible(p_navfx_intensity, enable_intensity);
 
 	UNUSED_PARAMETER(property);
 	return true;
@@ -534,6 +869,7 @@ static void noise_suppress_defaults_v2(obs_data_t *s)
 static obs_properties_t *noise_suppress_properties(void *data)
 {
 	obs_properties_t *ppts = obs_properties_create();
+	struct noise_suppress_data *ng = (struct noise_suppress_data *)data;
 
 #if defined(LIBRNNOISE_ENABLED) && defined(LIBSPEEXDSP_ENABLED)
 	obs_property_t *method = obs_properties_add_list(
@@ -541,7 +877,11 @@ static obs_properties_t *noise_suppress_properties(void *data)
 		OBS_COMBO_FORMAT_STRING);
 	obs_property_list_add_string(method, TEXT_METHOD_SPEEX, S_METHOD_SPEEX);
 	obs_property_list_add_string(method, TEXT_METHOD_RNN, S_METHOD_RNN);
-
+#ifdef LIBNVAFX_ENABLED
+	if (ng->nvafx_enabled)
+		obs_property_list_add_string(method, TEXT_METHOD_NVAFX,
+					     S_METHOD_NVAFX);
+#endif
 	obs_property_set_modified_callback(method,
 					   noise_suppress_method_modified);
 #endif
@@ -554,6 +894,15 @@ static obs_properties_t *noise_suppress_properties(void *data)
 #endif
 
 	UNUSED_PARAMETER(data);
+#ifdef LIBNVAFX_ENABLED
+	obs_property_t *nvafx_slider = obs_properties_add_float_slider(
+		ppts, S_NVAFX_INTENSITY, TEXT_NVAFX_INTENSITY, 0.0f, 1.0f,
+		0.01f);
+	if (!nvafx_loaded) {
+		obs_property_list_item_disable(method, 2, true);
+	}
+
+#endif
 	return ppts;
 }
 

+ 163 - 0
plugins/obs-filters/nvafx-load.h

@@ -0,0 +1,163 @@
+#include <Windows.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <util/platform.h>
+
+#define NVAFX_API __declspec(dllexport)
+
+#ifdef LIBNVAFX_ENABLED
+static HMODULE nv_audiofx = NULL;
+
+/** Denoiser Effect */
+#define NVAFX_EFFECT_DENOISER "denoiser"
+
+/** Parameter selectors */
+
+/** Denoiser parameters. @ref NvAFX_ParameterSelector */
+/** Denoiser filter model path (char*) */
+#define NVAFX_PARAM_DENOISER_MODEL_PATH "denoiser_model_path"
+/** Denoiser sample rate (unsigned int). Currently supported sample rate(s): 48000 */
+#define NVAFX_PARAM_DENOISER_SAMPLE_RATE "sample_rate"
+/** Denoiser number of samples per frame (unsigned int). This is immutable parameter */
+#define NVAFX_PARAM_DENOISER_NUM_SAMPLES_PER_FRAME "num_samples_per_frame"
+/** Denoiser number of channels in I/O (unsigned int). This is immutable parameter */
+#define NVAFX_PARAM_DENOISER_NUM_CHANNELS "num_channels"
+/** Denoiser noise suppression factor (float) */
+#define NVAFX_PARAM_DENOISER_INTENSITY_RATIO "intensity_ratio"
+
+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;
+
+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_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_SetString_t)(NvAFX_Handle effect,
+				       NvAFX_ParameterSelector param_name,
+				       const char *val);
+typedef NvAFX_Status NVAFX_API (*NvAFX_SetFloat_t)(
+	NvAFX_Handle effect, NvAFX_ParameterSelector param_name, float val);
+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_GetFloat_t)(
+	NvAFX_Handle effect, NvAFX_ParameterSelector param_name, float *val);
+typedef NvAFX_Status NVAFX_API (*NvAFX_Load_t)(NvAFX_Handle effect);
+typedef NvAFX_Status NVAFX_API (*NvAFX_Run_t)(NvAFX_Handle effect,
+					      const float **input,
+					      float **output,
+					      unsigned num_samples,
+					      unsigned num_channels);
+
+static NvAFX_GetEffectList_t NvAFX_GetEffectList = NULL;
+static NvAFX_CreateEffect_t NvAFX_CreateEffect = NULL;
+static NvAFX_DestroyEffect_t NvAFX_DestroyEffect = NULL;
+static NvAFX_SetU32_t NvAFX_SetU32 = NULL;
+static NvAFX_SetString_t NvAFX_SetString = NULL;
+static NvAFX_SetFloat_t NvAFX_SetFloat = NULL;
+static NvAFX_GetU32_t NvAFX_GetU32 = NULL;
+static NvAFX_GetString_t NvAFX_GetString = NULL;
+static NvAFX_GetFloat_t NvAFX_GetFloat = NULL;
+static NvAFX_Load_t NvAFX_Load = NULL;
+static NvAFX_Run_t NvAFX_Run = NULL;
+
+void release_lib(void)
+{
+	NvAFX_GetEffectList = NULL;
+	NvAFX_CreateEffect = NULL;
+	NvAFX_DestroyEffect = NULL;
+	NvAFX_SetU32 = NULL;
+	NvAFX_SetString = NULL;
+	NvAFX_SetFloat = NULL;
+	NvAFX_GetU32 = NULL;
+	NvAFX_GetString = NULL;
+	NvAFX_GetFloat = NULL;
+	NvAFX_Load = NULL;
+	NvAFX_Run = NULL;
+	if (nv_audiofx) {
+		FreeLibrary(nv_audiofx);
+		nv_audiofx = NULL;
+	}
+}
+
+static bool nvafx_get_sdk_path(char buffer[MAX_PATH])
+{
+	char value[MAX_PATH];
+	PVOID pvData = value;
+	DWORD BufferSize = 8192;
+	LPDWORD pcbData;
+	LSTATUS status = RegGetValue(
+		HKEY_LOCAL_MACHINE,
+		TEXT("SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"),
+		TEXT("NVAFX_SDK_DIR"), RRF_RT_REG_SZ, NULL, pvData,
+		&BufferSize);
+
+	if (status != ERROR_SUCCESS)
+		return false;
+	else if (!os_wcs_to_utf8((wchar_t *)pvData, 0, buffer, MAX_PATH))
+		return false;
+	return true;
+}
+
+static bool load_lib(void)
+{
+	char buffer[MAX_PATH];
+	if (!nvafx_get_sdk_path(buffer))
+		return false;
+
+	size_t length = strlen(buffer);
+	wchar_t path[MAX_PATH];
+
+	if (!os_utf8_to_wcs(buffer, 0, path, length + 1))
+		return false;
+	SetDllDirectory(path);
+	nv_audiofx = LoadLibrary(L"NVAudioEffects.dll");
+	SetDllDirectory(NULL);
+
+	return !!nv_audiofx;
+}
+#endif

+ 17 - 0
plugins/obs-filters/obs-filters.c

@@ -28,6 +28,8 @@ extern struct obs_source_info async_delay_filter;
 #if NOISEREDUCTION_ENABLED
 extern struct obs_source_info noise_suppress_filter;
 extern struct obs_source_info noise_suppress_filter_v2;
+extern bool load_nvafx();
+extern void release_lib();
 #endif
 extern struct obs_source_info invert_polarity_filter;
 extern struct obs_source_info noise_gate_filter;
@@ -57,6 +59,14 @@ bool obs_module_load(void)
 	obs_register_source(&chroma_key_filter_v2);
 	obs_register_source(&async_delay_filter);
 #if NOISEREDUCTION_ENABLED
+#ifdef LIBNVAFX_ENABLED
+	/* load nvidia audio fx dll */
+	if (!load_nvafx()) {
+		printf("[noise suppress: Unable to load NVAudioEffects.dll.]");
+	} else {
+		printf("[noise suppress: NVAudioEffects.dll loaded.]");
+	}
+#endif
 	obs_register_source(&noise_suppress_filter);
 	obs_register_source(&noise_suppress_filter_v2);
 #endif
@@ -69,3 +79,10 @@ bool obs_module_load(void)
 	obs_register_source(&luma_key_filter_v2);
 	return true;
 }
+
+#ifdef LIBNVAFX_ENABLED
+void obs_module_unload(void)
+{
+	release_lib();
+}
+#endif