obs-filters: Add RTX denoiser

This adds RTX denoiser filter to be used with RTX GPUs.
(from Nvidia Audio Effects SDK)
master
pkv 2020-10-13 00:54:00 +02:00 committed by Jim
parent a609554b89
commit 54892b27db
5 changed files with 554 additions and 5 deletions

View File

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

View File

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

View File

@ -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,14 +400,102 @@ 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)
{
struct noise_suppress_data *ng =
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;
}

View File

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

View File

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