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
7074static 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
389493static 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 }
0 commit comments