mirror of
https://github.com/RedWizardsLab/EchoHub.git
synced 2026-09-04 00:26:07 +02:00
Merge pull request #36 from HueByte/dev_fix_notification_sound
fix: error when playing notification sounds in quick succession
This commit is contained in:
@@ -13,6 +13,7 @@ Follow-up patch release for v0.2.9 addressing auto-updater regressions, adding a
|
||||
- Fix pre-update backup failing when a Serilog-held log file is locked — `UpdateBackupService` now enumerates files manually, skips the `logs/` directory and `.log` files, and logs-and-continues on `IOException`/`UnauthorizedAccessException` instead of aborting the whole backup
|
||||
- Simplify update progress dispatch — remove redundant `Application.Invoke` wrappers around progress updates that are already called from the UI thread (introduced while fixing the freeze above)
|
||||
- Fix cursor position being reset to the start of the line when auto-completing commands in the CLI app — insertion point is now moved to the end of the completed text
|
||||
- Fix notification sounds crashing or being silently dropped when several arrive in quick succession — playback is now serialized through a semaphore that's held for the duration of each sound (using `PlaybackFinished` with a 10s safety timeout) and always released in `finally`, so back-to-back notifications queue up and play in order instead of racing the underlying audio player (fixes #20)
|
||||
|
||||
## Refactoring
|
||||
|
||||
|
||||
@@ -6,7 +6,11 @@ namespace EchoHub.Client.Services;
|
||||
|
||||
public class NotificationSoundService
|
||||
{
|
||||
// Safety net: if PlaybackFinished never fires we don't want to block future notifications forever.
|
||||
private static readonly TimeSpan PlaybackTimeout = TimeSpan.FromSeconds(10);
|
||||
|
||||
private readonly Player _player = new();
|
||||
private readonly SemaphoreSlim _lock = new(1, 1);
|
||||
private readonly NotificationConfig _config;
|
||||
private string? _resolvedSoundPath;
|
||||
|
||||
@@ -41,18 +45,31 @@ public class NotificationSoundService
|
||||
|
||||
private async Task PlayInternal()
|
||||
{
|
||||
await _lock.WaitAsync();
|
||||
|
||||
// _player.Play returns as soon as playback starts, so we wait on PlaybackFinished
|
||||
// to hold the lock for the duration of the sound. A one-shot handler + timeout
|
||||
// keeps the finally release robust: never-fires → timeout; fires twice → ignored
|
||||
// (TrySetResult); handler throws → caller's catch still runs finally.
|
||||
var completion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
void OnFinished(object? s, EventArgs e) => completion.TrySetResult();
|
||||
_player.PlaybackFinished += OnFinished;
|
||||
|
||||
try
|
||||
{
|
||||
if (_player.Playing)
|
||||
await _player.Stop();
|
||||
|
||||
await _player.SetVolume(_config.Volume);
|
||||
await _player.Play(_resolvedSoundPath!);
|
||||
await Task.WhenAny(completion.Task, Task.Delay(PlaybackTimeout));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Failed to play notification sound");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_player.PlaybackFinished -= OnFinished;
|
||||
_lock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private void ResolveSoundPath()
|
||||
|
||||
Reference in New Issue
Block a user