@@ -255,3 +255,93 @@ def test_reduce_noise_with_hpf():
255255 # The 440 Hz tone should be preserved, the 40 Hz rumble attenuated
256256 corr_tone = np .corrcoef (tone , result )[0 , 1 ]
257257 assert corr_tone > 0.95
258+
259+
260+ # --- Mode selection tests ---
261+
262+
263+ def test_stationary_mode_explicit ():
264+ """mode='stationary' with explicit noise clip should reduce noise."""
265+ rng = np .random .default_rng (42 )
266+ noise_only = (0.1 * rng .standard_normal (SR // 2 )).astype (np .float32 )
267+ tone_noisy = _make_noisy_tone (duration_s = 1.5 , noise_level = 0.1 )
268+ signal = np .concatenate ([noise_only , tone_noisy ])
269+ noise_clip = signal [:SR // 2 ]
270+
271+ reduced = reduce_noise (signal , SR , noise_clip = noise_clip ,
272+ strength = 1.0 , mode = "stationary" )
273+ original_rms = np .sqrt (np .mean (signal [:SR // 2 ].astype (np .float64 ) ** 2 ))
274+ reduced_rms = np .sqrt (np .mean (reduced [:SR // 2 ].astype (np .float64 ) ** 2 ))
275+ assert reduced_rms < original_rms
276+
277+
278+ def test_adaptive_mode_explicit ():
279+ """mode='adaptive' should reduce noise without explicit noise clip."""
280+ noisy = _make_noisy_tone (duration_s = 2.0 , noise_level = 0.15 )
281+ reduced = reduce_noise (noisy , SR , strength = 0.75 , mode = "adaptive" )
282+ assert reduced .shape == noisy .shape
283+ assert reduced .dtype == np .float32
284+
285+
286+ def test_auto_mode_with_guide_stem_uses_stationary ():
287+ """Auto mode with a good stem should use stationary (stronger NR)."""
288+ rng = np .random .default_rng (42 )
289+ silence = np .zeros (SR , dtype = np .float32 )
290+ tone = _make_tone (duration_s = 1.0 , freq = 440 )
291+ vocal_sep = np .concatenate ([silence , tone ])
292+
293+ noise_full = (0.1 * rng .standard_normal (SR * 2 )).astype (np .float32 )
294+ vocal_rec = noise_full .copy ()
295+ vocal_rec [SR :] += tone
296+
297+ reduced = reduce_noise (vocal_rec , SR , strength = 1.0 ,
298+ guide_stem = vocal_sep , mode = "auto" )
299+ # Should successfully reduce noise in the silent region
300+ original_rms = np .sqrt (np .mean (vocal_rec [:SR ].astype (np .float64 ) ** 2 ))
301+ reduced_rms = np .sqrt (np .mean (reduced [:SR ].astype (np .float64 ) ** 2 ))
302+ assert reduced_rms < original_rms
303+
304+
305+ def test_auto_mode_without_guide_uses_adaptive ():
306+ """Auto mode without stem or clip should fallback to adaptive."""
307+ noisy = _make_noisy_tone (duration_s = 2.0 , noise_level = 0.1 )
308+ reduced = reduce_noise (noisy , SR , strength = 0.75 , mode = "auto" )
309+ assert reduced .shape == noisy .shape
310+ assert reduced .dtype == np .float32
311+
312+
313+ def test_smoothing_parameters_accepted ():
314+ """Custom freq/time smoothing values should not crash."""
315+ noisy = _make_noisy_tone (duration_s = 1.0 , noise_level = 0.1 )
316+ result = reduce_noise (noisy , SR , strength = 0.5 ,
317+ freq_smooth_hz = 200 , time_smooth_ms = 100 )
318+ assert result .shape == noisy .shape
319+ assert np .all (np .isfinite (result ))
320+
321+
322+ def test_n_std_thresh_parameter ():
323+ """Lower n_std_thresh should produce more aggressive noise reduction."""
324+ rng = np .random .default_rng (42 )
325+ noise_only = (0.1 * rng .standard_normal (SR // 2 )).astype (np .float32 )
326+ tone_noisy = _make_noisy_tone (duration_s = 1.5 , noise_level = 0.1 )
327+ signal = np .concatenate ([noise_only , tone_noisy ])
328+ noise_clip = signal [:SR // 2 ]
329+
330+ reduced_mild = reduce_noise (signal , SR , noise_clip = noise_clip ,
331+ strength = 1.0 , mode = "stationary" ,
332+ n_std_thresh = 3.0 )
333+ reduced_aggressive = reduce_noise (signal , SR , noise_clip = noise_clip ,
334+ strength = 1.0 , mode = "stationary" ,
335+ n_std_thresh = 0.5 )
336+ rms_mild = np .sqrt (np .mean (reduced_mild [:SR // 2 ].astype (np .float64 ) ** 2 ))
337+ rms_aggressive = np .sqrt (np .mean (
338+ reduced_aggressive [:SR // 2 ].astype (np .float64 ) ** 2 ))
339+ assert rms_aggressive <= rms_mild
340+
341+
342+ def test_use_torch_false_explicit ():
343+ """use_torch=False should work (CPU path)."""
344+ noisy = _make_noisy_tone (duration_s = 1.0 , noise_level = 0.1 )
345+ result = reduce_noise (noisy , SR , strength = 0.5 , use_torch = False )
346+ assert result .shape == noisy .shape
347+ assert result .dtype == np .float32
0 commit comments