1+ #include " sndfile.hh"
2+ #include " cxxopts.hpp"
3+
4+ #include " rnnoise.h"
5+ #include < cstdint>
6+ #include < filesystem>
7+ #include < fmt/core.h>
8+ #include < spdlog/spdlog.h>
9+ #include < array>
10+ #include < memory>
11+ #include < utility>
12+ #include < optional>
13+
14+ #include " lazy_file_writer.hpp"
15+
16+ template <auto DeleterFunction>
17+ using CustomDeleter = std::integral_constant<decltype (DeleterFunction), DeleterFunction>;
18+
19+ template <typename ManagedType, auto Functor>
20+ using PointerWrapper = std::unique_ptr<ManagedType, CustomDeleter<Functor>>;
21+
22+
23+ inline constexpr std::size_t AUDIO_BUFFER_LENGTH = 480 ;
24+ inline constexpr std::size_t NUM_CHANNELS = 1 ;
25+ inline constexpr std::size_t SAMPLERATE = 48000 ;
26+
27+ inline constexpr float RNNOISE_PCM16_MULTIPLY_FACTOR = 32768 .0f ;
28+
29+ using RNNoiseDenoiseStatePtr = PointerWrapper<DenoiseState,rnnoise_destroy>;
30+ using RnnModelPtr = PointerWrapper<RNNModel,rnnoise_model_free>;
31+ using TSamplesBufferArray = std::array<float ,AUDIO_BUFFER_LENGTH>;
32+
33+ RnnModelPtr rnn_model_ptr;
34+ RNNoiseDenoiseStatePtr rnnoise_denoise_state_ptr;
35+
36+ static void initialize_rnnoise_library (){
37+ rnnoise_denoise_state_ptr.reset (rnnoise_create (nullptr ));
38+ }
39+
40+ static void normalize_to_rnnoise_expected_level (TSamplesBufferArray& samples_buffer){
41+ for (auto & sample : samples_buffer){
42+ sample *= RNNOISE_PCM16_MULTIPLY_FACTOR;
43+ }
44+ }
45+
46+ static void denormalize_from_rnnoise_expected_level (TSamplesBufferArray& samples_buffer){
47+ for (auto & sample : samples_buffer){
48+ sample /= RNNOISE_PCM16_MULTIPLY_FACTOR;
49+ }
50+ }
51+
52+ static void dump_vad_prob (LazyFileWriter& lazy_probe_dumper,float vad_probe_value){
53+ lazy_probe_dumper.write (vad_probe_value);
54+ }
55+ static void process_audio_recording (
56+ LazyFileWriter& lazy_vad_probe_writer,
57+ const std::filesystem::path& input_file,
58+ const std::filesystem::path& output_file
59+ ){
60+ SndfileHandle input_audio_file_handle{SndfileHandle (input_file.c_str ())};
61+
62+ spdlog::info (" Opened input audio file:{}" , input_file.c_str ());
63+ spdlog::info (" Number of channels:{}" , input_audio_file_handle.channels ());
64+ spdlog::info (" Samplerate:{}" , input_audio_file_handle.samplerate ());
65+
66+ SndfileHandle output_audio_file_handle{SndfileHandle{
67+ output_file.c_str (),
68+ SFM_WRITE,
69+ SF_FORMAT_WAV | SF_FORMAT_PCM_16,
70+ NUM_CHANNELS,
71+ SAMPLERATE
72+ }
73+ };
74+
75+
76+ static TSamplesBufferArray samples_buffer{};
77+
78+ spdlog::info (" Processing audio..." );
79+ while (input_audio_file_handle.read (samples_buffer.data (), samples_buffer.size ()) != 0 ) {
80+ normalize_to_rnnoise_expected_level (samples_buffer);
81+ float vad_prob = rnnoise_process_frame (rnnoise_denoise_state_ptr.get (), samples_buffer.data (), samples_buffer.data ());
82+ dump_vad_prob (lazy_vad_probe_writer,vad_prob);
83+ denormalize_from_rnnoise_expected_level (samples_buffer);
84+ output_audio_file_handle.write (samples_buffer.data (),samples_buffer.size ());
85+ }
86+ spdlog::info (" Processing done. WAVE file can be found at: {}" , output_file.c_str ());
87+ }
88+
89+ int main (int argc, char ** argv){
90+ cxxopts::Options options (" rnnoise_libsoundfile denoiser" , " Simple runner of rnnoise over WAVe files with 48K samplerate" );
91+ options.add_options ()
92+ (" input" , " Input file to process" ,cxxopts::value<std::filesystem::path>())
93+ (" output" , " Output file" , cxxopts::value<std::filesystem::path>())
94+ (" vad_probe" , " Path to store output VAD prob data" , cxxopts::value<std::filesystem::path>()->default_value (std::filesystem::current_path ()/" vad_prob.txt" ))
95+ (" help" , " Print usage" );
96+
97+ auto result = options.parse (argc, argv);
98+
99+ if (result.count (" help" ))
100+ {
101+ fmt::print (options.help ());
102+ exit (0 );
103+ }
104+
105+
106+ using TOptionalPathHolder = std::optional<std::filesystem::path>;
107+ TOptionalPathHolder input_file_path_opt = result[" input" ].as <std::filesystem::path>();
108+ TOptionalPathHolder output_file_path_opt = result[" output" ].as <std::filesystem::path>();
109+ TOptionalPathHolder output_vad_probe = result[" vad_probe" ].as <std::filesystem::path>();
110+
111+ try {
112+ input_file_path_opt = result[" input" ].as <std::filesystem::path>();
113+ output_file_path_opt = result[" output" ].as <std::filesystem::path>();
114+ output_vad_probe = result[" vad_probe" ].as <std::filesystem::path>();
115+ }
116+ catch (...){
117+ std::cerr << " Failed to obtain one of the required CMD args. Check help message below and verify passed options:" << std::endl;
118+ fmt::print (options.help ());
119+ exit (-1 );
120+ }
121+
122+ LazyFileWriter vad_file_probe (output_vad_probe.value ());
123+ initialize_rnnoise_library ();
124+ process_audio_recording (
125+ vad_file_probe,
126+ input_file_path_opt.value (),
127+ output_file_path_opt.value ()
128+ );
129+ return 0 ;
130+ }
0 commit comments