"""Test that sfxr waveforms produce sustained audio (not single-cycle pops). Before the phase-wrap fix, square and sawtooth waveforms would only produce one cycle of audio then become DC, resulting in very quiet output with pops. After the fix, all waveforms should produce comparable output levels. """ import mcrfpy import sys # Generate each waveform with identical envelope params WAVEFORMS = {0: "square", 1: "sawtooth", 2: "sine", 3: "noise"} durations = {} sample_counts = {} for wt, name in WAVEFORMS.items(): buf = mcrfpy.SoundBuffer.sfxr(wave_type=wt, base_freq=0.3, env_attack=0.0, env_sustain=0.3, env_decay=0.4) durations[name] = buf.duration sample_counts[name] = buf.sample_count print(f"{name}: {buf.sample_count} samples, {buf.duration:.4f}s") # All waveforms should produce similar duration (same envelope) # Before fix, they all had the same envelope params so durations should match for name, dur in durations.items(): assert dur > 0.1, f"FAIL: {name} duration too short ({dur:.4f}s)" print(f" {name} duration OK: {dur:.4f}s") # Test that normalize() on a middle slice doesn't massively amplify # (If the signal is DC/near-silent, normalize would boost enormously, # changing sample values from near-0 to near-max. With sustained waveforms, # the signal is already substantial so normalize has less effect.) for wt, name in [(0, "square"), (1, "sawtooth")]: buf = mcrfpy.SoundBuffer.sfxr(wave_type=wt, base_freq=0.3, env_attack=0.0, env_sustain=0.3, env_decay=0.4) # Slice the sustain portion (not attack/decay edges) mid = buf.slice(0.05, 0.15) if mid.sample_count > 0: # Apply pitch_shift as a transformation test - should change duration shifted = mid.pitch_shift(2.0) expected_count = mid.sample_count // 2 actual_count = shifted.sample_count ratio = actual_count / max(1, expected_count) print(f" {name} pitch_shift(2.0): {mid.sample_count} -> {shifted.sample_count} " f"(expected ~{expected_count}, ratio={ratio:.2f})") assert 0.8 < ratio < 1.2, f"FAIL: {name} pitch shift ratio off ({ratio:.2f})" else: print(f" {name} slice returned empty (skipping pitch test)") # Generate a tone and sfxr with same waveform to compare # The tone generator was already working, sfxr was broken tone_sq = mcrfpy.SoundBuffer.tone(440, 0.3, "square") sfxr_sq = mcrfpy.SoundBuffer.sfxr(wave_type=0, base_freq=0.5, env_attack=0.0, env_sustain=0.3, env_decay=0.0) print(f"\nComparison - tone square: {tone_sq.sample_count} samples, {tone_sq.duration:.4f}s") print(f"Comparison - sfxr square: {sfxr_sq.sample_count} samples, {sfxr_sq.duration:.4f}s") # Both should have substantial sample counts assert tone_sq.sample_count > 10000, f"FAIL: tone square too short" assert sfxr_sq.sample_count > 5000, f"FAIL: sfxr square too short" print("\nPASS: All waveform tests passed") sys.exit(0)