zohann commited on
Commit
2e57aaf
·
verified ·
1 Parent(s): 15487d0

Upload Audio_Effects_SDK/samples/effects_demo/effects_demo.cpp with huggingface_hub

Browse files
Audio_Effects_SDK/samples/effects_demo/effects_demo.cpp ADDED
@@ -0,0 +1,882 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved.
3
+
4
+ NVIDIA CORPORATION and its licensors retain all intellectual property
5
+ and proprietary rights in and to this software, related documentation
6
+ and any modifications thereto. Any use, reproduction, disclosure or
7
+ distribution of this software and related documentation without an express
8
+ license agreement from NVIDIA CORPORATION is strictly prohibited.
9
+ */
10
+ #include <math.h>
11
+
12
+ #include <algorithm>
13
+ #include <chrono>
14
+ #include <cstring>
15
+ #include <iostream>
16
+ #include <iomanip>
17
+ #include <memory>
18
+ #include <set>
19
+ #include <sstream>
20
+ #include <thread>
21
+ #include <tuple>
22
+
23
+ #include "utils/wave_reader/waveReadWrite.hpp"
24
+ #include "utils/ConfigReader.hpp"
25
+
26
+ #include <nvAudioEffects.h>
27
+ #include <map>
28
+
29
+ namespace {
30
+
31
+ const char kConfigEffectVariable[] = "effect";
32
+ const char kConfigSampleRateVariable[] = "sample_rate";
33
+ const char kConfigFileInputVariable[] = "input_wav_list";
34
+ const char kConfigFileInputFarendVariable[] = "input_farend_wav_list";
35
+ const char kConfigFileOutputVariable[] = "output_wav_list";
36
+ const char kConfigFileRTVariable[] = "real_time";
37
+ const char kConfigResetVariable[] = "reset";
38
+ const char kConfigFrameSize[] = "frame_size";
39
+ const char kConfigUseDefaultGpu[] = "use_default_gpu";
40
+ const char kConfigLogTarget[] = "log_target_list";
41
+ const char kConfigLogTargetFile[] = "log_target_file";
42
+ const char kConfigLogLevel[] = "log_level";
43
+ const char kConfigLogTargetFileDefault[] = "/tmp/nvAudioEffects_log.txt";
44
+ const char kConfigFileModelVariable[] = "model";
45
+ const char kConfigIntensityRatioVariable[] = "intensity_ratio";
46
+ const char kConfigVadEnable[] = "enable_vad";
47
+ const char kConfigChainedEffectGpuList[] = "chained_effect_gpu_list";
48
+
49
+ /* allowed sample rates */
50
+ const std::vector<uint32_t> kAllowedSampleRates = { 8000, 16000, 48000 };
51
+
52
+ } // namespace
53
+
54
+ struct StreamData {
55
+ // Wav file name(s) separated by ';'
56
+ std::string input_wav_file;
57
+ // Total number of samples in all input wav files together
58
+ unsigned num_samples = 0;
59
+ // Number of audio samples written to output file
60
+ unsigned already_written = 0;
61
+ // 32-bit float audio samples read from all input wav files
62
+ std::vector<float>* input_wav_samples;
63
+ // (AEC only) 32-bit float audio samples for farend input wav files
64
+ std::vector<float>* input_farend_wav_samples;
65
+ // Wav file name(s) to write the output data
66
+ std::string output_wav_file;
67
+ // Object for wav writing library
68
+ std::vector<std::unique_ptr<CWaveFileWrite>> wav_write;
69
+ // Index of each input file end in the audio data vector
70
+ std::vector<int> file_end_offsets;
71
+ // Index of current output file used as suffix to filename
72
+ int output_file_num;
73
+ };
74
+
75
+ class EffectsDemoApp {
76
+ public:
77
+ bool run(const ConfigReader& config_reader);
78
+
79
+ private:
80
+ // Validate configuration data.
81
+ bool validateConfig(const ConfigReader& config_reader);
82
+ // write to output wav
83
+ bool writeOutputWav(const std::vector<float*>& output_samples, size_t num_samples_per_channel,
84
+ int stream_index);
85
+ // sample rate config
86
+ uint32_t input_sample_rate_ = 0;
87
+ // output sample rate config
88
+ uint32_t output_sample_rate_ = 0;
89
+ // stream data
90
+ std::vector<StreamData> stream_data_;
91
+ // GPU selection flag - by default set to false
92
+ uint32_t use_default_gpu_ = 0;
93
+ // inited from configuration
94
+ bool real_time_ = false;
95
+ // intensity_ratio_ config, lies between 0 (no effect applied) to 1 (fully turned on)
96
+ float intensity_ratio_ = 1.0f;
97
+ // VAD support (denoiser/dereverb+denoiser only)
98
+ bool vad_supported_ = false;
99
+
100
+ unsigned int output_channels_ = 1;
101
+ };
102
+
103
+ bool EffectsDemoApp::validateConfig(const ConfigReader& config_reader) {
104
+ if (config_reader.IsConfigValueAvailable(kConfigEffectVariable) == false) {
105
+ std::cerr << "No " << kConfigEffectVariable << " variable found" << std::endl;
106
+ return false;
107
+ }
108
+
109
+ if (config_reader.IsConfigValueAvailable(kConfigFileModelVariable) == false) {
110
+ std::cerr << "No " << kConfigFileModelVariable << " variable found" << std::endl;
111
+ return false;
112
+ }
113
+
114
+ if (config_reader.IsConfigValueAvailable(kConfigFileInputVariable) == false) {
115
+ std::cerr << "No " << kConfigFileInputVariable << " variable found" << std::endl;
116
+ return false;
117
+ }
118
+
119
+ if (config_reader.IsConfigValueAvailable(kConfigFileOutputVariable) == false) {
120
+ std::cerr << "No " << kConfigFileOutputVariable << " variable found" << std::endl;
121
+ return false;
122
+ }
123
+
124
+ std::string real_time;
125
+ if (config_reader.GetConfigValue(kConfigFileRTVariable, &real_time) == false) {
126
+ std::cerr << "No " << kConfigFileRTVariable << " variable found" << std::endl;
127
+ return false;
128
+ }
129
+
130
+ if (real_time[0] != '0') {
131
+ real_time_ = true;
132
+ }
133
+
134
+ if (config_reader.IsConfigValueAvailable(kConfigResetVariable)) {
135
+ std::cout << "Reset available" << std::endl;
136
+ }
137
+
138
+ std::string use_default_gpu;
139
+ if (config_reader.IsConfigValueAvailable(kConfigUseDefaultGpu) &&
140
+ config_reader.GetConfigValue(kConfigUseDefaultGpu, &use_default_gpu)) {
141
+ use_default_gpu_ = std::strtoul(use_default_gpu.c_str(), nullptr, 0);
142
+ std::cout << "Use default GPU: " << use_default_gpu_ << std::endl;
143
+ }
144
+
145
+ std::string intensity_ratio;
146
+ float intensity_ratio_local;
147
+ if (config_reader.GetConfigValue(kConfigIntensityRatioVariable, &intensity_ratio)) {
148
+ intensity_ratio_local = std::strtof(intensity_ratio.c_str(), nullptr);
149
+ if (intensity_ratio_local < 0.0f || intensity_ratio_local > 1.0f) {
150
+ std::cerr << kConfigIntensityRatioVariable << " not supported" << std::endl;
151
+ return false;
152
+ }
153
+ } else {
154
+ intensity_ratio_local = 1.0f;
155
+ }
156
+
157
+ intensity_ratio_ = intensity_ratio_local;
158
+ std::cout << "intensity ratio is ::" << intensity_ratio_ << "!!" << std::endl;
159
+ return true;
160
+ }
161
+
162
+ bool EffectsDemoApp::writeOutputWav(const std::vector<float*>& output_samples,
163
+ size_t num_samples_per_channel, int stream_index) {
164
+ size_t to_write = num_samples_per_channel;
165
+ size_t max_allowed = stream_data_[stream_index].num_samples *
166
+ (output_sample_rate_/input_sample_rate_);
167
+ size_t already_written = stream_data_[stream_index].already_written;
168
+ size_t can_write = static_cast<size_t>(max_allowed - already_written);
169
+ if (can_write == 0) { return true; }
170
+ size_t will_write = std::min(to_write, can_write);
171
+
172
+ for (int i = 0; i < output_channels_; i++) {
173
+ if (!stream_data_[stream_index].wav_write[i]->writeChunk(output_samples[i],
174
+ will_write * sizeof(float))) {
175
+ std::cerr << "Could not write output to file "
176
+ << stream_data_[stream_index].output_wav_file << std::endl;
177
+ return false;
178
+ }
179
+ }
180
+ stream_data_[stream_index].already_written += will_write;
181
+ return true;
182
+ }
183
+
184
+ void logger_cb(LoggingSeverity level, const char* log, void* userdata) {
185
+ std::cout << "LOG" << '(' << LogSeverityToString(level) << ") " << log << '\n';
186
+ }
187
+
188
+ inline LoggingSeverity StringToLogSeverity(std::string &severity) {
189
+ if ("ERROR" == severity) return LOG_LEVEL_ERROR;
190
+ if ("WARNING" == severity) return LOG_LEVEL_WARNING;
191
+ if ("INFO" == severity) return LOG_LEVEL_INFO;
192
+ return LOG_LEVEL_ERROR;
193
+ }
194
+
195
+ inline LoggingTarget StringToLogTarget(std::string &severity) {
196
+ if ("NONE" == severity) return LOG_TARGET_NONE;
197
+ if ("STDERR" == severity) return LOG_TARGET_STDERR;
198
+ if ("FILE" == severity) return LOG_TARGET_FILE;
199
+ if ("CALLBACK" == severity) return LOG_TARGET_CALLBACK;
200
+ return LOG_TARGET_NONE;
201
+ }
202
+
203
+ bool EffectsDemoApp::run(const ConfigReader& config_reader) {
204
+ if (validateConfig(config_reader) == false) {
205
+ return false;
206
+ }
207
+
208
+ auto input_wav_list = config_reader.GetConfigValueList(kConfigFileInputVariable);
209
+ auto output_wav_list = config_reader.GetConfigValueList(kConfigFileOutputVariable);
210
+ // Support 1024 max inputs to simplify
211
+ const int kMaxSupportedInputs = 1024;
212
+ if (input_wav_list.size() > kMaxSupportedInputs) {
213
+ std::cerr << "This sample application supports a maximum of " << kMaxSupportedInputs <<
214
+ " input files (although more may be supported by the SDK depending on GPU,"
215
+ " please refer to the programming guide for limits and procedure for"
216
+ " setting batch size in SDK)." << std::endl;
217
+ return false;
218
+ }
219
+ std::vector<std::string> log_targets;
220
+ if (config_reader.IsConfigValueAvailable(kConfigLogTarget)) {
221
+ log_targets = config_reader.GetConfigValueList(kConfigLogTarget);
222
+ }
223
+ LoggingSeverity severity = LOG_LEVEL_INFO;
224
+ int target = LOG_TARGET_STDERR;
225
+ std::string log_file(kConfigLogTargetFileDefault);
226
+
227
+ if (!log_targets.empty()) {
228
+ target = LOG_TARGET_NONE;
229
+ for (auto& item : log_targets) {
230
+ target |= StringToLogTarget(item);
231
+ }
232
+ }
233
+
234
+ if (target & LOG_TARGET_FILE) {
235
+ if (config_reader.IsConfigValueAvailable(kConfigLogTargetFile)) {
236
+ log_file = config_reader.GetConfigValue(kConfigLogTargetFile);
237
+ }
238
+ }
239
+
240
+ if (config_reader.IsConfigValueAvailable(kConfigLogLevel)) {
241
+ auto log_level = config_reader.GetConfigValue(kConfigLogLevel);
242
+ if (!log_level.empty()) {
243
+ severity = StringToLogSeverity(log_level);
244
+ }
245
+ }
246
+
247
+ if (input_wav_list.size() > output_wav_list.size()) {
248
+ std::cout << "Error: Input and output wav files list size mismatch found" << std::endl;
249
+ return false;
250
+ }
251
+
252
+ std::vector<bool> reset_list(input_wav_list.size(), false);
253
+ if (config_reader.IsConfigValueAvailable(kConfigResetVariable)) {
254
+ auto config_list = config_reader.GetConfigValueList(kConfigResetVariable);
255
+ for (auto& item : config_list) {
256
+ char* p;
257
+ unsigned int num = strtol(item.c_str(), &p, 10);
258
+ if (*p != 0) {
259
+ // Invalid?
260
+ continue;
261
+ }
262
+
263
+ if (num < 1 || num > input_wav_list.size()) {
264
+ std::cerr << "Error: Invalid stream specified for reset" <<std::endl;
265
+ return false;
266
+ }
267
+
268
+ reset_list[num-1] = true;
269
+ }
270
+ }
271
+
272
+ const unsigned num_streams = input_wav_list.size();
273
+
274
+ if (real_time_) {
275
+ std::cout << "App will run in real time mode ..." << std::endl;
276
+ }
277
+
278
+ NvAFX_Status log_status;
279
+ log_status = NvAFX_InitializeLogger(severity, target, log_file.c_str(),
280
+ target & LOG_TARGET_CALLBACK ? logger_cb : nullptr,
281
+ nullptr);
282
+
283
+ if (log_status != NVAFX_STATUS_SUCCESS) {
284
+ std::cerr << "NvAFX_InitializeLogger() failed" << std::endl;
285
+ return false;
286
+ }
287
+
288
+ int num_effects;
289
+ NvAFX_EffectSelector* supported_effects;
290
+ if (NvAFX_GetEffectList(&num_effects, &supported_effects) != NVAFX_STATUS_SUCCESS) {
291
+ std::cerr << "NvAFX_GetEffectList() failed" << std::endl;
292
+ return false;
293
+ }
294
+
295
+ std::cout << "Total Effects supported: " << num_effects << std::endl;
296
+ for (int i = 0; i < num_effects; ++i) {
297
+ std::cout << "(" << i + 1 << ") " << supported_effects[i] << std::endl;
298
+ }
299
+
300
+ bool is_aec = false;
301
+
302
+ NvAFX_Handle handle;
303
+ std::vector<std::string> effects = config_reader.GetConfigValueList(kConfigEffectVariable);
304
+ std::vector<std::string> sample_rate_str = config_reader.GetConfigValueList(kConfigSampleRateVariable);
305
+ std::vector<uint32_t> sample_rates;
306
+ for (auto& s: sample_rate_str) {
307
+ sample_rates.push_back(std::strtoul(s.c_str(), nullptr, 0));
308
+ }
309
+
310
+ std::string effect_name;
311
+ if (effects.size() == 1) {
312
+ // Single effect
313
+ if (sample_rates.size() != effects.size()) {
314
+ std::cerr << "Expected single sample rate for single effect" << std::endl;
315
+ return false;
316
+ }
317
+ input_sample_rate_ = sample_rates[0];
318
+ if (std::find(kAllowedSampleRates.begin(), kAllowedSampleRates.end(), input_sample_rate_) ==
319
+ kAllowedSampleRates.end()) {
320
+ std::cerr << "Sample rate " << input_sample_rate_ << " not supported" << std::endl;
321
+ return false;
322
+ }
323
+ NvAFX_Status status = NVAFX_STATUS_FAILED;
324
+ if (effects[0] == "denoiser") {
325
+ status = NvAFX_CreateEffect(NVAFX_EFFECT_DENOISER, &handle);
326
+ } else if (effects[0] == "dereverb") {
327
+ status = NvAFX_CreateEffect(NVAFX_EFFECT_DEREVERB, &handle);
328
+ } else if (effects[0] == "dereverb_denoiser") {
329
+ status = NvAFX_CreateEffect(NVAFX_EFFECT_DEREVERB_DENOISER, &handle);
330
+ } else if (effects[0] == "aec") {
331
+ status = NvAFX_CreateEffect(NVAFX_EFFECT_AEC, &handle);
332
+ is_aec = true;
333
+ } else if (effects[0] == "superres") {
334
+ status = NvAFX_CreateEffect(NVAFX_EFFECT_SUPERRES, &handle);
335
+ } else {
336
+ std::cerr << "NvAFX_CreateEffect() failed. Invalid Effect Value : " << effects[0] << std::endl;
337
+ return false;
338
+ }
339
+ if (status == NVAFX_UNSUPPORTED_RUNTIME) {
340
+ float version = (CUDA_SUPPORTED_RUNTIME / 1000) + (CUDA_SUPPORTED_RUNTIME%100)/100.f;
341
+ std::cerr << "Unsupported CUDA runtime (requires >= " << version << "). "
342
+ "Please ensure that a driver supporting the required CUDA version is installed (or if using FCU, library path "
343
+ "contains the correct CUDA compat libraries). For more details, please refer to the "
344
+ "programming guide." << std::endl;
345
+ return false;
346
+ } else if (status != NVAFX_STATUS_SUCCESS) {
347
+ std::cerr << "NvAFX_CreateEffect() failed" << std::endl;
348
+ return false;
349
+ }
350
+
351
+ effect_name = effects[0];
352
+ const std::set<std::string> kVadSupportedEffects = {"denoiser", "dereverb_denoiser"};
353
+ vad_supported_ = (kVadSupportedEffects.find(effects[0]) != kVadSupportedEffects.end()) &&
354
+ config_reader.IsConfigValueAvailable(kConfigVadEnable) &&
355
+ std::strtoul(config_reader.GetConfigValue(kConfigVadEnable).c_str(), nullptr, 0);
356
+
357
+
358
+ if (vad_supported_) {
359
+ std::cout << "Enabling VAD" << std::endl;
360
+ if (NvAFX_SetU32(handle, NVAFX_PARAM_ENABLE_VAD, 1) != NVAFX_STATUS_SUCCESS) {
361
+ std::cerr << "Could not enable VAD" << std::endl;
362
+ return false;
363
+ }
364
+ }
365
+
366
+ // If the system has multiple supported GPUs, then the application can either
367
+ // use CUDA driver APIs or CUDA runtime APIs to enumerate the GPUs and select one based on the
368
+ // application's requirements or offload the responsibility to SDK to select the GPU by setting
369
+ // NVAFX_PARAM_USE_DEFAULT_GPU as 1
370
+ if (NvAFX_SetU32(handle, NVAFX_PARAM_USE_DEFAULT_GPU, use_default_gpu_) != NVAFX_STATUS_SUCCESS) {
371
+ std::cerr << "NvAFX_SetBool(NVAFX_PARAM_USE_DEFAULT_GPU " << ") failed" << std::endl;
372
+ }
373
+
374
+ status = NvAFX_SetU32(handle, NVAFX_PARAM_INPUT_SAMPLE_RATE, input_sample_rate_);
375
+ if (status == NVAFX_STATUS_INVALID_PARAM) {
376
+ // Try depreciated param
377
+ status = NvAFX_SetU32(handle, NVAFX_PARAM_SAMPLE_RATE, input_sample_rate_);
378
+ }
379
+ if (status!= NVAFX_STATUS_SUCCESS) {
380
+ std::cerr << "NvAFX_SetU32(Sample Rate: " << input_sample_rate_ << ") failed" << std::endl;
381
+ return false;
382
+ }
383
+
384
+ } else {
385
+ const std::map<std::tuple<std::string, std::string, uint32_t, uint32_t>, NvAFX_EffectSelector> supported_configs =
386
+ {
387
+ // 16k Effect + Superres 16k-48k
388
+ {std::make_tuple(std::string("denoiser"), std::string("superres"), 16000, 16000), NVAFX_CHAINED_EFFECT_DENOISER_16k_SUPERRES_16k_TO_48k},
389
+ {std::make_tuple(std::string("dereverb"), std::string("superres"), 16000, 16000), NVAFX_CHAINED_EFFECT_DEREVERB_16k_SUPERRES_16k_TO_48k},
390
+ {std::make_tuple(std::string("dereverb_denoiser"), std::string("superres"), 16000, 16000),
391
+ NVAFX_CHAINED_EFFECT_DEREVERB_DENOISER_16k_SUPERRES_16k_TO_48k},
392
+
393
+ // Superres 8k-16k + 16k Effect
394
+ {std::make_tuple("superres", "denoiser", 8000, 16000), NVAFX_CHAINED_EFFECT_SUPERRES_8k_TO_16k_DENOISER_16k },
395
+ {std::make_tuple("superres", "dereverb", 8000, 16000), NVAFX_CHAINED_EFFECT_SUPERRES_8k_TO_16k_DEREVERB_16k },
396
+ {std::make_tuple("superres", "dereverb_denoiser", 8000, 16000),
397
+ NVAFX_CHAINED_EFFECT_SUPERRES_8k_TO_16k_DEREVERB_DENOISER_16k },
398
+ };
399
+ auto effect_config = supported_configs.find(std::make_tuple(effects[0], effects[1], sample_rates[0], sample_rates[1]));
400
+ if (effect_config == supported_configs.end()) {
401
+ std::cerr << "Unsupported effect chain" <<std::endl;
402
+ return false;
403
+ }
404
+ auto status = NvAFX_CreateChainedEffect(effect_config->second,&handle);
405
+ if (status == NVAFX_UNSUPPORTED_RUNTIME) {
406
+ float version = (CUDA_SUPPORTED_RUNTIME / 1000) + (CUDA_SUPPORTED_RUNTIME%100)/100.f;
407
+ std::cerr << "Unsupported CUDA runtime (requires >= " << version << "). "
408
+ "Please ensure that a driver supporting the required CUDA version is installed (or if using FCU, library path "
409
+ "contains the correct CUDA compat libraries). For more details, please refer to the "
410
+ "programming guide." << std::endl;
411
+ return false;
412
+ } else if (status != NVAFX_STATUS_SUCCESS) {
413
+ std::cerr << "Could not create effect" << std::endl;
414
+ return false;
415
+ }
416
+ input_sample_rate_ = sample_rates[0];
417
+
418
+ effect_name = "Chained Effect (" + effects[0] + " + " + effects[1] + ")";
419
+
420
+ if (config_reader.IsConfigValueAvailable(kConfigChainedEffectGpuList)) {
421
+ std::vector<std::string> gpu_list = config_reader.GetConfigValueList(kConfigChainedEffectGpuList);
422
+ std::vector<uint32_t> gpus;
423
+ for (auto& s: gpu_list) {
424
+ gpus.push_back(std::strtoul(s.c_str(), nullptr, 0));
425
+ }
426
+
427
+ NvAFX_SetU32List(handle, NVAFX_PARAM_CHAINED_EFFECT_GPU_LIST, gpus.data(), gpus.size());
428
+ }
429
+
430
+ }
431
+
432
+ unsigned num_input_samples_per_frame = 0;
433
+ //Superres has more number of output samples
434
+ // Note: Number of samples are per channel
435
+ unsigned num_output_samples_per_frame = 0;
436
+ if (config_reader.IsConfigValueAvailable(kConfigFrameSize)) {
437
+ auto config_value = config_reader.GetConfigValue(kConfigFrameSize);
438
+ unsigned frame_size = std::strtoul(config_value.c_str(), nullptr, 0);
439
+ // frame_size is in milliseconds.
440
+ num_input_samples_per_frame = (input_sample_rate_ * frame_size) / 1000;
441
+ }
442
+
443
+ // Obtain farend audio if effect is AEC
444
+ std::vector<std::string> input_farend_wav_list;
445
+ if (is_aec) {
446
+ input_farend_wav_list = config_reader.GetConfigValueList(kConfigFileInputFarendVariable);
447
+ if (input_farend_wav_list.size() != input_wav_list.size()) {
448
+ std::cerr << "AEC effect requires farend audio as input in addition to nearend audio."
449
+ "Please ensure that the config value \"" << kConfigFileInputFarendVariable <<
450
+ "\" is present and has correct number of files" << std::endl;
451
+ return false;
452
+ }
453
+ }
454
+
455
+ NvAFX_Status status;
456
+
457
+ std::vector<std::string> model_files = config_reader.GetConfigValueList(kConfigFileModelVariable);
458
+ // Note: For single effect, model can be passed in as a single string or as a list of strings with size 1
459
+ std::unique_ptr<char*[]> model_files_param(new char*[model_files.size()]);
460
+ for (int i = 0; i < model_files.size(); i++) {
461
+ model_files_param[i] = (char*) model_files[i].data();
462
+ }
463
+ if (NvAFX_SetStringList(handle, NVAFX_PARAM_MODEL_PATH, (const char**)model_files_param.get(),
464
+ model_files.size())
465
+ != NVAFX_STATUS_SUCCESS) {
466
+ std::cerr << "NvAFX_SetString() failed" << std::endl;
467
+ return false;
468
+ }
469
+
470
+ if (NvAFX_SetU32(handle, NVAFX_PARAM_NUM_STREAMS, num_streams) != NVAFX_STATUS_SUCCESS) {
471
+ std::cerr << "NvAFX_SetU32(NVAFX_PARAM_NUM_STREAMS) failed" << std::endl;
472
+ return false;
473
+ }
474
+
475
+ unsigned int list_size = 0;
476
+ std::unique_ptr<unsigned int[]> supported_list = nullptr;
477
+ auto ret = NvAFX_GetU32List(handle, NVAFX_PARAM_SUPPORTED_NUM_SAMPLES_PER_FRAME,
478
+ supported_list.get(), &list_size);
479
+ if (ret != NVAFX_STATUS_OUTPUT_BUFFER_TOO_SMALL) {
480
+ std::cerr << "NvAFX_GetU32List(NVAFX_PARAM_SUPPORTED_NUM_SAMPLES_PER_FRAME) failed."
481
+ << std::endl;
482
+ return false;
483
+ }
484
+ supported_list.reset(new unsigned int[list_size]);
485
+ if (NvAFX_GetU32List(handle, NVAFX_PARAM_SUPPORTED_NUM_SAMPLES_PER_FRAME,
486
+ supported_list.get(), &list_size) != NVAFX_STATUS_SUCCESS) {
487
+ std::cerr << "NvAFX_GetU32List(NVAFX_PARAM_SUPPORTED_NUM_SAMPLES_PER_FRAME) failed."
488
+ << std::endl;
489
+ return false;
490
+ }
491
+ // If config specifies certain value check if it is in supported list
492
+ if (config_reader.IsConfigValueAvailable(kConfigFrameSize)) {
493
+ auto pointer = std::find(supported_list.get(), supported_list.get() + list_size,
494
+ num_input_samples_per_frame);
495
+ if (pointer == supported_list.get() + list_size) {
496
+ std::ostringstream oss;
497
+ std::string separator("");
498
+ oss << "'";
499
+ for (unsigned i = 0; i < list_size; ++i) {
500
+ oss << separator << (supported_list[i] * 1000) / input_sample_rate_;
501
+ separator = ", ";
502
+ }
503
+ oss << "'.";
504
+ std::cerr << "Supplied value for " << kConfigFrameSize << " is not supported. Supplied value: "
505
+ << config_reader.GetConfigValue(kConfigFrameSize) << "." << " Supported values: "
506
+ << oss.str() << std::endl;
507
+ return false;
508
+ }
509
+ } else {
510
+ num_input_samples_per_frame = supported_list[0];
511
+ }
512
+
513
+ status = NvAFX_SetU32(handle, NVAFX_PARAM_NUM_SAMPLES_PER_INPUT_FRAME, num_input_samples_per_frame);
514
+ if (status == NVAFX_STATUS_INVALID_PARAM) {
515
+ // Try previous version
516
+ status = NvAFX_SetU32(handle, NVAFX_PARAM_NUM_SAMPLES_PER_FRAME, num_input_samples_per_frame);
517
+ }
518
+ if (status!= NVAFX_STATUS_SUCCESS) {
519
+ std::cerr << "NvAFX_SetU32(NVAFX_PARAM_NUM_SAMPLES_PER_FRAME) failed" << std::endl;
520
+ return false;
521
+ }
522
+
523
+ // Intensity ratio may not be supported for some effects, ignore if status is invalid param
524
+ status = NvAFX_SetFloat(handle, NVAFX_PARAM_INTENSITY_RATIO, intensity_ratio_);
525
+ if (status != NVAFX_STATUS_SUCCESS && status != NVAFX_STATUS_INVALID_PARAM) {
526
+ std::cerr << "NvAFX_SetFloat(NVAFX_PARAM_INTENSITY_RATIO) failed" << std::endl;
527
+ }
528
+
529
+ std::cout << "Loading effect" << " ... ";
530
+ if (NvAFX_Load(handle) != NVAFX_STATUS_SUCCESS) {
531
+ std::cerr << "NvAFX_Load() failed" << std::endl;
532
+ return false;
533
+ }
534
+ std::cout << "Done" << std::endl;
535
+
536
+ // Try to get output number of samples per frame (valid only with new SDK)
537
+ status = NvAFX_GetU32(handle, NVAFX_PARAM_NUM_SAMPLES_PER_OUTPUT_FRAME,
538
+ &num_output_samples_per_frame);
539
+ if (status != NVAFX_STATUS_SUCCESS) {
540
+ if (status == NVAFX_STATUS_INVALID_PARAM) {
541
+ // Old SDK, output samples = input samples
542
+ num_output_samples_per_frame = num_input_samples_per_frame;
543
+ } else {
544
+ std::cerr << "NvAFX_GetU32() failed: NVAFX_PARAM_NUM_SAMPLES_PER_OUTPUT_FRAME" << std::endl;
545
+ return false;
546
+ }
547
+ }
548
+
549
+ // Try to get output sample rate (valid only with new SDK)
550
+ status = NvAFX_GetU32(handle, NVAFX_PARAM_OUTPUT_SAMPLE_RATE, &output_sample_rate_);
551
+ if (status != NVAFX_STATUS_SUCCESS) {
552
+ if (status == NVAFX_STATUS_INVALID_PARAM) {
553
+ // Old SDK, output sample rate = input sample rate
554
+ output_sample_rate_ = input_sample_rate_;
555
+ } else {
556
+ std::cerr << "NvAFX_GetU32() failed: NVAFX_PARAM_OUTPUT_SAMPLE_RATE" << std::endl;
557
+ return false;
558
+ }
559
+ }
560
+
561
+ // Try to get input/output channels, if that fails (old SDK?), try depreciated num_channels
562
+ unsigned num_input_channels, num_output_channels;
563
+ status = NvAFX_GetU32(handle, NVAFX_PARAM_NUM_INPUT_CHANNELS, &num_input_channels);
564
+ if (status != NVAFX_STATUS_SUCCESS) {
565
+ if (status == NVAFX_STATUS_INVALID_PARAM) {
566
+ if (NvAFX_GetU32(handle, NVAFX_PARAM_NUM_CHANNELS, &num_input_channels) != NVAFX_STATUS_SUCCESS) {
567
+ std::cerr << "NvAFX_GetU32(NVAFX_PARAM_NUM_CHANNELS) failed" << std::endl;
568
+ return false;
569
+ } else {
570
+ num_output_channels = num_input_channels;
571
+ }
572
+ } else {
573
+ std::cerr << "NvAFX_GetU32() failed" << std::endl;
574
+ return false;
575
+ }
576
+ } else if (NvAFX_GetU32(handle, NVAFX_PARAM_NUM_OUTPUT_CHANNELS, &num_output_channels) !=
577
+ NVAFX_STATUS_SUCCESS) {
578
+ std::cerr << "NvAFX_GetU32(NVAFX_PARAM_NUM_OUTPUT_CHANNELS) failed" << std::endl;
579
+ return false;
580
+ }
581
+
582
+ output_channels_ = num_output_channels;
583
+
584
+ std::cout << "Effect properties:" << std::endl
585
+ << " Effect : " << effect_name << std::endl
586
+ << " Input Channels : " << num_input_channels << std::endl
587
+ << " Input Sample Rate : " << input_sample_rate_ << std::endl
588
+ << " Output Sample Rate : " << output_sample_rate_ << std::endl
589
+ << " Samples per frame : " << num_input_samples_per_frame << std::endl
590
+ << " Number of streams : " << num_streams << std::endl;
591
+
592
+ size_t max_num_input_samples = 0;
593
+ // read the noisy input wav file data and cache it in RAM.
594
+ for (unsigned i = 0; i < num_streams; ++i) {
595
+ StreamData data;
596
+ data.input_wav_file = input_wav_list[i];
597
+
598
+ if (!ReadWavFile(input_wav_list[i], input_sample_rate_, &data.input_wav_samples,
599
+ &data.num_samples, &data.file_end_offsets, num_input_samples_per_frame)) {
600
+ std::cerr << "Unable to read wav file: " << input_wav_list[i] << std::endl;
601
+ if (errno == EMFILE) {
602
+ std::cerr << "Open file limit reached. Please increase file limit using the ulimit "
603
+ "utility (for example, \"ulimit -n 20000\"). Please refer to the "
604
+ "documentation of the ulimit utility for more details." << std::endl;
605
+ }
606
+ return false;
607
+ }
608
+
609
+ // max_num_samples should be aligned to 'num_input_samples_per_frame' automatically
610
+ if (max_num_input_samples < data.input_wav_samples->size()) {
611
+ max_num_input_samples = data.input_wav_samples->size();
612
+ }
613
+
614
+ if (is_aec) {
615
+ unsigned int num_samples = 0;
616
+ std::vector<int> end_offsets;
617
+
618
+ if (!ReadWavFile(input_farend_wav_list[i], input_sample_rate_, &data.input_farend_wav_samples,
619
+ &num_samples, &end_offsets, num_input_samples_per_frame)) {
620
+ std::cerr << "Unable to read wav file: " << input_farend_wav_list[i] << std::endl;
621
+ if (errno == EMFILE) {
622
+ std::cerr << "Open file limit reached. Please increase file limit using the ulimit "
623
+ "utility (for example, \"ulimit -n 20000\"). Please refer to the "
624
+ "documentation of the ulimit utility for more details." << std::endl;
625
+ }
626
+ return false;
627
+ }
628
+
629
+ if (num_samples != data.num_samples || end_offsets != data.file_end_offsets) {
630
+ std::cerr << "Input farend file specification does not match nearend file specification."
631
+ "Farend and Nearend files must have the same number of samples";
632
+ return false;
633
+ }
634
+ }
635
+
636
+ data.output_wav_file = output_wav_list[i];
637
+ if (output_channels_ > 1) {
638
+ auto filename_wo_ext = output_wav_list[i].substr(0, output_wav_list[i].find_last_of("."));
639
+
640
+ for (int ch = 0; ch < output_channels_; ch++) {
641
+ std::string filename = filename_wo_ext + "_ch" + std::to_string(ch + 1) + ".wav";
642
+ data.wav_write.emplace_back(
643
+ std::unique_ptr<CWaveFileWrite>(new CWaveFileWrite(filename,
644
+ output_sample_rate_,
645
+ 1, 32, true)));
646
+
647
+ }
648
+ } else {
649
+ data.wav_write.emplace_back(std::unique_ptr<CWaveFileWrite>(new CWaveFileWrite(output_wav_list[i], output_sample_rate_,
650
+ 1, 32, true)));
651
+ }
652
+ for (int ch = 0; ch < num_output_channels; ch++) {
653
+ if (!data.wav_write[ch]->initFile()) {
654
+ std::cerr << "Unable to open file for writing: "
655
+ << data.wav_write[ch]->getFileName() << std::endl;
656
+ if (errno == EMFILE) {
657
+ std::cerr << "Open file limit reached. Please increase file limit using the ulimit "
658
+ "utility (for example, \"ulimit -n 20000\"). Please refer to the "
659
+ "documentation of the ulimit utility for more details." << std::endl;
660
+ }
661
+ return false;
662
+ }
663
+
664
+ }
665
+ data.output_file_num = 0;
666
+
667
+ stream_data_.push_back(std::move(data));
668
+ #ifndef ENABLE_PERF_DUMP
669
+ std::cout << "Input wav file: " << input_wav_list[i] << std::endl
670
+ << "Total " << data.num_samples << " samples read" << std::endl;
671
+ #endif // ENABLE_PERF_DUMP
672
+ }
673
+
674
+ // make all sizes same to ease effect run loop's work
675
+ for (auto& data : stream_data_) {
676
+ data.input_wav_samples->resize(max_num_input_samples);
677
+ }
678
+
679
+ float frame_in_secs = static_cast<float>(num_input_samples_per_frame) /
680
+ static_cast<float>(input_sample_rate_);
681
+ float total_run_time = 0.f;
682
+ float total_audio_duration = 0.f;
683
+ float checkpoint = 0.1f;
684
+ float expected_audio_duration = static_cast<float>(max_num_input_samples) /
685
+ static_cast<float>(input_sample_rate_);
686
+ std::vector<float> input_frame(num_input_channels * num_input_samples_per_frame * num_streams);
687
+ std::vector<float> output_frame(num_output_channels * num_output_samples_per_frame * num_streams);
688
+
689
+ std::string progress_bar = "[ ] ";
690
+ std::cout << "Processed: " << progress_bar << "0%\r";
691
+ std::cout.flush();
692
+
693
+ std::vector<NvAFX_Bool> bitmap(input_wav_list.size(), NVAFX_FALSE);
694
+
695
+ // wav data is already padded to align to num_input_samples_per_frame by ReadWavFile()
696
+ for (size_t offset = 0; offset < max_num_input_samples; offset += num_input_samples_per_frame) {
697
+ // prepare input data
698
+ bool should_reset = false;
699
+ for (unsigned i = 0; i < num_streams; ++i) {
700
+ float *in = stream_data_[i].input_wav_samples->data() + offset;
701
+ std::copy(in, in + num_input_samples_per_frame, &input_frame[i * num_input_samples_per_frame]);
702
+ if (is_aec) {
703
+ in = stream_data_[i].input_farend_wav_samples->data() + offset;
704
+ std::copy(in, in + num_input_samples_per_frame, &input_frame[(num_streams + i) * num_input_samples_per_frame]);
705
+ }
706
+
707
+ // Check if this file finished, if so, start a new file
708
+ if (stream_data_[i].file_end_offsets.size() &&
709
+ offset == static_cast<unsigned>(stream_data_[i].file_end_offsets[0])) {
710
+ stream_data_[i].file_end_offsets.erase(stream_data_[i].file_end_offsets.begin());
711
+ if (stream_data_[i].file_end_offsets.size()) {
712
+ // Open a new file only if this isn't the last one
713
+
714
+ if (output_channels_ > 1) {
715
+ uint32_t output_num = ++stream_data_[i].output_file_num;
716
+ auto filename_wo_ext = output_wav_list[i].substr(0, output_wav_list[i].find_last_of("_"));
717
+ for (int ch = 0; ch < output_channels_; ch++) {
718
+ stream_data_[i].wav_write[ch]->commitFile();
719
+ std::string filename = filename_wo_ext + + "_" + std::to_string(output_num) +
720
+ "_ch" + std::to_string(ch + 1) + ".wav";
721
+ stream_data_[i].wav_write[ch] = std::unique_ptr<CWaveFileWrite>(new CWaveFileWrite(filename, output_sample_rate_,
722
+ 1, 32, true));
723
+ if (!stream_data_[i].wav_write[ch]->initFile()) {
724
+ std::cerr << "Unable to open file for writing: " << filename;
725
+ return false;
726
+ }
727
+ }
728
+ } else {
729
+ auto ext = output_wav_list[i].find_last_of(".");
730
+ std::string filename = output_wav_list[i].substr(0, ext) + "_" +
731
+ std::to_string(++stream_data_[i].output_file_num) + ".wav";
732
+ stream_data_[i].wav_write[0] = std::unique_ptr<CWaveFileWrite>(new CWaveFileWrite(filename, output_sample_rate_,
733
+ 1, 32, true));
734
+
735
+ if (!stream_data_[i].wav_write[0]->initFile()) {
736
+ std::cerr << "Unable to open file for writing: " << filename;
737
+ return false;
738
+ }
739
+ }
740
+
741
+
742
+ if (reset_list[i]) {
743
+ should_reset = true;
744
+ bitmap[i] = NVAFX_TRUE;
745
+ }
746
+ }
747
+ }
748
+ }
749
+
750
+ if (should_reset) {
751
+ if (NvAFX_Reset(handle, bitmap.data(), input_wav_list.size()) != NVAFX_STATUS_SUCCESS) {
752
+ std::cerr << "Reset failed" << std::endl;
753
+ return false;
754
+ }
755
+ memset(bitmap.data(), NVAFX_FALSE, sizeof(NvAFX_Bool) * bitmap.size());
756
+ }
757
+
758
+ const float* input[2];
759
+ float* output[2];
760
+ input[0] = input_frame.data();
761
+ if (is_aec) {
762
+ input[1] = &input_frame[input_frame.size() / num_input_channels];
763
+ }
764
+
765
+ output[0] = output_frame.data();
766
+ if (num_output_channels > 1) {
767
+ output[1] = output_frame.data() + output_frame.size()/2;
768
+ }
769
+
770
+ auto start_tick = std::chrono::high_resolution_clock::now();
771
+ if (NvAFX_Run(handle, input, output, num_input_samples_per_frame, num_input_channels) != NVAFX_STATUS_SUCCESS) {
772
+ std::cerr << "NvAFX_Run() failed" << std::endl;
773
+ return false;
774
+ }
775
+
776
+ auto run_end_tick = std::chrono::high_resolution_clock::now();
777
+ total_run_time += (std::chrono::duration<float>(run_end_tick - start_tick)).count();
778
+ total_audio_duration += frame_in_secs;
779
+
780
+ if ((total_audio_duration / expected_audio_duration) > checkpoint) {
781
+ progress_bar[checkpoint * 10] = '=';
782
+ std::cout << "Processed: " << progress_bar << checkpoint * 100.f << "% ";
783
+ std::cout << (checkpoint >=1 ? "\n" : "\r");
784
+ std::cout.flush();
785
+ checkpoint += 0.1f;
786
+ }
787
+
788
+ for (unsigned i = 0; i < num_streams; ++i) {
789
+ unsigned idx = i * num_output_samples_per_frame;
790
+ std::vector<float*> outputs = {};
791
+ unsigned size_per_channel = output_frame.size()/output_channels_;
792
+ for (int ch = 0; ch < output_channels_; ch++) {
793
+ outputs.push_back(&output_frame[idx + ch * size_per_channel]);
794
+ }
795
+ if (!writeOutputWav(outputs, num_output_samples_per_frame, i)) {
796
+ return false;
797
+ }
798
+ }
799
+
800
+ if (real_time_) {
801
+ auto end_tick = std::chrono::high_resolution_clock::now();
802
+ std::chrono::duration<float> elapsed = end_tick - start_tick;
803
+ float sleep_time_secs = frame_in_secs - elapsed.count();
804
+ std::this_thread::sleep_for(std::chrono::milliseconds(static_cast<int>(sleep_time_secs * 1000)));
805
+ }
806
+ }
807
+
808
+ std::cout << "Processing time " << std::setprecision(2) << total_run_time << " secs for "
809
+ << total_audio_duration << std::setprecision(2) << " secs audio file (" << total_run_time / total_audio_duration
810
+ << " secs processing time per sec of audio)" << std::endl;
811
+ if (real_time_) {
812
+ std::cout << "Note: App ran in real time mode i.e. simulated the input data rate of a mic" << std::endl
813
+ << "'Processing time' could be less then actual run time" << std::endl;
814
+ }
815
+
816
+ for (auto& data : stream_data_) {
817
+ for (int ch = 0; ch < output_channels_; ch++) {
818
+ data.wav_write[ch]->commitFile();
819
+ }
820
+ }
821
+
822
+ std::cout << "Output wav files written. " << std::endl;
823
+ if (NvAFX_DestroyEffect(handle) != NVAFX_STATUS_SUCCESS) {
824
+ std::cerr << "NvAFX_Release() failed" << std::endl;
825
+ return false;
826
+ }
827
+
828
+ if (NvAFX_UninitializeLogger() != NVAFX_STATUS_SUCCESS) {
829
+ std::cerr << "NvAFX_UninitializeLogger() failed" << std::endl;
830
+ return false;
831
+ }
832
+
833
+ return true;
834
+ }
835
+
836
+ void ShowHelpAndExit(const char* bad_option) {
837
+ std::ostringstream oss;
838
+ if (bad_option) {
839
+ oss << "Error parsing \"" << bad_option << "\"" << std::endl;
840
+ }
841
+ std::cout << "Command Line Options:" << std::endl
842
+ << "-c Config file" << std::endl;
843
+
844
+ std::cout << oss.str();
845
+ exit(0);
846
+ }
847
+
848
+ void ParseCommandLine(int argc, char* argv[], std::string* config_file) {
849
+ if (argc == 1) {
850
+ ShowHelpAndExit(nullptr);
851
+ }
852
+
853
+ for (int i = 1; i < argc; i++) {
854
+ if (!strcasecmp(argv[i], "-h")) {
855
+ ShowHelpAndExit(nullptr);
856
+ }
857
+ if (!strcasecmp(argv[i], "-c")) {
858
+ if (++i == argc || !config_file->empty()) {
859
+ ShowHelpAndExit("-f");
860
+ }
861
+ config_file->assign(argv[i]);
862
+ continue;
863
+ }
864
+
865
+ ShowHelpAndExit(argv[i]);
866
+ }
867
+ }
868
+
869
+ int main(int argc, char *argv[]) {
870
+ std::string config_file;
871
+ ParseCommandLine(argc, argv, &config_file);
872
+
873
+ ConfigReader config_reader;
874
+ if (config_reader.Load(config_file) == false) {
875
+ std::cerr << "Config file load failed" << std::endl;
876
+ return -1;
877
+ }
878
+
879
+ EffectsDemoApp app;
880
+ if (app.run(config_reader)) { return 0; }
881
+ else { return -1; }
882
+ }