Skip to content

Commit c013f76

Browse files
committed
[common] added resample support
While this on the surface may not make much sense to do, according to Xiph the RnNoise neural net is computed specifically for 48KHz and as such would require a re-train simply to operate at a different sample rate. This patch also implements a ring buffer to avoid excessive memory operations.
1 parent 7f57360 commit c013f76

File tree

2 files changed

+192
-69
lines changed

2 files changed

+192
-69
lines changed

src/common/include/common/RnNoiseCommonPlugin.h

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,45 @@
33
#include <memory>
44
#include <vector>
55

6+
#include <samplerate.h>
7+
68
struct DenoiseState;
79

810
class RnNoiseCommonPlugin {
911
public:
12+
RnNoiseCommonPlugin();
13+
14+
void setSampleRate(unsigned long sampleRate);
1015

11-
void init();
16+
bool init();
1217

1318
void deinit();
1419

20+
const char * getError()
21+
{
22+
return m_errorStr;
23+
}
24+
1525
void process(const float *in, float *out, int32_t sampleFrames);
1626

1727
private:
28+
const char * m_errorStr;
1829

19-
void createDenoiseState();
30+
bool m_initialized;
31+
bool m_resample;
2032

21-
private:
2233
static const int k_denoiseFrameSize = 480;
2334
static const int k_denoiseSampleRate = 48000;
2435

36+
std::shared_ptr<SRC_STATE> m_srcIn;
37+
std::shared_ptr<SRC_STATE> m_srcOut;
38+
double m_downRatio;
39+
double m_upRatio;
2540
std::shared_ptr<DenoiseState> m_denoiseState;
2641

27-
std::vector<float> m_inputBuffer;
28-
std::vector<float> m_outputBuffer;
29-
};
30-
31-
32-
42+
std::vector<float> m_inBuffer;
43+
std::vector<float> m_outBuffer;
44+
size_t m_outBufferR;
45+
size_t m_outBufferW;
46+
size_t m_outBufferA;
47+
};

src/common/src/RnNoiseCommonPlugin.cpp

Lines changed: 168 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,83 +7,191 @@
77

88
#include <rnnoise/rnnoise.h>
99

10-
void RnNoiseCommonPlugin::init() {
11-
deinit();
12-
createDenoiseState();
10+
RnNoiseCommonPlugin::RnNoiseCommonPlugin() :
11+
m_errorStr (NULL),
12+
m_initialized(false),
13+
m_resample (false)
14+
{
1315
}
1416

15-
void RnNoiseCommonPlugin::deinit() {
16-
m_denoiseState.reset();
17+
void RnNoiseCommonPlugin::setSampleRate(unsigned long sampleRate)
18+
{
19+
m_downRatio = (double)k_denoiseSampleRate / (double)sampleRate;
20+
m_upRatio = (double)sampleRate / (double)k_denoiseSampleRate;
21+
m_resample = sampleRate != 48000;
1722
}
1823

19-
void RnNoiseCommonPlugin::process(const float *in, float *out, int32_t sampleFrames) {
20-
if (sampleFrames == 0) {
21-
return;
22-
}
23-
24-
if (!m_denoiseState) {
25-
createDenoiseState();
26-
}
24+
bool RnNoiseCommonPlugin::init() {
25+
int err;
2726

28-
// Good case, we can copy less data around and rnnoise lib is built for it
29-
if (sampleFrames == k_denoiseFrameSize) {
30-
m_inputBuffer.resize(sampleFrames);
27+
if (m_initialized)
28+
deinit();
3129

32-
for (size_t i = 0; i < sampleFrames; i++) {
33-
m_inputBuffer[i] = in[i] * std::numeric_limits<short>::max();
34-
}
30+
m_srcIn = std::shared_ptr<SRC_STATE>(
31+
src_new(SRC_SINC_BEST_QUALITY, 1, &err),
32+
[](SRC_STATE *st)
33+
{
34+
src_delete(st);
35+
}
36+
);
37+
38+
if (err)
39+
{
40+
m_errorStr = src_strerror(err);
41+
return false;
42+
}
43+
44+
m_srcOut = std::shared_ptr<SRC_STATE>(
45+
src_new(SRC_SINC_BEST_QUALITY, 1, &err),
46+
[](SRC_STATE *st)
47+
{
48+
src_delete(st);
49+
}
50+
);
51+
52+
if (err)
53+
{
54+
m_srcIn.reset();
55+
m_errorStr = src_strerror(err);
56+
return false;
57+
}
58+
59+
m_denoiseState = std::shared_ptr<DenoiseState>(
60+
rnnoise_create(),
61+
[](DenoiseState *st)
62+
{
63+
rnnoise_destroy(st);
64+
}
65+
);
3566

36-
rnnoise_process_frame(m_denoiseState.get(), out, &m_inputBuffer[0]);
67+
src_set_ratio(m_srcIn.get(), m_downRatio);
68+
src_set_ratio(m_srcOut .get(), m_upRatio );
3769

38-
for (size_t i = 0; i < sampleFrames; i++) {
39-
out[i] /= std::numeric_limits<short>::max();
40-
}
41-
} else {
42-
m_inputBuffer.resize(m_inputBuffer.size() + sampleFrames);
70+
m_inBuffer .resize(k_denoiseFrameSize);
71+
m_outBuffer.resize(k_denoiseFrameSize * 2);
72+
m_outBufferR = 0;
73+
m_outBufferW = 0;
74+
m_outBufferA = 0;
4375

44-
// From [-1.f,1.f] range to [min short, max short] range which rnnoise lib will understand
45-
{
46-
float *inputBufferWriteStart = (m_inputBuffer.end() - sampleFrames).base();
47-
for (size_t i = 0; i < sampleFrames; i++) {
48-
inputBufferWriteStart[i] = in[i] * std::numeric_limits<short>::max();
49-
}
50-
}
76+
m_initialized = true;
77+
m_errorStr = NULL;
78+
return true;
79+
}
5180

52-
const size_t samplesToProcess = m_inputBuffer.size() / k_denoiseFrameSize;
53-
const size_t framesToProcess = samplesToProcess * k_denoiseFrameSize;
81+
void RnNoiseCommonPlugin::deinit() {
82+
m_denoiseState.reset();
83+
m_srcIn .reset();
84+
m_srcOut .reset();
85+
m_initialized = false;
86+
}
5487

55-
m_outputBuffer.resize(m_outputBuffer.size() + framesToProcess);
88+
void RnNoiseCommonPlugin::process(const float *in, float *out, int32_t sampleFrames)
89+
{
90+
const float mul = 1.0f / std::numeric_limits<short>::max();
91+
if (!sampleFrames)
92+
return;
93+
94+
if (!m_initialized)
95+
init();
96+
97+
SRC_DATA srcIn;
98+
srcIn.data_in = in;
99+
srcIn.input_frames = sampleFrames;
100+
srcIn.end_of_input = 0;
101+
srcIn.src_ratio = m_downRatio;
102+
srcIn.data_out = &m_inBuffer[0];
103+
srcIn.output_frames = m_inBuffer.size();
104+
105+
SRC_DATA srcOut;
106+
srcOut.data_out = out;
107+
srcOut.output_frames = sampleFrames;
108+
srcOut.end_of_input = 0;
109+
srcOut.src_ratio = m_upRatio;
110+
111+
long frames = 0;
112+
while(srcIn.input_frames)
113+
{
114+
if (m_resample)
115+
{
116+
// resample the samples and then scale them
117+
src_process(m_srcIn.get(), &srcIn);
118+
for(long i = 0; i < srcIn.output_frames_gen; ++i)
119+
m_inBuffer[i] *= std::numeric_limits<short>::max();
120+
}
121+
else
122+
{
123+
// just copy the data and scale it
124+
srcIn.input_frames_used = srcIn.input_frames;
125+
if (srcIn.input_frames_used > srcIn.output_frames)
126+
srcIn.input_frames_used = srcIn.output_frames;
127+
srcIn.output_frames_gen = srcIn.input_frames_used;
128+
129+
for(long i = 0; i < srcIn.output_frames_gen; ++i)
130+
m_inBuffer[i] = in[i] * std::numeric_limits<short>::max();
131+
}
56132

57-
// Process input buffer by chunks of k_denoiseFrameSize, put result into out buffer to return into range [-1.f,1.f]
133+
srcIn.data_in += srcIn.input_frames_used;
134+
srcIn.input_frames -= srcIn.input_frames_used;
135+
136+
float *denoise_in = &m_inBuffer[0];
137+
while(srcIn.output_frames_gen)
138+
{
139+
const int wrote = rnnoise_add_samples(m_denoiseState.get(), denoise_in, srcIn.output_frames_gen);
140+
denoise_in += wrote;
141+
srcIn.output_frames_gen -= wrote;
142+
143+
if (rnnoise_get_needed(m_denoiseState.get()) == 0)
144+
{
145+
rnnoise_process_frame(m_denoiseState.get(), &m_outBuffer[m_outBufferW]);
146+
147+
// scale the levels back to normal
148+
for(int32_t i = 0; i < k_denoiseFrameSize; ++i)
149+
m_outBuffer[m_outBufferW + i] *= mul;
150+
151+
m_outBufferW += k_denoiseFrameSize;
152+
m_outBufferA += k_denoiseFrameSize;
153+
if (m_outBufferW == m_outBuffer.size())
154+
m_outBufferW = 0;
155+
}
156+
157+
// resample what we can to the output
158+
while(m_outBufferA && srcOut.output_frames)
159+
{
160+
srcOut.data_in = &m_outBuffer[m_outBufferR];
161+
srcOut.input_frames = m_outBufferW < m_outBufferR ? m_outBuffer.size() - m_outBufferR : m_outBufferW - m_outBufferR;
162+
163+
if (m_resample)
164+
src_process(m_srcOut.get(), &srcOut);
165+
else
58166
{
59-
float *outBufferWriteStart = (m_outputBuffer.end() - framesToProcess).base();
60-
61-
for (size_t i = 0; i < samplesToProcess; i++) {
62-
float *currentOutBuffer = &outBufferWriteStart[i * k_denoiseFrameSize];
63-
float *currentInBuffer = &m_inputBuffer[i * k_denoiseFrameSize];
64-
rnnoise_process_frame(m_denoiseState.get(), currentOutBuffer, currentInBuffer);
65-
66-
for (size_t j = 0; j < k_denoiseFrameSize; j++) {
67-
currentOutBuffer[j] /= std::numeric_limits<short>::max();
68-
}
69-
}
167+
// simply copy the buffer if we are not resampling
168+
srcOut.input_frames_used = srcOut.input_frames;
169+
if (srcOut.input_frames_used > srcOut.output_frames)
170+
srcOut.input_frames_used = srcOut.output_frames;
171+
memcpy(srcOut.data_out, srcOut.data_in, srcOut.input_frames_used * sizeof(float));
70172
}
71173

72-
const size_t toCopyIntoOutput = std::min(m_outputBuffer.size(), static_cast<size_t>(sampleFrames));
174+
if (!srcOut.input_frames_used && !srcOut.output_frames_gen)
175+
break;
73176

74-
std::memcpy(out, &m_outputBuffer[0], toCopyIntoOutput * sizeof(float));
177+
m_outBufferR += srcOut.input_frames_used;
178+
m_outBufferA -= srcOut.input_frames_used;
75179

76-
m_inputBuffer.erase(m_inputBuffer.begin(), m_inputBuffer.begin() + framesToProcess);
77-
m_outputBuffer.erase(m_outputBuffer.begin(), m_outputBuffer.begin() + toCopyIntoOutput);
180+
srcOut.data_out += srcOut.output_frames_gen;
181+
srcOut.output_frames -= srcOut.output_frames_gen;
182+
frames += srcOut.output_frames_gen;
78183

79-
if (toCopyIntoOutput < sampleFrames) {
80-
std::fill(out + toCopyIntoOutput, out + sampleFrames, 0.f);
81-
}
184+
if (m_outBufferR == m_outBuffer.size())
185+
m_outBufferR = 0;
186+
}
82187
}
83-
}
84-
85-
void RnNoiseCommonPlugin::createDenoiseState() {
86-
m_denoiseState = std::shared_ptr<DenoiseState>(rnnoise_create(), [](DenoiseState *st) {
87-
rnnoise_destroy(st);
88-
});
188+
}
189+
190+
// if we generated less frames then wanted, pad them across to the right
191+
if (frames && frames < sampleFrames)
192+
{
193+
const size_t pad = sampleFrames - frames;
194+
memmove(out + pad, out, frames);
195+
memset(out, 0, pad);
196+
}
89197
}

0 commit comments

Comments
 (0)