57 lines
1.6 KiB
C#
57 lines
1.6 KiB
C#
|
|
using Prismatoid;
|
||
|
|
|
||
|
|
namespace VoiceCat.App.Notifications;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Spoken event announcements via Prismatoid (bindings for the Prism speech library —
|
||
|
|
/// integrates with the active screen reader / system speech, no extra runtime deps).
|
||
|
|
///
|
||
|
|
/// All construction and calls are wrapped so a machine with no available speech backend simply
|
||
|
|
/// degrades to silence rather than crashing the app.
|
||
|
|
/// </summary>
|
||
|
|
public sealed class SpeechAnnouncer : IDisposable
|
||
|
|
{
|
||
|
|
private readonly PrismContext? _context;
|
||
|
|
private readonly object? _backend; // SpeechBackend; held as object to keep this resilient
|
||
|
|
|
||
|
|
public SpeechAnnouncer()
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
_context = new PrismContext();
|
||
|
|
_backend = _context.AcquireBestBackend();
|
||
|
|
}
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
_context?.Dispose();
|
||
|
|
_context = null;
|
||
|
|
_backend = null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>True when a speech backend is available on this machine.</summary>
|
||
|
|
public bool Available => _backend is not null;
|
||
|
|
|
||
|
|
/// <summary>Speak <paramref name="text"/>. Queued (does not interrupt prior speech) so a
|
||
|
|
/// burst of events is read in order.</summary>
|
||
|
|
public void Speak(string text)
|
||
|
|
{
|
||
|
|
if (string.IsNullOrWhiteSpace(text)) return;
|
||
|
|
try
|
||
|
|
{
|
||
|
|
if (_backend is { } b)
|
||
|
|
((dynamic)b).Speak(text, interrupt: false);
|
||
|
|
}
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
/* never surface a speech failure */
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Dispose()
|
||
|
|
{
|
||
|
|
try { (_backend as IDisposable)?.Dispose(); } catch { /* ignore */ }
|
||
|
|
_context?.Dispose();
|
||
|
|
}
|
||
|
|
}
|