From 84474731e7693c4ba1afed6bca7aeed8a66eb9ba Mon Sep 17 00:00:00 2001 From: Mute Date: Mon, 17 Aug 2026 06:45:06 +0000 Subject: [PATCH] fix click: 5ms fade-out from last sample to zero before silence padding --- Robovoice.App/AudioOutput.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Robovoice.App/AudioOutput.cs b/Robovoice.App/AudioOutput.cs index 72c14e0..33a23a5 100644 --- a/Robovoice.App/AudioOutput.cs +++ b/Robovoice.App/AudioOutput.cs @@ -8,6 +8,7 @@ internal sealed class AudioOutput : IDisposable private BufferedWaveProvider? _bufferProvider; private int _sampleRate; private string _deviceName = string.Empty; + private float _lastSample; private bool _disposed; public Action? Log { get; set; } @@ -18,6 +19,7 @@ internal sealed class AudioOutput : IDisposable _sampleRate = sampleRate; _deviceName = deviceName ?? string.Empty; + _lastSample = 0f; Stop(); @@ -42,6 +44,9 @@ internal sealed class AudioOutput : IDisposable ObjectDisposedException.ThrowIf(_disposed, this); if (_bufferProvider is null) return; + if (samples.Length > 0) + _lastSample = samples[^1]; + byte[] bytes = new byte[samples.Length * sizeof(float)]; Buffer.BlockCopy(samples, 0, bytes, 0, bytes.Length); _bufferProvider.AddSamples(bytes, 0, bytes.Length); @@ -52,8 +57,21 @@ internal sealed class AudioOutput : IDisposable ObjectDisposedException.ThrowIf(_disposed, this); if (_bufferProvider is null || _sampleRate == 0) return; + // Fade out from last sample to zero over 5ms to avoid click + int fadeSamples = (int)(_sampleRate * 0.005); + float[] fade = new float[fadeSamples]; + for (int i = 0; i < fadeSamples; i++) + { + float t = (float)i / fadeSamples; + fade[i] = _lastSample * (1f - t); + } + WriteSamples(fade); + + // Then 500ms of silence to let NAudio drain int padSamples = (int)(_sampleRate * 0.5); WriteSamples(new float[padSamples]); + + _lastSample = 0f; } private void OnPlaybackStopped(object? sender, StoppedEventArgs e) @@ -71,6 +89,7 @@ internal sealed class AudioOutput : IDisposable } _bufferProvider?.ClearBuffer(); _bufferProvider = null; + _lastSample = 0f; } private static int FindDevice(string? deviceName)