Skip to content

Commit ed9edcb

Browse files
Implemented CMake-based buildsystem (#1)
* Implemented draft CMake-based buildsystem * Initial read audio-write audio draft with libraris setup * Added lazy filestream writer for dumping VAD value * Adjusted errorcode to the correct one * Adjusted CMakeLists.txt * Adjusted to use exception instead of spdlog error reporting for LazyFileWriter
1 parent 372f7b4 commit ed9edcb

File tree

7 files changed

+321
-0
lines changed

7 files changed

+321
-0
lines changed

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
build/
2+
downloads
3+
src/rnnoise_data_little.c
4+
src/rnnoise_data_little.h
5+
src/rnnoise_data.h
6+
src/rnnoise_data.c
7+
CMakeUserPresets.json
8+
.vscode/
9+
sample_audios/

CMakeLists.txt

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
cmake_minimum_required(VERSION 3.21)
2+
project(rnnoise)
3+
4+
option(RNNOISE_COMPILE_OPUS OFF)
5+
option(RNNOISE_COMPILE_DEMO ON)
6+
7+
if(RNNOISE_COMPILE_OPUS)
8+
add_definitions(-DCOMPILE_OPUS)
9+
endif()
10+
11+
# Ignore CRT warnings on MSVC
12+
if(MSVC)
13+
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
14+
endif()
15+
16+
#Pre-Generate library resources
17+
set(RNNOISE_MODEL_VERSION "0b50c45")
18+
set(RNNOISE_MODEL_TAR "rnnoise_data-${RNNOISE_MODEL_VERSION}.tar.gz")
19+
set(FILE_URL "https://media.xiph.org/rnnoise/models/${RNNOISE_MODEL_TAR}")
20+
set(RNNOISE_DATA_DOWNLOADS_DIR ${CMAKE_CURRENT_LIST_DIR}/downloads/)
21+
set(RNNOISE_DOWNLOADED_PATH ${RNNOISE_DATA_DOWNLOADS_DIR}/${RNNOISE_MODEL_TAR})
22+
23+
24+
25+
if(NOT EXISTS "${RNNOISE_DOWNLOADED_PATH}")
26+
27+
file(DOWNLOAD ${FILE_URL} ${RNNOISE_DOWNLOADED_PATH}
28+
STATUS RNNOISE_DOWNLOAD_STATUS
29+
TIMEOUT 60
30+
SHOW_PROGRESS)
31+
32+
if(RNNOISE_DOWNLOAD_STATUS EQUAL 0)
33+
message("File downloaded successfully.")
34+
else()
35+
message(FATAL_ERROR "Failed to download file: ${RNNOISE_DOWNLOAD_STATUS}")
36+
37+
endif()
38+
39+
execute_process(
40+
COMMAND ${CMAKE_COMMAND} -E tar xzf ${RNNOISE_DOWNLOADED_PATH}
41+
WORKING_DIRECTORY ${RNNOISE_DATA_DOWNLOADS_DIR}
42+
)
43+
44+
endif()
45+
46+
if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/src/rnnoise_data.c")
47+
set(RNNOISE_MODEL_UNPACKED_SOURCE_DIR ${RNNOISE_DATA_DOWNLOADS_DIR}/src)
48+
set(RNNOIDE_MODEL_FILES_DESTINATION ${CMAKE_CURRENT_LIST_DIR}/src)
49+
50+
file(COPY ${RNNOISE_MODEL_UNPACKED_SOURCE_DIR}/rnnoise_data_little.h DESTINATION ${RNNOIDE_MODEL_FILES_DESTINATION})
51+
file(COPY ${RNNOISE_MODEL_UNPACKED_SOURCE_DIR}/rnnoise_data_little.c DESTINATION ${RNNOIDE_MODEL_FILES_DESTINATION})
52+
file(COPY ${RNNOISE_MODEL_UNPACKED_SOURCE_DIR}/rnnoise_data.c DESTINATION ${RNNOIDE_MODEL_FILES_DESTINATION})
53+
file(COPY ${RNNOISE_MODEL_UNPACKED_SOURCE_DIR}/rnnoise_data.h DESTINATION ${RNNOIDE_MODEL_FILES_DESTINATION})
54+
endif()
55+
56+
# Get source files
57+
58+
set(RN_NOISE_SRC
59+
src/_kiss_fft_guts.h
60+
src/arch.h
61+
src/celt_lpc.h
62+
src/common.h
63+
src/cpu_support.h
64+
src/denoise.h
65+
src/kiss_fft.h
66+
src/nnet_arch.h
67+
src/nnet.h
68+
src/opus_types.h
69+
src/pitch.h
70+
src/rnn.h
71+
src/rnnoise_data.h
72+
include/rnnoise.h
73+
src/vec.h
74+
75+
src/celt_lpc.c
76+
src/nnet_default.c
77+
src/rnn.c
78+
src/rnnoise_tables.c
79+
src/denoise.c
80+
src/kiss_fft.c
81+
src/parse_lpcnet_weights.c
82+
src/rnnoise_data.c
83+
#write_weights.c
84+
#dump_features.c
85+
src/nnet.c
86+
src/pitch.c
87+
)
88+
89+
add_library(rnnoise STATIC ${RN_NOISE_SRC})
90+
91+
add_definitions(-DRNNOISE_BUILD)
92+
93+
# Include dirs
94+
target_include_directories(rnnoise PUBLIC
95+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
96+
$<INSTALL_INTERFACE:include>
97+
PRIVATE src)
98+
99+
add_subdirectory(examples)

conanfile.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[requires]
2+
libsndfile/1.2.2
3+
cxxopts/3.2.0
4+
fmt/10.2.1
5+
spdlog/1.14.0
6+
7+
[generators]
8+
CMakeDeps
9+
CMakeToolchain
10+
[layout]
11+
cmake_layout

examples/CMakeLists.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
add_executable(rnnoise_demo rnnoise_demo.c)
2+
3+
target_link_libraries(rnnoise_demo PRIVATE rnnoise)
4+
5+
find_package(SndFile REQUIRED)
6+
find_package(cxxopts REQUIRED)
7+
find_package(fmt REQUIRED)
8+
find_package(spdlog REQUIRED)
9+
10+
add_executable(rnnoise_libsoundfile rnnoise_libsndfile.cpp)
11+
target_link_libraries(
12+
rnnoise_libsoundfile
13+
PRIVATE
14+
rnnoise
15+
SndFile::sndfile
16+
cxxopts::cxxopts
17+
fmt::fmt
18+
spdlog::spdlog
19+
)
20+
target_include_directories(rnnoise_libsoundfile PRIVATE ${CMAKE_CURRENT_LIST_DIR})

examples/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
## Building with Conan
2+
```shell
3+
pip install conan
4+
mkdir build
5+
6+
## For debug version of the app
7+
conan install conanfile.txt --build=missing --settings=build_type=Debug
8+
cd build
9+
cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=./Debug/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Debug ..
10+
11+
12+
## For Release version:
13+
conan install conanfile.txt --build=missing
14+
cd build
15+
cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=./Release/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release ..
16+
17+
18+
cmake --build .
19+
```

examples/lazy_file_writer.hpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#pragma once
2+
#include <filesystem>
3+
#include <fstream>
4+
#include <iostream>
5+
6+
class LazyFileWriter {
7+
private:
8+
std::filesystem::path m_filepath;
9+
std::fstream m_file_stream;
10+
void openFileIfNeeded() {
11+
if (!m_file_stream.is_open()) {
12+
m_file_stream.open(m_filepath, std::ios::out | std::ios::app);
13+
if (!m_file_stream.is_open()) {
14+
throw std::runtime_error("Failed to open the lazy file writer");
15+
}
16+
}
17+
}
18+
19+
public:
20+
LazyFileWriter(const std::filesystem::path& filepath) : m_filepath{filepath}{}
21+
22+
~LazyFileWriter() {
23+
if (m_file_stream.is_open()) {
24+
m_file_stream.close();
25+
}
26+
}
27+
28+
template<typename TypeToWrite>
29+
void write(TypeToWrite&& data) {
30+
openFileIfNeeded();
31+
m_file_stream << data << std::endl;
32+
}
33+
};

examples/rnnoise_libsndfile.cpp

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
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

Comments
 (0)