Skip to content

Commit 76be501

Browse files
authored
Fix rnnoise only works with 48khz audio (#1266)
This change converts the sampling frequency to whatever was requested by the consumer. It also improves a rare underflow condition in the filter and uses fake samples to fill the gap.
1 parent 7010653 commit 76be501

2 files changed

Lines changed: 144 additions & 6 deletions

File tree

src/modules/rnnoise/filter_rnnoise.c

Lines changed: 108 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
* 02110-1301 USA
1919
*/
2020

21+
#include <framework/mlt_factory.h>
2122
#include <framework/mlt_filter.h>
2223
#include <framework/mlt_frame.h>
2324
#include <framework/mlt_log.h>
@@ -65,6 +66,9 @@ typedef struct
6566
// We delay the dry signal by the same amount so both are aligned.
6667
float dry_ring[MAX_CHANNELS][960];
6768
int dry_ring_pos;
69+
70+
// Use if requested sample rate is not 48kHz.
71+
mlt_filter resample_filter;
6872
} private_data;
6973

7074
static void reset_all(mlt_filter filter, private_data *pdata, int channels)
@@ -105,15 +109,31 @@ static int rnnoise_get_audio(mlt_frame frame,
105109
mlt_filter filter = mlt_frame_pop_audio(frame);
106110
mlt_properties filter_props = MLT_FILTER_PROPERTIES(filter);
107111
private_data *pdata = (private_data *) filter->child;
112+
mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter));
113+
double fps = mlt_profile_fps(profile);
114+
mlt_position frame_pos = mlt_frame_get_position(frame);
115+
int requested_samples = *samples;
116+
int requested_frequency = *frequency;
117+
118+
if (fps <= 0.0)
119+
fps = 25.0;
108120

109121
// RNNoise requires 48 kHz float input
110122
*frequency = RNNOISE_RATE;
111123
*format = mlt_audio_float;
124+
*samples = mlt_audio_calculate_frame_samples(fps, RNNOISE_RATE, frame_pos);
112125

113126
int error = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples);
114127
if (error || *samples == 0)
115128
return error;
116129

130+
if (*frequency != RNNOISE_RATE) {
131+
mlt_log_warning(MLT_FILTER_SERVICE(filter),
132+
"RNNoise filter requires 48 kHz input, got %d Hz; bypassing\n",
133+
*frequency);
134+
return 0;
135+
}
136+
117137
if (*format != mlt_audio_float && frame->convert_audio != NULL)
118138
frame->convert_audio(frame, buffer, format, mlt_audio_float);
119139

@@ -122,8 +142,6 @@ static int rnnoise_get_audio(mlt_frame frame,
122142
const int n_samples = *samples;
123143
const int ch = *channels;
124144
const int rnn_frame = rnnoise_get_frame_size(); // always 480
125-
mlt_position frame_pos = mlt_frame_get_position(frame);
126-
127145
if (ch > MAX_CHANNELS) {
128146
mlt_log_warning(MLT_FILTER_SERVICE(filter),
129147
"RNNoise filter supports up to %d channels, got %d; bypassing\n",
@@ -346,6 +364,64 @@ static int rnnoise_get_audio(mlt_frame frame,
346364
for (int s = 0; s < new_in_carry; s++)
347365
pdata->in_carry[c][s] = in_ch[in_carry_src + s];
348366

367+
// 4. If this frame is still short, process one extra RNNoise chunk.
368+
// This avoids immediate output zero-fill by creating one more chunk and
369+
// pushing any excess into out_carry for subsequent frames.
370+
//
371+
// Important: this extra chunk is only partially "real" input. It starts
372+
// from the true leftover tail of this frame and then zero-pads the rest
373+
// of the 480-sample RNNoise block. Therefore, some produced samples are
374+
// synthesized continuation rather than fully observed source audio.
375+
if (out_pos < n_samples) {
376+
int extra_base = n_samples - new_in_carry;
377+
int missing_state_logged_extra = 0;
378+
379+
for (int s = 0; s < rnn_frame; s++) {
380+
int q = extra_base + s;
381+
// q in [0, n_samples) reads real current-frame tail samples.
382+
// q outside that range is zero padding used to complete the block.
383+
float raw = (q >= 0 && q < n_samples) ? in_ch[q] : 0.0f;
384+
frame_in[s] = raw * 32768.0f;
385+
}
386+
387+
if (pdata->states[c]) {
388+
rnnoise_process_frame(pdata->states[c], frame_out, frame_in);
389+
} else {
390+
if (!missing_state_logged_extra) {
391+
mlt_log_error(MLT_FILTER_SERVICE(filter),
392+
"Missing RNNoise state for channel %d; bypassing denoise\n",
393+
c);
394+
missing_state_logged_extra = 1;
395+
}
396+
memcpy(frame_out, frame_in, sizeof(frame_out));
397+
}
398+
399+
for (int s = 0; s < rnn_frame; s++) {
400+
int q = extra_base + s;
401+
// Keep dry/wet latency alignment unchanged even for padded input;
402+
// this preserves timing but can include synthesized content.
403+
float raw = (q >= 0 && q < n_samples) ? in_ch[q] : 0.0f;
404+
float denoised = frame_out[s] / 32768.0f;
405+
int ring_idx = pdata->dry_ring_pos % ring_size;
406+
float delayed_raw = pdata->dry_ring[c][ring_idx];
407+
pdata->dry_ring[c][ring_idx] = raw;
408+
pdata->dry_ring_pos++;
409+
float mixed = delayed_raw * (1.0f - (float) mix) + denoised * (float) mix;
410+
411+
if (out_pos < n_samples) {
412+
out_ch[out_pos++] = mixed;
413+
} else if (carry_pos < OUT_CARRY_CAPACITY) {
414+
out_carry_ch[carry_pos++] = mixed;
415+
} else {
416+
mlt_log_warning(MLT_FILTER_SERVICE(filter),
417+
"frame=%d ch=%d out_carry overflow at pos=%d (extra chunk)\n",
418+
(int) frame_pos,
419+
c,
420+
carry_pos);
421+
}
422+
}
423+
}
424+
349425
if (out_pos < n_samples) {
350426
memset(out_ch + out_pos, 0, (size_t) (n_samples - out_pos) * sizeof(float));
351427
}
@@ -382,8 +458,36 @@ static int rnnoise_get_audio(mlt_frame frame,
382458
*buffer = out.data;
383459
*format = mlt_audio_float;
384460

461+
if (requested_frequency && requested_frequency != *frequency) {
462+
if (!pdata->resample_filter) {
463+
pdata->resample_filter = mlt_factory_filter(mlt_service_profile(
464+
MLT_FILTER_SERVICE(filter)),
465+
"resample",
466+
NULL);
467+
if (!pdata->resample_filter) {
468+
pdata->resample_filter = mlt_factory_filter(mlt_service_profile(
469+
MLT_FILTER_SERVICE(filter)),
470+
"swresample",
471+
NULL);
472+
}
473+
}
474+
if (pdata->resample_filter) {
475+
mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "audio_frequency", *frequency);
476+
mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "audio_channels", *channels);
477+
mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "audio_samples", *samples);
478+
mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "audio_format", *format);
479+
mlt_filter_process(pdata->resample_filter, frame);
480+
// Final call to get_audio() to apply the resample filter
481+
*frequency = requested_frequency;
482+
*samples = requested_samples;
483+
if (*samples <= 0)
484+
*samples = mlt_audio_calculate_frame_samples(fps, *frequency, frame_pos);
485+
error = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples);
486+
}
487+
}
488+
385489
mlt_service_unlock(MLT_FILTER_SERVICE(filter));
386-
return 0;
490+
return error;
387491
}
388492

389493
static mlt_frame filter_process(mlt_filter filter, mlt_frame frame)
@@ -404,6 +508,7 @@ static void close_filter(mlt_filter filter)
404508
}
405509
free(pdata->out_carry[i]);
406510
}
511+
mlt_filter_close(pdata->resample_filter);
407512
free(pdata);
408513
filter->child = NULL;
409514
}

src/modules/rnnoise/link_rnnoise.c

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
*/
2020

2121
#include <framework/mlt.h>
22-
#include <math.h>
22+
2323
#include <rnnoise.h>
24+
2425
#include <stdio.h>
2526
#include <stdlib.h>
2627
#include <string.h>
@@ -65,6 +66,9 @@ typedef struct
6566
// denoised output when mix is between 0 and 1.
6667
float dry_ring[MAX_CHANNELS][960];
6768
int dry_ring_pos;
69+
70+
// Use if requested sample rate is not 48kHz.
71+
mlt_filter resample_filter;
6872
} private_data;
6973

7074
static void reset_state(mlt_link self)
@@ -151,13 +155,14 @@ static int link_get_audio(mlt_frame frame,
151155
if (link_fps <= 0.0)
152156
link_fps = 25.0;
153157
mlt_position frame_pos = mlt_frame_get_position(frame);
158+
int requested_samples = *samples;
159+
int requested_frequency = *frequency;
154160

155161
// Force 48kHz float for RNNoise
156162
*frequency = RNNOISE_RATE;
157163
*format = mlt_audio_float;
158164
*channels = *channels <= 0 ? 2 : *channels;
159-
if (*samples <= 0)
160-
*samples = mlt_audio_calculate_frame_samples(link_fps, RNNOISE_RATE, frame_pos);
165+
*samples = mlt_audio_calculate_frame_samples(link_fps, RNNOISE_RATE, frame_pos);
161166

162167
mlt_service_lock(MLT_LINK_SERVICE(self));
163168

@@ -430,6 +435,33 @@ static int link_get_audio(mlt_frame frame,
430435

431436
pdata->expected_frame = frame_pos + 1;
432437

438+
if (requested_frequency && requested_frequency != *frequency) {
439+
if (!pdata->resample_filter) {
440+
pdata->resample_filter = mlt_factory_filter(mlt_service_profile(MLT_LINK_SERVICE(self)),
441+
"resample",
442+
NULL);
443+
if (!pdata->resample_filter) {
444+
pdata->resample_filter = mlt_factory_filter(mlt_service_profile(
445+
MLT_LINK_SERVICE(self)),
446+
"swresample",
447+
NULL);
448+
}
449+
}
450+
if (pdata->resample_filter) {
451+
mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "audio_frequency", *frequency);
452+
mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "audio_channels", *channels);
453+
mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "audio_samples", *samples);
454+
mlt_properties_set_int(MLT_FRAME_PROPERTIES(frame), "audio_format", *format);
455+
mlt_filter_process(pdata->resample_filter, frame);
456+
// Final call to get_audio() to apply the resample filter
457+
*frequency = requested_frequency;
458+
*samples = requested_samples;
459+
if (*samples <= 0)
460+
*samples = mlt_audio_calculate_frame_samples(link_fps, *frequency, frame_pos);
461+
error = mlt_frame_get_audio(frame, buffer, format, frequency, channels, samples);
462+
}
463+
}
464+
433465
mlt_service_unlock(MLT_LINK_SERVICE(self));
434466
return error;
435467
}
@@ -535,6 +567,7 @@ static void link_close(mlt_link self)
535567
}
536568
free(pdata->out_carry[c]);
537569
}
570+
mlt_filter_close(pdata->resample_filter);
538571
free(pdata);
539572
}
540573
self->child = NULL;

0 commit comments

Comments
 (0)