2
0
mirror of https://github.com/9ParsonsB/Pulsar.git synced 2025-04-05 17:39:39 -04:00
Jonathan Miller 1950d477fd
Herald v2 (#74)
* Add speech rate setting

* Add volume slider

* New speech manager skeleton

* User API key from resx

* Implement voice list retrieve via new api

* Rewrite to use ObAPI, remove all dependancies

* Use volume setting

* Clean up using statements

* Volume and timing adjustments

* Lookup rate value

* Use numeric rates for tighter spread

* Manage plugin data folder via core interface

* Add check that nullable settings are not null.

* Get file size before it's deleted.

* Improve old settings migration.

* Ignore cache sizes below 1MB

* Re-index orphaned files in cache, purge legacy wav files.

* Call top level error logging for native voice exception.

* Async title and detail requests to remove pause

* Remove NetCoreAudio use of temp files.

* Remove orphan using.
2022-04-04 11:58:30 -02:30

104 lines
3.0 KiB
C#

using Observatory.Framework;
using System.Collections.Generic;
using System.Xml;
using System.Linq;
using System.Threading.Tasks;
using System.Speech.Synthesis;
using System.Runtime.InteropServices;
namespace Observatory.NativeNotification
{
public class NativeVoice
{
private Queue<NotificationArgs> notificationEvents;
private bool processing;
public NativeVoice()
{
notificationEvents = new();
processing = false;
}
public void EnqueueAndAnnounce(NotificationArgs eventArgs)
{
notificationEvents.Enqueue(eventArgs);
if (!processing)
{
processing = true;
ProcessQueueAsync();
}
}
private async void ProcessQueueAsync()
{
try
{
await Task.Factory.StartNew(ProcessQueue);
}
catch (System.Exception ex)
{
ObservatoryCore.LogError(ex, " - Native Voice Notifier");
}
}
private void ProcessQueue()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
string voice = Properties.Core.Default.VoiceSelected;
var speech = new SpeechSynthesizer()
{
Volume = Properties.Core.Default.VoiceVolume,
Rate = Properties.Core.Default.VoiceRate
};
speech.SelectVoice(voice);
while (notificationEvents.Any())
{
var notification = notificationEvents.Dequeue();
if (notification.TitleSsml?.Length > 0)
{
string ssml = AddVoiceToSsml(notification.TitleSsml, voice);
speech.SpeakSsml(ssml);
}
else
{
speech.Speak(notification.Title);
}
if (notification.DetailSsml?.Length > 0)
{
string ssml = AddVoiceToSsml(notification.DetailSsml, voice);
speech.SpeakSsml(ssml);
}
else
{
speech.Speak(notification.Detail);
}
}
}
processing = false;
}
private string AddVoiceToSsml(string ssml, string voiceName)
{
XmlDocument ssmlDoc = new();
ssmlDoc.LoadXml(ssml);
var ssmlNamespace = ssmlDoc.DocumentElement.NamespaceURI;
XmlNamespaceManager ssmlNs = new(ssmlDoc.NameTable);
ssmlNs.AddNamespace("ssml", ssmlNamespace);
var voiceNode = ssmlDoc.SelectSingleNode("/ssml:speak/ssml:voice", ssmlNs);
voiceNode.Attributes.GetNamedItem("name").Value = voiceName;
return ssmlDoc.OuterXml;
}
}
}