Decompiled source of BoomboxTTS v1.0.1
BoomboxMod.dll
Decompiled 9 months agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Pipes; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BoomboxMod.Voice; using GameNetcodeStuff; using HarmonyLib; using LethalNetworkAPI; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("BoomboxMod")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("BoomboxMod")] [assembly: AssemblyCopyright("Copyright © 2024")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("a4a64f24-92c8-43f6-a94e-b511157c3b05")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BoomboxMod { public class BoomboxClient { public static readonly int OutBufferSize = 8192; public static readonly int InBufferSize = 8388608; private readonly NamedPipeClientStream clientStream; private readonly StreamWriter streamWriter; private readonly BinaryReader binaryReader; public float[] buffer = new float[InBufferSize]; public byte[] audioByteBuffer = new byte[InBufferSize * 2]; public float audioLengthSeconds = 0f; public bool connected = false; public BoomboxClient() { try { clientStream = new NamedPipeClientStream("BOOMBOX"); streamWriter = new StreamWriter(clientStream, Encoding.UTF8, OutBufferSize, leaveOpen: true); binaryReader = new BinaryReader(clientStream, Encoding.UTF8, leaveOpen: true); } catch (IOException data) { BoomboxPlugin.Error(data); } } public void Initialize() { CreateServer(); ConnectToServer(); connected = true; } private void CreateServer() { Process[] processesByName = Process.GetProcessesByName("BoomboxSynthesizer"); Process[] array = processesByName; foreach (Process process in array) { process.Close(); } string baseDirectory = BoomboxPlugin.GetBaseDirectory(); Process process2 = Process.Start(baseDirectory + "BoomboxSynthesizer.exe"); if (process2 == null) { BoomboxPlugin.Error("Failed to start BoomboxSynthesizer."); } else { BoomboxPlugin.Log("Started BoomboxSynthesizer"); } } public string FilterMessageContent(string input) { return input.Replace("\r", "").Replace("\n", "").Replace("_", " "); } private void ConnectToServer() { BoomboxPlugin.Log("Connecting to server..."); try { clientStream.Connect(7500); } catch (TimeoutException arg) { BoomboxPlugin.Error($"Connection timed out: {arg}"); } catch (IOException arg2) { BoomboxPlugin.Error($"Failed to connect to server: {arg2}"); } } public MessageData Speak(int playerId, string content, string voice, float volumeScale = 1f) { if (!connected) { BoomboxPlugin.Error("Tried to speak before connection established!"); return default(MessageData); } SendMessageToServer(FilterMessageContent(content), voice); ClearSamplesBuffer(); int num = binaryReader.ReadInt32(); if (num > audioByteBuffer.Length) { audioByteBuffer = new byte[num]; } Array.Clear(buffer, 0, num); int num2 = 0; binaryReader.Read(audioByteBuffer, 0, num); for (int i = 0; i < Math.Min(num / 2, buffer.Length); i++) { float num3 = volumeScale * ((float)BitConverter.ToInt16(audioByteBuffer, i * 2) / 32767f); buffer[i] = num3; if (num3 != 0f) { num2 = i; } } float lengthSeconds = (float)num2 / 11025f; BoomboxPlugin.Log("Completed: " + content); return new MessageData(playerId, buffer, lengthSeconds); } private void SendMessageToServer(string content, string voice) { BoomboxPlugin.Log("Sending to server: " + content); if (!clientStream.IsConnected) { CreateServer(); ConnectToServer(); } try { streamWriter.WriteLine(voice + "=" + content); streamWriter.Flush(); } catch (Exception data) { BoomboxPlugin.Error(data); } } private void ClearSamplesBuffer() { for (int i = 0; i < buffer.Length; i++) { buffer[i] = 0f; } } } [BepInPlugin("ExDrill.BoomboxMod", "BoomboxMod", "1.0.0")] public class BoomboxPlugin : BaseUnityPlugin { public const string ModGUID = "ExDrill.BoomboxMod"; public const string ModName = "BoomboxMod"; public const string ModVersion = "1.0.0"; public static BoomboxClient Client; public static bool IsNiceChatLoaded; public readonly Harmony harmony = new Harmony("ExDrill.BoomboxMod"); protected static ManualLogSource logger; public void Awake() { logger = ((BaseUnityPlugin)this).Logger; Client = new BoomboxClient(); IsNiceChatLoaded = false; Client.Initialize(); BoomboxConfig.Initialize(((BaseUnityPlugin)this).Config); harmony.PatchAll(); } public void OnDestroy() { if (Harmony.HasAnyPatches("taffyko.NiceChat")) { Log("Nice Chat detected."); IsNiceChatLoaded = true; } } public static void Log(object data) { ManualLogSource obj = logger; if (obj != null) { obj.LogInfo(data); } } public static void Warn(object data) { ManualLogSource obj = logger; if (obj != null) { obj.LogWarning(data); } } public static void Error(object data) { ManualLogSource obj = logger; if (obj != null) { obj.LogError(data); } } public static string GetBaseDirectory() { return new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath.Replace("BoomboxMod.dll", ""); } } public class BoomboxConfig { public static ConfigEntry<float> Volume; public static ConfigEntry<string> SilencePrefix; private static bool isLoaded; public static void Initialize(ConfigFile config) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown if (!isLoaded) { Volume = config.Bind<float>("Tweaks", "TTS Volume", 0.5f, (ConfigDescription)null); SilencePrefix = config.Bind<string>("Tweaks", "Silence Prefix", "/", new ConfigDescription("TTS by default will be silenced if a message was sent with the / prefix, this can be changed.", (AcceptableValueBase)null, Array.Empty<object>())); isLoaded = true; } } } [HarmonyPatch] public class Patches { private static string lastSentMessage = ""; [PublicNetworkVariable] public static LethalNetworkVariable<Dictionary<string, string>> UserToVoice = new LethalNetworkVariable<Dictionary<string, string>>("boomboxUserToVoice") { Value = new Dictionary<string, string>() }; private static float[] emptyBuffer = new float[BoomboxClient.InBufferSize]; private static readonly List<Message> Queue = new List<Message>(); private static Task<MessageData> currentTask = null; [HarmonyPatch(typeof(HUDManager), "AddPlayerChatMessageClientRpc")] [HarmonyPostfix] public static void AddPlayerChatMessageClientRpcPostfix(HUDManager __instance, string chatMessage, int playerId) { PlayerControllerB[] players = GetPlayers(); if (playerId > players.Length || (Object)(object)players[playerId] == (Object)null) { return; } PlayerControllerB val = players[playerId]; if (VoiceChanged(val, chatMessage)) { return; } BoomboxPlugin.Log(val.playerUsername); if (!UserToVoice.Value.ContainsKey(val.playerUsername)) { UserToVoice.Value[val.playerUsername] = "none"; } if (UserToVoice.Value[val.playerUsername] == "none") { return; } if (!BoomboxPlugin.IsNiceChatLoaded) { if (lastSentMessage == chatMessage) { return; } lastSentMessage = chatMessage; } if (!chatMessage.StartsWith(BoomboxConfig.SilencePrefix.Value)) { Queue.Add(new Message(playerId, chatMessage)); } } [HarmonyPatch(typeof(HUDManager), "Update")] [HarmonyPostfix] public static void UpdatePostfix(HUDManager __instance) { if (currentTask != null) { if (!currentTask.IsCompleted) { return; } if (!currentTask.IsCanceled && !currentTask.IsFaulted) { Speak(__instance.playersManager, currentTask.Result); } currentTask = null; } if (Queue.Count > 0) { Message next = Queue[0]; PlayerControllerB player = __instance.playersManager.allPlayerScripts[next.playerId]; Queue.RemoveAt(0); currentTask = Task.Run(() => BoomboxPlugin.Client.Speak(next.playerId, next.content, UserToVoice.Value[player.playerUsername])); } } public static void Speak(StartOfRound playerManager, MessageData data) { //IL_0235: Unknown result type (might be due to invalid IL or missing references) PlayerControllerB val = playerManager.allPlayerScripts[data.playerId]; if ((Object)(object)val == (Object)null) { BoomboxPlugin.Warn("Couldn't find player"); return; } BoomboxPlugin.Log("Found player"); GameObject orCreateSpeaker = GetOrCreateSpeaker(val); AudioSource component = orCreateSpeaker.GetComponent<AudioSource>(); if ((Object)(object)component == (Object)null) { BoomboxPlugin.Error("Failed to speak, audio source not found!"); return; } if ((Object)(object)component.clip == (Object)null) { component.clip = AudioClip.Create("BOOMBOX_CLIP", BoomboxClient.InBufferSize, 1, 11025, false); } BoomboxPlugin.Log("Adding sample to clip."); component.clip.SetData(emptyBuffer, 0); component.clip.SetData(data.buffer, 0); if (!((Object)(object)SoundManager.Instance.playerVoiceMixers[val.playerClientId] == (Object)null)) { component.outputAudioMixerGroup = SoundManager.Instance.playerVoiceMixers[val.playerClientId]; component.playOnAwake = false; component.rolloffMode = (AudioRolloffMode)2; component.minDistance = 1f; component.maxDistance = 40f; component.dopplerLevel = 0.5f; component.pitch = 1f; component.spatialize = true; component.spatialBlend = (val.isPlayerDead ? 0f : 1f); bool flag = !val.isPlayerDead || StartOfRound.Instance.localPlayerController.isPlayerDead; component.volume = (flag ? BoomboxConfig.Volume.Value : 0f); BoomboxPlugin.Log("Adjusted audio source"); AudioHighPassFilter component2 = orCreateSpeaker.GetComponent<AudioHighPassFilter>(); if ((Object)(object)component2 != (Object)null) { ((Behaviour)component2).enabled = false; } AudioLowPassFilter component3 = orCreateSpeaker.GetComponent<AudioLowPassFilter>(); if ((Object)(object)component3 != (Object)null) { component3.lowpassResonanceQ = 1f; component3.cutoffFrequency = 5000f; } BoomboxPlugin.Log("Adjusted audio filters"); if (component.isPlaying) { component.Stop(); } component.PlayOneShot(component.clip, 1f); RoundManager.Instance.PlayAudibleNoise(orCreateSpeaker.transform.position, 25f, 0.7f, 0, false, 0); BoomboxPlugin.Log("Playing audio!"); } } private static GameObject GetOrCreateSpeaker(PlayerControllerB player) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_005d: Unknown result type (might be due to invalid IL or missing references) Transform obj = ((Component)player).gameObject.transform.Find("Speaker"); GameObject val = ((obj != null) ? ((Component)obj).gameObject : null); if ((Object)(object)val == (Object)null) { BoomboxPlugin.Log("No speaker found, creating a new one..."); GameObject val2 = new GameObject("Speaker"); val2.transform.parent = ((Component)player).transform; val2.transform.localPosition = Vector3.zero; val2.AddComponent<AudioSource>(); val2.AddComponent<AudioHighPassFilter>(); val2.AddComponent<AudioLowPassFilter>(); return val2; } BoomboxPlugin.Log("Speaker found!"); return val; } private static bool VoiceChanged(PlayerControllerB player, string chatMessage) { if (chatMessage.StartsWith("!voice ")) { string[] array = chatMessage.Split(new char[1] { ' ' }); if (array[1] == "david" || array[1] == "zira") { BoomboxPlugin.Log("Switched voice to: " + array[1]); UserToVoice.Value[player.playerUsername] = array[1]; return true; } BoomboxPlugin.Log("Cleared voice"); UserToVoice.Value[player.playerUsername] = "none"; return true; } return false; } public static PlayerControllerB[] GetPlayers() { return HUDManager.Instance.playersManager.allPlayerScripts; } } } namespace BoomboxMod.Voice { public class Message { public readonly int playerId; public readonly string content; public Message(int playerId, string content) { this.playerId = playerId; this.content = content; } } public readonly struct MessageData { public readonly int playerId; public readonly float[] buffer; public readonly float lengthSeconds; public MessageData(int playerId, float[] buffer, float lengthSeconds) { this.playerId = playerId; this.buffer = buffer; this.lengthSeconds = lengthSeconds; } } }
System.Speech.dll
Decompiled 9 months ago
The result has been truncated due to the large size, download it to view full contents!
#define TRACE using System; using System.CodeDom.Compiler; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Net; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using System.Runtime.Serialization; using System.Security; using System.Security.Permissions; using System.Security.Policy; using System.Speech.AudioFormat; using System.Speech.Internal; using System.Speech.Internal.GrammarBuilding; using System.Speech.Internal.ObjectTokens; using System.Speech.Internal.SapiInterop; using System.Speech.Internal.SrgsCompiler; using System.Speech.Internal.SrgsParser; using System.Speech.Internal.Synthesis; using System.Speech.Recognition; using System.Speech.Recognition.SrgsGrammar; using System.Speech.Synthesis; using System.Speech.Synthesis.TtsEngine; using System.Text; using System.Threading; using System.Xml; using System.Xml.XPath; using Microsoft.CSharp; using Microsoft.VisualBasic; using Microsoft.Win32; [assembly: CompilationRelaxations(8)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: ComVisible(false)] [assembly: CLSCompliant(true)] [assembly: AssemblyTitle("System.Speech.dll")] [assembly: AssemblyDescription("System.Speech.dll")] [assembly: AssemblyDefaultAlias("System.Speech.dll")] [assembly: AssemblyCompany("Microsoft Corporation")] [assembly: AssemblyProduct("Microsoft® .NET Framework")] [assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")] [assembly: AssemblyFileVersion("4.8.9037.0")] [assembly: AssemblyInformationalVersion("4.8.9037.0")] [assembly: SatelliteContractVersion("4.0.0.0")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: AssemblyDelaySign(true)] [assembly: AssemblyKeyFile("f:\\dd\\wpf\\src\\windows.snk")] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: DefaultDllImportSearchPaths(DllImportSearchPath.System32 | DllImportSearchPath.AssemblyDirectory)] [assembly: FileIOPermission(SecurityAction.RequestOptional, Unrestricted = true)] [assembly: EnvironmentPermission(SecurityAction.RequestOptional, Unrestricted = true)] [assembly: RegistryPermission(SecurityAction.RequestOptional, Unrestricted = true)] [assembly: SecurityPermission(SecurityAction.RequestOptional, Unrestricted = true)] [assembly: PermissionSet(SecurityAction.RequestOptional, Unrestricted = true)] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("4.0.0.0")] [module: UnverifiableCode] namespace System.IO { internal static class FileHelper { [SecurityCritical] internal static FileStream CreateAndOpenTemporaryFile(out string filePath, FileAccess fileAccess = FileAccess.Write, FileOptions fileOptions = FileOptions.None, string extension = null, string subFolder = "WPF") { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) int num = 5; filePath = null; bool flag = SecurityManager.CurrentThreadRequiresSecurityContextCapture(); string text = Path.GetTempPath(); if (!string.IsNullOrEmpty(subFolder)) { string text2 = Path.Combine(text, subFolder); if (!Directory.Exists(text2)) { if (!flag) { Directory.CreateDirectory(text2); } else { ((CodeAccessPermission)new FileIOPermission((FileIOPermissionAccess)3, text)).Assert(); Directory.CreateDirectory(text2); CodeAccessPermission.RevertAssert(); } } text = text2; } if (flag) { ((CodeAccessPermission)new FileIOPermission((FileIOPermissionAccess)3, text)).Assert(); } FileStream fileStream = null; while (fileStream == null) { string text3 = Path.Combine(text, Path.GetRandomFileName()); if (!string.IsNullOrEmpty(extension)) { text3 = Path.ChangeExtension(text3, extension); } num--; try { fileStream = new FileStream(text3, FileMode.CreateNew, fileAccess, FileShare.None, 4096, fileOptions); filePath = text3; } catch (Exception ex) when (num > 0 && (ex is IOException || ex is UnauthorizedAccessException)) { } } return fileStream; } [SecurityCritical] internal static void DeleteTemporaryFile(string filePath) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrEmpty(filePath)) { if (SecurityManager.CurrentThreadRequiresSecurityContextCapture()) { ((CodeAccessPermission)new FileIOPermission((FileIOPermissionAccess)2, filePath)).Assert(); } try { File.Delete(filePath); } catch (IOException) { } } } } } namespace System.Speech { internal enum SRID { NullParamIllegal, ArrayOfNullIllegal, ParamsEntryNullIllegal, Unavailable, UnexpectedError, CollectionReadOnly, StringCanNotBeEmpty, EnumInvalid, NotSupportedWithThisVersionOfSAPI, NotSupportedWithThisVersionOfSAPI2, NotSupportedWithThisVersionOfSAPIBaseUri, NotSupportedWithThisVersionOfSAPITagFormat, NotSupportedWithThisVersionOfSAPICompareOption, MustBeGreaterThanZero, InvalidXml, OperationAborted, InvariantCultureInfo, DuplicatedEntry, StreamMustBeReadable, StreamMustBeWriteable, StreamMustBeSeekable, StreamEndedUnexpectedly, CannotReadFromDirectory, UnknownMimeFormat, CannotLoadResourceFromManifest, TokenInUse, TokenDeleted, TokenUninitialized, InvalidTokenId, NotFound, NoBackSlash, InvalidRegistryEntry, TokenCannotCreateInstance, InvalidXmlFormat, IncorrectAttributeValue, MissingRequiredAttribute, InvalidRuleRefSelf, InvalidDynamicExport, InvalidToken, MetaNameHTTPEquiv, EmptyRule, InvalidTokenString, InvalidQuotedString, ExportDynamicRule, EmptyDisplayString, EmptyPronunciationString, InvalidPhoneme, MuliplePronunciationString, MultipleDisplayString, RuleRedefinition, EmptyOneOf, InvalidGrammarOrdering, MinMaxOutOfRange, InvalidExampleOrdering, GrammarDefTwice, UnsupportedFormat, InvalidImport, DuplicatedRuleName, RootRuleAlreadyDefined, RuleNameIdConflict, RuleNotDynamic, StateWithNoArcs, NoTerminatingRulePath, RuleRefNoUri, UnavailableProperty, MinGreaterThanMax, ReqConfidenceNotSupported, SapiPropertiesAndSemantics, InvalidAttributeDefinedTwice, GrammarCompilerError, RuleScriptNotFound, DynamicRuleNotFound, RuleScriptInvalidParameters, RuleScriptInvalidReturnType, NoClassname, EmbeddedClassLibraryFailed, CannotFindClass, StrongTypedGrammarNotAGrammar, NoScriptsForRules, ClassNotPublic, MethodNotPublic, IncompatibleLanguageProperties, IncompatibleNamespaceProperties, IncompatibleDebugProperties, CannotLoadDotNetSemanticCode, InvalidSemanticProcessingType, InvalidScriptDefinition, InvalidMethodName, ConstructorNotAllowed, OverloadNotAllowed, OnInitOnPublicRule, ArgumentMismatch, CantGetPropertyFromSerializedInfo, CantFindAConstructor, TooManyArcs, TooManyRulesWithSemanticsGlobals, MaxTransitionsCount, UnknownElement, CircularRuleRef, InvalidFlagsSet, RuleDefinedMultipleTimes, RuleDefinedMultipleTimes2, RuleNotDefined, RootNotDefined, InvalidLanguage, InvalidRuleId, InvalidRepeatProbability, InvalidConfidence, InvalidMinRepeat, InvalidMaxRepeat, InvalidWeight, InvalidName, InvalidValueType, TagFormatNotSet, NoName, NoName1, InvalidSpecialRuleRef, InvalidRuleRef, InvalidNotEmptyElement, InvalidEmptyElement, InvalidEmptyRule, UndefRuleRef, UnsupportedLanguage, UnsupportedPhoneticAlphabet, UnsupportedLexicon, InvalidScriptAttribute, NoLanguageSet, MethodAttributeDefinedMultipeTimes, RuleAttributeDefinedMultipeTimes, InvalidAssemblyReferenceAttribute, InvalidImportNamespaceAttribute, NoUriForSpecialRuleRef, NoAliasForSpecialRuleRef, NoSmlData, InvalidNameValueProperty, InvalidTagInAnEmptyItem, InvalidSrgs, InvalidSrgsNamespace, Line, Position, InvalidVersion, InvalidTagFormat, MissingTagFormat, InvalidGrammarMode, InvalidGrammarAttribute, InvalidRuleAttribute, InvalidRulerefAttribute, InvalidOneOfAttribute, InvalidItemAttribute, InvalidTokenAttribute, InvalidItemRepeatAttribute, InvalidReqConfAttribute, InvalidTagAttribute, InvalidLexiconAttribute, InvalidMetaAttribute, InvalidItemAttribute2, InvalidElement, InvalidRuleScope, InvalidDynamicSetting, InvalidSubsetAttribute, InvalidVoiceElementInPromptOutput, NoRuleId, PromptBuilderInvalideState, PromptBuilderStateEnded, PromptBuilderStateSentence, PromptBuilderStateParagraph, PromptBuilderStateVoice, PromptBuilderStateStyle, PromptBuilderAgeOutOfRange, PromptBuilderMismatchStyle, PromptBuilderMismatchVoice, PromptBuilderMismatchParagraph, PromptBuilderMismatchSentence, PromptBuilderNestedParagraph, PromptBuilderNestedSentence, PromptBuilderInvalidAttribute, PromptBuilderInvalidElement, PromptBuilderInvalidVariant, PromptBuilderDatabaseName, PromptAsyncOperationCancelled, SynthesizerPauseResumeMismatched, SynthesizerInvalidMediaType, SynthesizerUnknownMediaType, SynthesizerSpeakError, SynthesizerInvalidWaveFile, SynthesizerPromptInUse, SynthesizerUnknownPriority, SynthesizerUnknownEvent, SynthesizerVoiceFailed, SynthesizerSetVoiceNoMatch, SynthesizerNoCulture, SynthesizerSyncSpeakWhilePaused, SynthesizerSyncSetOutputWhilePaused, SynthesizerNoCulture2, SynthesizerNoSpeak, SynthesizerSetOutputSpeaking, InvalidSpeakAttribute, UnsupportedAlphabet, GrammarInvalidWeight, GrammarInvalidPriority, DictationInvalidTopic, DictationTopicNotFound, RecognizerGrammarNotFound, RecognizerRuleNotFound, RecognizerInvalidBinaryGrammar, RecognizerRuleNotFoundStream, RecognizerNoRootRuleToActivate, RecognizerNoRootRuleToActivate1, RecognizerRuleActivationFailed, RecognizerAlreadyRecognizing, RecognizerHasNoGrammar, NegativeTimesNotSupported, AudioDeviceFormatError, AudioDeviceError, AudioDeviceInternalError, RecognizerNotFound, RecognizerNotEnabled, RecognitionNotSupported, RecognitionNotSupportedOn64bit, GrammarAlreadyLoaded, RecognizerNoInputSource, GrammarNotLoaded, GrammarLoadingInProgress, GrammarLoadFailed, GrammarWrongRecognizer, NotSupportedOnDictationGrammars, LocalFilesOnly, NotValidAudioFile, NotValidAudioStream, FileNotFound, CannotSetPriorityOnDictation, RecognizerUpdateTableTooLarge, MaxAlternatesInvalid, RecognizerSettingGetError, RecognizerSettingUpdateError, RecognizerSettingNotSupported, ResourceUsageOutOfRange, RateOutOfRange, EndSilenceOutOfRange, RejectionThresholdOutOfRange, ReferencedGrammarNotFound, SapiErrorRuleNotFound2, NoAudioAvailable, ResultNotGrammarAvailable, ResultInvalidFormat, UnhandledVariant, DupSemanticKey, DupSemanticValue, CannotUseCustomFormat, NoPromptEngine, NoPromptEngineInterface, SeekNotSupported, ExtraDataNotPresent, BitsPerSampleInvalid, DataBlockSizeInvalid, NotWholeNumberBlocks, BlockSignatureInvalid, NumberOfSamplesInvalid, SapiErrorUninitialized, SapiErrorAlreadyInitialized, SapiErrorNotSupportedFormat, SapiErrorInvalidFlags, SapiErrorEndOfStream, SapiErrorDeviceBusy, SapiErrorDeviceNotSupported, SapiErrorDeviceNotEnabled, SapiErrorNoDriver, SapiErrorFileMustBeUnicode, InsufficientData, SapiErrorInvalidPhraseID, SapiErrorBufferTooSmall, SapiErrorFormatNotSpecified, SapiErrorAudioStopped0, AudioPaused, SapiErrorRuleNotFound, SapiErrorTTSEngineException, SapiErrorTTSNLPException, SapiErrorEngineBUSY, AudioConversionEnabled, NoHypothesisAvailable, SapiErrorCantCreate, AlreadyInLex, SapiErrorNotInLex, LexNothingToSync, SapiErrorLexVeryOutOfSync, SapiErrorUndefinedForwardRuleRef, SapiErrorEmptyRule, SapiErrorGrammarCompilerInternalError, SapiErrorRuleNotDynamic, SapiErrorDuplicateRuleName, SapiErrorDuplicateResourceName, SapiErrorTooManyGrammars, SapiErrorCircularReference, SapiErrorInvalidImport, SapiErrorInvalidWAVFile, RequestPending, SapiErrorAllWordsOptional, SapiErrorInstanceChangeInvalid, SapiErrorRuleNameIdConflict, SapiErrorNoRules, SapiErrorCircularRuleRef, NoParseFound, SapiErrorInvalidHandle, SapiErrorRemoteCallTimedout, SapiErrorAudioBufferOverflow, SapiErrorNoAudioData, SapiErrorDeadAlternate, SapiErrorHighLowConfidence, SapiErrorInvalidFormatString, SPNotSupportedOnStreamInput, SapiErrorAppLexReadOnly, SapiErrorNoTerminatingRulePath, WordExistsWithoutPronunciation, SapiErrorStreamClosed, SapiErrorNoMoreItems, SapiErrorNotFound, SapiErrorInvalidAudioState, SapiErrorGenericMMSYS, SapiErrorMarshalerException, SapiErrorNotDynamicGrammar, SapiErrorAmbiguousProperty, SapiErrorInvalidRegistrykey, SapiErrorInvalidTokenId, SapiErrorXMLBadSyntax, SapiErrorXMLResourceNotFound, SapiErrorTokenInUse, SapiErrorTokenDeleted, SapiErrorMultilingualNotSupported, SapiErrorExportDynamicRule, SapiErrorSTGF, SapiErrorWordFormat, SapiErrorStreamNotActive, SapiErrorEngineResponseInvalid, SapiErrorSREngineException, SapiErrorStreamPosInvalid, SapiErrorRecognizerInactive, SapiErrorRemoteCallOnWrongThread, SapiErrorRemoteProcessTerminated, SapiErrorRemoteProcessAlreadyRunning, SapiErrorLangIdMismatch, SapiErrorPartialParseFound, SapiErrorNotTopLevelRule, SapiErrorNoRuleActive, SapiErrorLexRequiresCookie, SapiErrorStreamUninitialized, SapiErrorUnused0, SapiErrorNotSupportedLang, SapiErrorVoicePaused, SapiErrorAudioBufferUnderflow, SapiErrorAudioStoppedUnexpectedly, SapiErrorNoWordPronunciation, SapiErrorAlternatesWouldBeInconsistent, SapiErrorNotSupportedForSharedRecognizer, SapiErrorTimeOut, SapiErrorReenterSynchronize, SapiErrorStateWithNoArcs, SapiErrorNotActiveSession, SapiErrorAlreadyDeleted, SapiErrorAudioStopped, SapiErrorRecoXMLGenerationFail, SapiErrorSMLGenerationFail, SapiErrorNotPromptVoice, SapiErrorRootRuleAlreadyDefined, SapiErrorUnused1, SapiErrorUnused2, SapiErrorUnused3, SapiErrorUnused4, SapiErrorUnused5, SapiErrorUnused6, SapiErrorScriptDisallowed, SapiErrorRemoteCallTimedOutStart, SapiErrorRemoteCallTimedOutConnect, SapiErrorSecMgrChangeNotAllowed, SapiErrorCompleteButExtendable, SapiErrorFailedToDeleteFile, SapiErrorSharedEngineDisabled, SapiErrorRecognizerNotFound, SapiErrorAudioNotFound, SapiErrorNoVowel, SapiErrorNotSupportedPhoneme, SapiErrorNoRulesToActivate, SapiErrorNoWordEntryNotification, SapiErrorWordNeedsNormalization, SapiErrorCannotNormalize, LimitReached, NotSupported, SapiErrorTopicNotADaptable, SapiErrorPhonemeConversion, SapiErrorNotSupportedForInprocRecognizer } internal static class SR { private static ResourceManager _resourceManager = new ResourceManager("ExceptionStringTable", typeof(SR).Assembly); internal static string Get(SRID id, params object[] args) { string text = _resourceManager.GetString(id.ToString()); if (string.IsNullOrEmpty(text)) { text = _resourceManager.GetString("Unavailable"); } else if (args != null && args.Length != 0) { text = string.Format(CultureInfo.InvariantCulture, text, args); } return text; } } } namespace System.Speech.Synthesis { public class BookmarkReachedEventArgs : PromptEventArgs { private string _bookmark; private TimeSpan _audioPosition; public string Bookmark => _bookmark; public TimeSpan AudioPosition => _audioPosition; internal BookmarkReachedEventArgs(Prompt prompt, string bookmark, TimeSpan audioPosition) : base(prompt) { _bookmark = bookmark; _audioPosition = audioPosition; } } [DebuggerDisplay("{_text}")] public class FilePrompt : Prompt { public FilePrompt(string path, SynthesisMediaType media) : this(new Uri(path, UriKind.Relative), media) { } public FilePrompt(Uri promptFile, SynthesisMediaType media) : base(promptFile, media) { } } [DebuggerDisplay("{VoiceInfo.Name} [{Enabled ? \"Enabled\" : \"Disabled\"}]")] public class InstalledVoice { private VoiceInfo _voice; private bool _enabled; private VoiceSynthesis _voiceSynthesizer; public VoiceInfo VoiceInfo => _voice; public bool Enabled { get { return _enabled; } set { SetEnabledFlag(value, switchContext: true); } } internal InstalledVoice(VoiceSynthesis voiceSynthesizer, VoiceInfo voice) { _voiceSynthesizer = voiceSynthesizer; _voice = voice; _enabled = true; } public override bool Equals(object obj) { if (!(obj is InstalledVoice installedVoice)) { return false; } if (VoiceInfo.Name == installedVoice.VoiceInfo.Name && VoiceInfo.Age == installedVoice.VoiceInfo.Age && VoiceInfo.Gender == installedVoice.VoiceInfo.Gender) { return VoiceInfo.Culture.Equals(installedVoice.VoiceInfo.Culture); } return false; } public override int GetHashCode() { return VoiceInfo.Name.GetHashCode(); } internal static InstalledVoice Find(List<InstalledVoice> list, VoiceInfo voiceId) { foreach (InstalledVoice item in list) { if (item.Enabled && item.VoiceInfo.Equals(voiceId)) { return item; } } return null; } internal static InstalledVoice FirstEnabled(List<InstalledVoice> list, CultureInfo culture) { InstalledVoice installedVoice = null; foreach (InstalledVoice item in list) { if (item.Enabled) { if (Helpers.CompareInvariantCulture(item.VoiceInfo.Culture, culture)) { return item; } if (installedVoice == null) { installedVoice = item; } } } return installedVoice; } internal void SetEnabledFlag(bool value, bool switchContext) { try { if (_enabled == value) { return; } _enabled = value; if (!_enabled) { if (_voice.Equals(_voiceSynthesizer.CurrentVoice(switchContext).VoiceInfo)) { _voiceSynthesizer.Voice = null; } } else { _voiceSynthesizer.Voice = null; } } catch (InvalidOperationException) { _voiceSynthesizer.Voice = null; } } } [DebuggerDisplay("{_text}")] public class Prompt { internal string _text; internal Uri _audio; internal SynthesisMediaType _media; internal bool _syncSpeak; internal Exception _exception; private bool _completed; private object _synthesizer; private static ResourceLoader _resourceLoader = new ResourceLoader(); public bool IsCompleted { get { return _completed; } internal set { _completed = value; } } internal object Synthesizer { set { if (value != null && (_synthesizer != null || _completed)) { throw new ArgumentException(SR.Get(SRID.SynthesizerPromptInUse), "value"); } _synthesizer = value; } } public Prompt(string textToSpeak) : this(textToSpeak, SynthesisTextFormat.Text) { } public Prompt(PromptBuilder promptBuilder) { Helpers.ThrowIfNull(promptBuilder, "promptBuilder"); _text = promptBuilder.ToXml(); _media = SynthesisMediaType.Ssml; } public Prompt(string textToSpeak, SynthesisTextFormat media) { Helpers.ThrowIfNull(textToSpeak, "textToSpeak"); if ((uint)(_media = (SynthesisMediaType)media) <= 1u) { _text = textToSpeak; return; } throw new ArgumentException(SR.Get(SRID.SynthesizerUnknownMediaType), "media"); } internal Prompt(Uri promptFile, SynthesisMediaType media) { Helpers.ThrowIfNull(promptFile, "promptFile"); switch (_media = media) { case SynthesisMediaType.Text: case SynthesisMediaType.Ssml: { string mimeType; Uri baseUri; string localPath; using Stream stream = _resourceLoader.LoadFile(promptFile, out mimeType, out baseUri, out localPath); try { using TextReader textReader = new StreamReader(stream); _text = textReader.ReadToEnd(); break; } finally { _resourceLoader.UnloadFile(localPath); } } case SynthesisMediaType.WaveAudio: _text = promptFile.ToString(); _audio = promptFile; break; default: throw new ArgumentException(SR.Get(SRID.SynthesizerUnknownMediaType), "media"); } } } public enum SynthesisMediaType { Text, Ssml, WaveAudio } public enum SynthesisTextFormat { Text, Ssml } internal enum PromptPriority { Normal, High } [Serializable] public class PromptBuilder { internal enum SsmlState { Header = 1, Paragraph = 2, Sentence = 4, StyleEmphasis = 8, StyleProsody = 0x10, Voice = 0x20, Ended = 0x40 } [Serializable] private struct StackElement { internal SsmlElement _possibleChildren; internal SsmlState _state; internal CultureInfo _culture; internal StackElement(SsmlElement possibleChildren, SsmlState state, CultureInfo culture) { _possibleChildren = possibleChildren; _state = state; _culture = culture; } } private enum ElementType { Prosody, Emphasis, SayAs, Phoneme, Sub, Break, Audio, Bookmark, StartVoice, StartParagraph, StartSentence, EndSentence, EndParagraph, StartStyle, EndStyle, EndVoice, Text, SsmlMarkup } [Serializable] private struct AttributeItem { internal string _key; internal string _value; internal string _namespace; internal AttributeItem(string key, string value) { _key = key; _value = value; _namespace = null; } internal AttributeItem(string ns, string key, string value) : this(key, value) { _namespace = ns; } } [Serializable] private class Element { internal ElementType _type; internal string _text; internal Collection<AttributeItem> _attributes; internal Element(ElementType type) { _type = type; } internal Element(ElementType type, string text) : this(type) { _text = text; } } private Stack<StackElement> _elementStack = new Stack<StackElement>(); private CultureInfo _culture; private List<Element> _elements = new List<Element>(); private static ResourceLoader _resourceLoader = new ResourceLoader(); private const string _xmlnsDefault = "http://www.w3.org/2001/10/synthesis"; private static readonly string[] _promptBuilderElementName = new string[11] { "prosody", "emphasis", "say-as", "phoneme", "sub", "break", "audio", "mark", "voice", "p", "s" }; public bool IsEmpty => _elements.Count == 0; public CultureInfo Culture { get { return _culture; } set { if (value == null) { throw new ArgumentNullException("value"); } _culture = value; } } public PromptBuilder() : this(CultureInfo.CurrentUICulture) { } public PromptBuilder(CultureInfo culture) { Helpers.ThrowIfNull(culture, "culture"); if (culture.Equals(CultureInfo.InvariantCulture)) { throw new ArgumentException(SR.Get(SRID.InvariantCultureInfo), "culture"); } _culture = culture; ClearContent(); } public void ClearContent() { _elements.Clear(); _elementStack.Push(new StackElement(SsmlElement.AudioMarkTextWithStyle | SsmlElement.ParagraphOrSentence | SsmlElement.Lexicon | SsmlElement.Meta | SsmlElement.MetaData, SsmlState.Header, _culture)); } public void AppendText(string textToSpeak) { Helpers.ThrowIfNull(textToSpeak, "textToSpeak"); ValidateElement(_elementStack.Peek(), SsmlElement.Text); _elements.Add(new Element(ElementType.Text, textToSpeak)); } public void AppendText(string textToSpeak, PromptRate rate) { Helpers.ThrowIfNull(textToSpeak, "textToSpeak"); if (rate < PromptRate.NotSet || rate > PromptRate.ExtraSlow) { throw new ArgumentOutOfRangeException("rate"); } ValidateElement(_elementStack.Peek(), SsmlElement.Text); Element element = new Element(ElementType.Prosody, textToSpeak); _elements.Add(element); string value = null; switch (rate) { case PromptRate.ExtraFast: value = "x-fast"; break; case PromptRate.ExtraSlow: value = "x-slow"; break; default: value = rate.ToString().ToLowerInvariant(); break; case PromptRate.NotSet: break; } if (!string.IsNullOrEmpty(value)) { element._attributes = new Collection<AttributeItem>(); element._attributes.Add(new AttributeItem("rate", value)); } } public void AppendText(string textToSpeak, PromptVolume volume) { Helpers.ThrowIfNull(textToSpeak, "textToSpeak"); if (volume < PromptVolume.NotSet || volume > PromptVolume.Default) { throw new ArgumentOutOfRangeException("volume"); } ValidateElement(_elementStack.Peek(), SsmlElement.Text); Element element = new Element(ElementType.Prosody, textToSpeak); _elements.Add(element); string value = null; switch (volume) { case PromptVolume.ExtraSoft: value = "x-soft"; break; case PromptVolume.ExtraLoud: value = "x-loud"; break; default: value = volume.ToString().ToLowerInvariant(); break; case PromptVolume.NotSet: break; } if (!string.IsNullOrEmpty(value)) { element._attributes = new Collection<AttributeItem>(); element._attributes.Add(new AttributeItem("volume", value)); } } public void AppendText(string textToSpeak, PromptEmphasis emphasis) { Helpers.ThrowIfNull(textToSpeak, "textToSpeak"); if (emphasis < PromptEmphasis.NotSet || emphasis > PromptEmphasis.Reduced) { throw new ArgumentOutOfRangeException("emphasis"); } ValidateElement(_elementStack.Peek(), SsmlElement.Text); Element element = new Element(ElementType.Emphasis, textToSpeak); _elements.Add(element); if (emphasis != 0) { element._attributes = new Collection<AttributeItem>(); element._attributes.Add(new AttributeItem("level", emphasis.ToString().ToLowerInvariant())); } } public void StartStyle(PromptStyle style) { Helpers.ThrowIfNull(style, "style"); StackElement stackElement = _elementStack.Peek(); ValidateElement(stackElement, SsmlElement.Prosody); SsmlState ssmlState = (SsmlState)0; SsmlElement possibleChildren = stackElement._possibleChildren; _elements.Add(new Element(ElementType.StartStyle)); if (style.Emphasis != 0) { Element element = new Element(ElementType.Emphasis); _elements.Add(element); element._attributes = new Collection<AttributeItem>(); element._attributes.Add(new AttributeItem("level", style.Emphasis.ToString().ToLowerInvariant())); possibleChildren = SsmlElement.AudioMarkTextWithStyle; ssmlState = SsmlState.StyleEmphasis; } if (style.Rate != 0 || style.Volume != 0) { if (ssmlState != 0) { _elements.Add(new Element(ElementType.StartStyle)); } Element element2 = new Element(ElementType.Prosody); _elements.Add(element2); if (style.Rate != 0) { string value = style.Rate switch { PromptRate.ExtraFast => "x-fast", PromptRate.ExtraSlow => "x-slow", _ => style.Rate.ToString().ToLowerInvariant(), }; element2._attributes = new Collection<AttributeItem>(); element2._attributes.Add(new AttributeItem("rate", value)); } if (style.Volume != 0) { string value2 = style.Volume switch { PromptVolume.ExtraSoft => "x-soft", PromptVolume.ExtraLoud => "x-loud", _ => style.Volume.ToString().ToLowerInvariant(), }; if (element2._attributes == null) { element2._attributes = new Collection<AttributeItem>(); } element2._attributes.Add(new AttributeItem("volume", value2)); } possibleChildren = SsmlElement.AudioMarkTextWithStyle | SsmlElement.ParagraphOrSentence; ssmlState |= SsmlState.StyleProsody; } _elementStack.Push(new StackElement(possibleChildren, ssmlState, stackElement._culture)); } public void EndStyle() { StackElement stackElement = _elementStack.Pop(); if (stackElement._state != 0) { if ((stackElement._state & (SsmlState)24) == 0) { throw new InvalidOperationException(SR.Get(SRID.PromptBuilderMismatchStyle)); } _elements.Add(new Element(ElementType.EndStyle)); if (stackElement._state == (SsmlState)24) { _elements.Add(new Element(ElementType.EndStyle)); } } } public void StartVoice(VoiceInfo voice) { Helpers.ThrowIfNull(voice, "voice"); if (!VoiceInfo.ValidateGender(voice.Gender)) { throw new ArgumentException(SR.Get(SRID.EnumInvalid, "VoiceGender"), "voice"); } if (!VoiceInfo.ValidateAge(voice.Age)) { throw new ArgumentException(SR.Get(SRID.EnumInvalid, "VoiceAge"), "voice"); } StackElement stackElement = _elementStack.Peek(); ValidateElement(stackElement, SsmlElement.Voice); CultureInfo culture = ((voice.Culture == null) ? stackElement._culture : voice.Culture); Element element = new Element(ElementType.StartVoice); element._attributes = new Collection<AttributeItem>(); _elements.Add(element); if (!string.IsNullOrEmpty(voice.Name)) { element._attributes.Add(new AttributeItem("name", voice.Name)); } if (voice.Culture != null) { element._attributes.Add(new AttributeItem("xml", "lang", voice.Culture.Name)); } if (voice.Gender != 0) { element._attributes.Add(new AttributeItem("gender", voice.Gender.ToString().ToLowerInvariant())); } if (voice.Age != 0) { element._attributes.Add(new AttributeItem("age", ((int)voice.Age).ToString(CultureInfo.InvariantCulture))); } if (voice.Variant >= 0) { element._attributes.Add(new AttributeItem("variant", voice.Variant.ToString(CultureInfo.InvariantCulture))); } _elementStack.Push(new StackElement(SsmlElement.AudioMarkTextWithStyle | SsmlElement.Sentence, SsmlState.Voice, culture)); } public void StartVoice(string name) { Helpers.ThrowIfEmptyOrNull(name, "name"); StartVoice(new VoiceInfo(name)); } public void StartVoice(VoiceGender gender) { StartVoice(new VoiceInfo(gender)); } public void StartVoice(VoiceGender gender, VoiceAge age) { StartVoice(new VoiceInfo(gender, age)); } public void StartVoice(VoiceGender gender, VoiceAge age, int voiceAlternate) { StartVoice(new VoiceInfo(gender, age, voiceAlternate)); } public void StartVoice(CultureInfo culture) { StartVoice(new VoiceInfo(culture)); } public void EndVoice() { if (_elementStack.Pop()._state != SsmlState.Voice) { throw new InvalidOperationException(SR.Get(SRID.PromptBuilderMismatchVoice)); } _elements.Add(new Element(ElementType.EndVoice)); } public void StartParagraph() { StartParagraph(null); } public void StartParagraph(CultureInfo culture) { StackElement stackElement = _elementStack.Peek(); ValidateElement(stackElement, SsmlElement.Paragraph); Element element = new Element(ElementType.StartParagraph); _elements.Add(element); if (culture != null) { if (culture.Equals(CultureInfo.InvariantCulture)) { throw new ArgumentException(SR.Get(SRID.InvariantCultureInfo), "culture"); } element._attributes = new Collection<AttributeItem>(); element._attributes.Add(new AttributeItem("xml", "lang", culture.Name)); } else { culture = stackElement._culture; } _elementStack.Push(new StackElement(SsmlElement.AudioMarkTextWithStyle | SsmlElement.Sentence, SsmlState.Paragraph, culture)); } public void EndParagraph() { if (_elementStack.Pop()._state != SsmlState.Paragraph) { throw new InvalidOperationException(SR.Get(SRID.PromptBuilderMismatchParagraph)); } _elements.Add(new Element(ElementType.EndParagraph)); } public void StartSentence() { StartSentence(null); } public void StartSentence(CultureInfo culture) { StackElement stackElement = _elementStack.Peek(); ValidateElement(stackElement, SsmlElement.Sentence); Element element = new Element(ElementType.StartSentence); _elements.Add(element); if (culture != null) { if (culture.Equals(CultureInfo.InvariantCulture)) { throw new ArgumentException(SR.Get(SRID.InvariantCultureInfo), "culture"); } element._attributes = new Collection<AttributeItem>(); element._attributes.Add(new AttributeItem("xml", "lang", culture.Name)); } else { culture = stackElement._culture; } _elementStack.Push(new StackElement(SsmlElement.AudioMarkTextWithStyle, SsmlState.Sentence, culture)); } public void EndSentence() { if (_elementStack.Pop()._state != SsmlState.Sentence) { throw new InvalidOperationException(SR.Get(SRID.PromptBuilderMismatchSentence)); } _elements.Add(new Element(ElementType.EndSentence)); } public void AppendTextWithHint(string textToSpeak, SayAs sayAs) { Helpers.ThrowIfNull(textToSpeak, "textToSpeak"); if (sayAs < SayAs.SpellOut || sayAs > SayAs.Text) { throw new ArgumentOutOfRangeException("sayAs"); } ValidateElement(_elementStack.Peek(), SsmlElement.Text); if (sayAs != SayAs.Text) { Element element = new Element(ElementType.SayAs, textToSpeak); _elements.Add(element); element._attributes = new Collection<AttributeItem>(); string value = null; string value2 = null; switch (sayAs) { case SayAs.SpellOut: value = "characters"; break; case SayAs.NumberOrdinal: value = "ordinal"; break; case SayAs.NumberCardinal: value = "cardinal"; break; case SayAs.Date: value = "date"; break; case SayAs.DayMonthYear: value = "date"; value2 = "dmy"; break; case SayAs.MonthDayYear: value = "date"; value2 = "mdy"; break; case SayAs.YearMonthDay: value = "date"; value2 = "ymd"; break; case SayAs.YearMonth: value = "date"; value2 = "ym"; break; case SayAs.MonthYear: value = "date"; value2 = "my"; break; case SayAs.MonthDay: value = "date"; value2 = "md"; break; case SayAs.DayMonth: value = "date"; value2 = "dm"; break; case SayAs.Year: value = "date"; value2 = "y"; break; case SayAs.Month: value = "date"; value2 = "m"; break; case SayAs.Day: value = "date"; value2 = "d"; break; case SayAs.Time: value = "time"; break; case SayAs.Time24: value = "time"; value2 = "hms24"; break; case SayAs.Time12: value = "time"; value2 = "hms12"; break; case SayAs.Telephone: value = "telephone"; break; } element._attributes.Add(new AttributeItem("interpret-as", value)); if (!string.IsNullOrEmpty(value2)) { element._attributes.Add(new AttributeItem("format", value2)); } } else { AppendText(textToSpeak); } } public void AppendTextWithHint(string textToSpeak, string sayAs) { Helpers.ThrowIfNull(textToSpeak, "textToSpeak"); Helpers.ThrowIfEmptyOrNull(sayAs, "sayAs"); ValidateElement(_elementStack.Peek(), SsmlElement.Text); Element element = new Element(ElementType.SayAs, textToSpeak); _elements.Add(element); element._attributes = new Collection<AttributeItem>(); element._attributes.Add(new AttributeItem("interpret-as", sayAs)); } public void AppendTextWithPronunciation(string textToSpeak, string pronunciation) { Helpers.ThrowIfEmptyOrNull(textToSpeak, "textToSpeak"); Helpers.ThrowIfEmptyOrNull(pronunciation, "pronunciation"); ValidateElement(_elementStack.Peek(), SsmlElement.Text); PhonemeConverter.ValidateUpsIds(pronunciation); Element element = new Element(ElementType.Phoneme, textToSpeak); _elements.Add(element); element._attributes = new Collection<AttributeItem>(); element._attributes.Add(new AttributeItem("ph", pronunciation)); } public void AppendTextWithAlias(string textToSpeak, string substitute) { Helpers.ThrowIfNull(textToSpeak, "textToSpeak"); Helpers.ThrowIfNull(substitute, "substitute"); ValidateElement(_elementStack.Peek(), SsmlElement.Text); Element element = new Element(ElementType.Sub, textToSpeak); _elements.Add(element); element._attributes = new Collection<AttributeItem>(); element._attributes.Add(new AttributeItem("alias", substitute)); } public void AppendBreak() { ValidateElement(_elementStack.Peek(), SsmlElement.Break); _elements.Add(new Element(ElementType.Break)); } public void AppendBreak(PromptBreak strength) { ValidateElement(_elementStack.Peek(), SsmlElement.Break); Element element = new Element(ElementType.Break); _elements.Add(element); string text = null; text = strength switch { PromptBreak.None => "none", PromptBreak.ExtraSmall => "x-weak", PromptBreak.Small => "weak", PromptBreak.Medium => "medium", PromptBreak.Large => "strong", PromptBreak.ExtraLarge => "x-strong", _ => throw new ArgumentNullException("strength"), }; element._attributes = new Collection<AttributeItem>(); element._attributes.Add(new AttributeItem("strength", text)); } public void AppendBreak(TimeSpan duration) { ValidateElement(_elementStack.Peek(), SsmlElement.Break); if (duration.Ticks < 0) { throw new ArgumentOutOfRangeException("duration"); } Element element = new Element(ElementType.Break); _elements.Add(element); element._attributes = new Collection<AttributeItem>(); element._attributes.Add(new AttributeItem("time", duration.TotalMilliseconds + "ms")); } public void AppendAudio(string path) { Helpers.ThrowIfEmptyOrNull(path, "path"); Uri audioFile; try { audioFile = new Uri(path, UriKind.RelativeOrAbsolute); } catch (UriFormatException ex) { throw new ArgumentException(ex.Message, path, ex); } ValidateElement(_elementStack.Peek(), SsmlElement.Audio); AppendAudio(audioFile); } public void AppendAudio(Uri audioFile) { Helpers.ThrowIfNull(audioFile, "audioFile"); ValidateElement(_elementStack.Peek(), SsmlElement.Audio); Element element = new Element(ElementType.Audio); _elements.Add(element); element._attributes = new Collection<AttributeItem>(); element._attributes.Add(new AttributeItem("src", audioFile.ToString())); } public void AppendAudio(Uri audioFile, string alternateText) { Helpers.ThrowIfNull(audioFile, "audioFile"); Helpers.ThrowIfNull(alternateText, "alternateText"); ValidateElement(_elementStack.Peek(), SsmlElement.Audio); Element element = new Element(ElementType.Audio, alternateText); _elements.Add(element); element._attributes = new Collection<AttributeItem>(); element._attributes.Add(new AttributeItem("src", audioFile.ToString())); } public void AppendBookmark(string bookmarkName) { Helpers.ThrowIfEmptyOrNull(bookmarkName, "bookmarkName"); ValidateElement(_elementStack.Peek(), SsmlElement.Mark); Element element = new Element(ElementType.Bookmark); _elements.Add(element); element._attributes = new Collection<AttributeItem>(); element._attributes.Add(new AttributeItem("name", bookmarkName)); } public void AppendPromptBuilder(PromptBuilder promptBuilder) { Helpers.ThrowIfNull(promptBuilder, "promptBuilder"); StringReader stringReader = new StringReader(promptBuilder.ToXml()); XmlTextReader xmlTextReader = new XmlTextReader(stringReader); AppendSsml(xmlTextReader); xmlTextReader.Close(); stringReader.Close(); } public void AppendSsml(string path) { Helpers.ThrowIfEmptyOrNull(path, "path"); AppendSsml(new Uri(path, UriKind.Relative)); } public void AppendSsml(Uri ssmlFile) { Helpers.ThrowIfNull(ssmlFile, "ssmlFile"); string localPath; Uri redirectedUri; using Stream input = _resourceLoader.LoadFile(ssmlFile, out localPath, out redirectedUri); try { AppendSsml(new XmlTextReader(input)); } finally { _resourceLoader.UnloadFile(localPath); } } public void AppendSsml(XmlReader ssmlFile) { Helpers.ThrowIfNull(ssmlFile, "ssmlFile"); AppendSsmlInternal(ssmlFile); } [EditorBrowsable(EditorBrowsableState.Advanced)] public void AppendSsmlMarkup(string ssmlMarkup) { Helpers.ThrowIfEmptyOrNull(ssmlMarkup, "ssmlMarkup"); _elements.Add(new Element(ElementType.SsmlMarkup, ssmlMarkup)); } public string ToXml() { using StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture); using XmlTextWriter writer = new XmlTextWriter(stringWriter); WriteXml(writer); SsmlState state = _elementStack.Peek()._state; if (state != SsmlState.Header) { string text = SR.Get(SRID.PromptBuilderInvalideState); switch (state) { case SsmlState.Ended: text += SR.Get(SRID.PromptBuilderStateEnded); break; case SsmlState.Sentence: text += SR.Get(SRID.PromptBuilderStateSentence); break; case SsmlState.Paragraph: text += SR.Get(SRID.PromptBuilderStateParagraph); break; case SsmlState.StyleEmphasis: case SsmlState.StyleProsody: case (SsmlState)24: text += SR.Get(SRID.PromptBuilderStateStyle); break; case SsmlState.Voice: text += SR.Get(SRID.PromptBuilderStateVoice); break; default: throw new NotSupportedException(); } throw new InvalidOperationException(text); } return stringWriter.ToString(); } private void WriteXml(XmlTextWriter writer) { writer.WriteStartElement("speak"); writer.WriteAttributeString("version", "1.0"); writer.WriteAttributeString("xmlns", "http://www.w3.org/2001/10/synthesis"); writer.WriteAttributeString("xml", "lang", null, _culture.Name); bool flag = false; foreach (Element element in _elements) { flag = flag || element._type == ElementType.StartSentence || element._type == ElementType.StartParagraph || element._type == ElementType.StartStyle || element._type == ElementType.StartVoice; switch (element._type) { case ElementType.Text: writer.WriteString(element._text); break; case ElementType.SsmlMarkup: writer.WriteRaw(element._text); break; case ElementType.Prosody: case ElementType.Emphasis: case ElementType.SayAs: case ElementType.Phoneme: case ElementType.Sub: case ElementType.Break: case ElementType.Audio: case ElementType.Bookmark: case ElementType.StartVoice: case ElementType.StartParagraph: case ElementType.StartSentence: writer.WriteStartElement(_promptBuilderElementName[(int)element._type]); if (element._attributes != null) { foreach (AttributeItem attribute in element._attributes) { if (attribute._namespace == null) { writer.WriteAttributeString(attribute._key, attribute._value); } else { writer.WriteAttributeString(attribute._namespace, attribute._key, null, attribute._value); } } } if (element._text != null) { writer.WriteString(element._text); } if (!flag) { writer.WriteEndElement(); } flag = false; break; case ElementType.EndSentence: case ElementType.EndParagraph: case ElementType.EndStyle: case ElementType.EndVoice: writer.WriteEndElement(); break; default: throw new NotSupportedException(); case ElementType.StartStyle: break; } } writer.WriteEndElement(); } private static void ValidateElement(StackElement stackElement, SsmlElement currentElement) { if ((stackElement._possibleChildren & currentElement) == 0) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, SR.Get(SRID.PromptBuilderInvalidElement), new object[2] { currentElement.ToString(), stackElement._state.ToString() })); } } private void AppendSsmlInternal(XmlReader ssmlFile) { StackElement stackElement = _elementStack.Peek(); ValidateElement(_elementStack.Peek(), SsmlElement.Voice); using StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture); using (XmlTextWriter writer = new XmlTextWriter(stringWriter)) { TextWriterEngine engine = new TextWriterEngine(writer, stackElement._culture); SsmlParser.Parse(ssmlFile, engine, null); } _elements.Add(new Element(ElementType.SsmlMarkup, stringWriter.ToString())); } } public abstract class PromptEventArgs : AsyncCompletedEventArgs { private Prompt _prompt; public Prompt Prompt => _prompt; internal PromptEventArgs(Prompt prompt) : base(prompt._exception, prompt._exception != null, prompt) { _prompt = prompt; } } public class SpeakStartedEventArgs : PromptEventArgs { internal SpeakStartedEventArgs(Prompt prompt) : base(prompt) { } } [Serializable] public class PromptStyle { private PromptRate _rate; private PromptVolume _volume; private PromptEmphasis _emphasis; public PromptRate Rate { get { return _rate; } set { _rate = value; } } public PromptVolume Volume { get { return _volume; } set { _volume = value; } } public PromptEmphasis Emphasis { get { return _emphasis; } set { _emphasis = value; } } public PromptStyle() { } public PromptStyle(PromptRate rate) { Rate = rate; } public PromptStyle(PromptVolume volume) { Volume = volume; } public PromptStyle(PromptEmphasis emphasis) { Emphasis = emphasis; } } public enum SayAs { SpellOut, NumberOrdinal, NumberCardinal, Date, DayMonthYear, MonthDayYear, YearMonthDay, YearMonth, MonthYear, MonthDay, DayMonth, Year, Month, Day, Time, Time24, Time12, Telephone, Text } public enum VoiceGender { NotSet, Male, Female, Neutral } public enum VoiceAge { NotSet = 0, Child = 10, Teen = 15, Adult = 30, Senior = 65 } public enum PromptRate { NotSet, ExtraFast, Fast, Medium, Slow, ExtraSlow } public enum PromptVolume { NotSet, Silent, ExtraSoft, Soft, Medium, Loud, ExtraLoud, Default } public enum PromptEmphasis { NotSet, Strong, Moderate, None, Reduced } public enum PromptBreak { None, ExtraSmall, Small, Medium, Large, ExtraLarge } public sealed class SpeechSynthesizer : IDisposable { private VoiceSynthesis _voiceSynthesis; private bool _isDisposed; private bool paused; private Stream _outputStream; private bool _closeStreamOnExit; public SynthesizerState State => VoiceSynthesizer.State; public int Rate { get { return VoiceSynthesizer.Rate; } set { if (value < -10 || value > 10) { throw new ArgumentOutOfRangeException("value", SR.Get(SRID.RateOutOfRange)); } VoiceSynthesizer.Rate = value; } } public int Volume { get { return VoiceSynthesizer.Volume; } set { if (value < 0 || value > 100) { throw new ArgumentOutOfRangeException("value", SR.Get(SRID.ResourceUsageOutOfRange)); } VoiceSynthesizer.Volume = value; } } public VoiceInfo Voice => VoiceSynthesizer.CurrentVoice(switchContext: true).VoiceInfo; private VoiceSynthesis VoiceSynthesizer { get { if (_voiceSynthesis == null && _isDisposed) { throw new ObjectDisposedException("SpeechSynthesizer"); } if (_voiceSynthesis == null) { WeakReference speechSynthesizer = new WeakReference(this); _voiceSynthesis = new VoiceSynthesis(speechSynthesizer); } return _voiceSynthesis; } } public event EventHandler<SpeakStartedEventArgs> SpeakStarted { [MethodImpl(MethodImplOptions.Synchronized)] add { Helpers.ThrowIfNull(value, "value"); VoiceSynthesis voiceSynthesizer = VoiceSynthesizer; voiceSynthesizer._speakStarted = (EventHandler<SpeakStartedEventArgs>)Delegate.Combine(voiceSynthesizer._speakStarted, value); } [MethodImpl(MethodImplOptions.Synchronized)] remove { Helpers.ThrowIfNull(value, "value"); VoiceSynthesis voiceSynthesizer = VoiceSynthesizer; voiceSynthesizer._speakStarted = (EventHandler<SpeakStartedEventArgs>)Delegate.Remove(voiceSynthesizer._speakStarted, value); } } public event EventHandler<SpeakCompletedEventArgs> SpeakCompleted { [MethodImpl(MethodImplOptions.Synchronized)] add { Helpers.ThrowIfNull(value, "value"); VoiceSynthesis voiceSynthesizer = VoiceSynthesizer; voiceSynthesizer._speakCompleted = (EventHandler<SpeakCompletedEventArgs>)Delegate.Combine(voiceSynthesizer._speakCompleted, value); } [MethodImpl(MethodImplOptions.Synchronized)] remove { Helpers.ThrowIfNull(value, "value"); VoiceSynthesis voiceSynthesizer = VoiceSynthesizer; voiceSynthesizer._speakCompleted = (EventHandler<SpeakCompletedEventArgs>)Delegate.Remove(voiceSynthesizer._speakCompleted, value); } } public event EventHandler<SpeakProgressEventArgs> SpeakProgress { [MethodImpl(MethodImplOptions.Synchronized)] add { Helpers.ThrowIfNull(value, "value"); VoiceSynthesizer.AddEvent(TtsEventId.WordBoundary, ref VoiceSynthesizer._speakProgress, value); } [MethodImpl(MethodImplOptions.Synchronized)] remove { Helpers.ThrowIfNull(value, "value"); VoiceSynthesizer.RemoveEvent(TtsEventId.WordBoundary, ref VoiceSynthesizer._speakProgress, value); } } public event EventHandler<BookmarkReachedEventArgs> BookmarkReached { [MethodImpl(MethodImplOptions.Synchronized)] add { Helpers.ThrowIfNull(value, "value"); VoiceSynthesizer.AddEvent(TtsEventId.Bookmark, ref VoiceSynthesizer._bookmarkReached, value); } [MethodImpl(MethodImplOptions.Synchronized)] remove { Helpers.ThrowIfNull(value, "value"); VoiceSynthesizer.RemoveEvent(TtsEventId.Bookmark, ref VoiceSynthesizer._bookmarkReached, value); } } public event EventHandler<VoiceChangeEventArgs> VoiceChange { [MethodImpl(MethodImplOptions.Synchronized)] add { Helpers.ThrowIfNull(value, "value"); VoiceSynthesizer.AddEvent(TtsEventId.VoiceChange, ref VoiceSynthesizer._voiceChange, value); } [MethodImpl(MethodImplOptions.Synchronized)] remove { Helpers.ThrowIfNull(value, "value"); VoiceSynthesizer.RemoveEvent(TtsEventId.VoiceChange, ref VoiceSynthesizer._voiceChange, value); } } public event EventHandler<PhonemeReachedEventArgs> PhonemeReached { [MethodImpl(MethodImplOptions.Synchronized)] add { Helpers.ThrowIfNull(value, "value"); VoiceSynthesizer.AddEvent(TtsEventId.Phoneme, ref VoiceSynthesizer._phonemeReached, value); } [MethodImpl(MethodImplOptions.Synchronized)] remove { Helpers.ThrowIfNull(value, "value"); VoiceSynthesizer.RemoveEvent(TtsEventId.Phoneme, ref VoiceSynthesizer._phonemeReached, value); } } public event EventHandler<VisemeReachedEventArgs> VisemeReached { [MethodImpl(MethodImplOptions.Synchronized)] add { Helpers.ThrowIfNull(value, "value"); VoiceSynthesizer.AddEvent(TtsEventId.Viseme, ref VoiceSynthesizer._visemeReached, value); } [MethodImpl(MethodImplOptions.Synchronized)] remove { Helpers.ThrowIfNull(value, "value"); VoiceSynthesizer.RemoveEvent(TtsEventId.Viseme, ref VoiceSynthesizer._visemeReached, value); } } public event EventHandler<StateChangedEventArgs> StateChanged { [MethodImpl(MethodImplOptions.Synchronized)] add { Helpers.ThrowIfNull(value, "value"); VoiceSynthesis voiceSynthesizer = VoiceSynthesizer; voiceSynthesizer._stateChanged = (EventHandler<StateChangedEventArgs>)Delegate.Combine(voiceSynthesizer._stateChanged, value); } [MethodImpl(MethodImplOptions.Synchronized)] remove { Helpers.ThrowIfNull(value, "value"); VoiceSynthesis voiceSynthesizer = VoiceSynthesizer; voiceSynthesizer._stateChanged = (EventHandler<StateChangedEventArgs>)Delegate.Remove(voiceSynthesizer._stateChanged, value); } } ~SpeechSynthesizer() { Dispose(disposing: false); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } public void SelectVoice(string name) { Helpers.ThrowIfEmptyOrNull(name, "name"); TTSVoice engine = VoiceSynthesizer.GetEngine(name, CultureInfo.CurrentUICulture, VoiceGender.NotSet, VoiceAge.NotSet, 1, switchContext: true); if (engine == null || name != engine.VoiceInfo.Name) { throw new ArgumentException(SR.Get(SRID.SynthesizerSetVoiceNoMatch)); } VoiceSynthesizer.Voice = engine; } public void SelectVoiceByHints(VoiceGender gender) { SelectVoiceByHints(gender, VoiceAge.NotSet, 1, CultureInfo.CurrentUICulture); } public void SelectVoiceByHints(VoiceGender gender, VoiceAge age) { SelectVoiceByHints(gender, age, 1, CultureInfo.CurrentUICulture); } public void SelectVoiceByHints(VoiceGender gender, VoiceAge age, int voiceAlternate) { SelectVoiceByHints(gender, age, voiceAlternate, CultureInfo.CurrentUICulture); } public void SelectVoiceByHints(VoiceGender gender, VoiceAge age, int voiceAlternate, CultureInfo culture) { Helpers.ThrowIfNull(culture, "culture"); if (voiceAlternate < 0) { throw new ArgumentOutOfRangeException("voiceAlternate", SR.Get(SRID.PromptBuilderInvalidVariant)); } if (!VoiceInfo.ValidateGender(gender)) { throw new ArgumentException(SR.Get(SRID.EnumInvalid, "VoiceGender"), "gender"); } if (!VoiceInfo.ValidateAge(age)) { throw new ArgumentException(SR.Get(SRID.EnumInvalid, "VoiceAge"), "age"); } TTSVoice engine = VoiceSynthesizer.GetEngine(null, culture, gender, age, voiceAlternate, switchContext: true); if (engine == null) { throw new InvalidOperationException(SR.Get(SRID.SynthesizerSetVoiceNoMatch)); } VoiceSynthesizer.Voice = engine; } public Prompt SpeakAsync(string textToSpeak) { Helpers.ThrowIfNull(textToSpeak, "textToSpeak"); Prompt prompt = new Prompt(textToSpeak, SynthesisTextFormat.Text); SpeakAsync(prompt); return prompt; } public void SpeakAsync(Prompt prompt) { Helpers.ThrowIfNull(prompt, "prompt"); prompt.Synthesizer = this; VoiceSynthesizer.SpeakAsync(prompt); } public Prompt SpeakSsmlAsync(string textToSpeak) { Helpers.ThrowIfNull(textToSpeak, "textToSpeak"); Prompt prompt = new Prompt(textToSpeak, SynthesisTextFormat.Ssml); SpeakAsync(prompt); return prompt; } public Prompt SpeakAsync(PromptBuilder promptBuilder) { Helpers.ThrowIfNull(promptBuilder, "promptBuilder"); Prompt prompt = new Prompt(promptBuilder); SpeakAsync(prompt); return prompt; } public void Speak(string textToSpeak) { Speak(new Prompt(textToSpeak, SynthesisTextFormat.Text)); } public void Speak(Prompt prompt) { Helpers.ThrowIfNull(prompt, "prompt"); if (State == SynthesizerState.Paused) { throw new InvalidOperationException(SR.Get(SRID.SynthesizerSyncSpeakWhilePaused)); } prompt.Synthesizer = this; prompt._syncSpeak = true; VoiceSynthesizer.Speak(prompt); } public void Speak(PromptBuilder promptBuilder) { Speak(new Prompt(promptBuilder)); } public void SpeakSsml(string textToSpeak) { Speak(new Prompt(textToSpeak, SynthesisTextFormat.Ssml)); } public void Pause() { if (!paused) { VoiceSynthesizer.Pause(); paused = true; } } public void Resume() { if (paused) { VoiceSynthesizer.Resume(); paused = false; } } public void SpeakAsyncCancel(Prompt prompt) { Helpers.ThrowIfNull(prompt, "prompt"); VoiceSynthesizer.Abort(prompt); } public void SpeakAsyncCancelAll() { VoiceSynthesizer.Abort(); } public void SetOutputToWaveFile(string path) { Helpers.ThrowIfEmptyOrNull(path, "path"); SetOutputToNull(); SetOutputStream(new FileStream(path, FileMode.Create, FileAccess.Write), null, headerInfo: true, closeStreamOnExit: true); } public void SetOutputToWaveFile(string path, SpeechAudioFormatInfo formatInfo) { Helpers.ThrowIfEmptyOrNull(path, "path"); Helpers.ThrowIfNull(formatInfo, "formatInfo"); SetOutputToNull(); SetOutputStream(new FileStream(path, FileMode.Create, FileAccess.Write), formatInfo, headerInfo: true, closeStreamOnExit: true); } public void SetOutputToWaveStream(Stream audioDestination) { Helpers.ThrowIfNull(audioDestination, "audioDestination"); SetOutputStream(audioDestination, null, headerInfo: true, closeStreamOnExit: false); } public void SetOutputToAudioStream(Stream audioDestination, SpeechAudioFormatInfo formatInfo) { Helpers.ThrowIfNull(audioDestination, "audioDestination"); Helpers.ThrowIfNull(formatInfo, "formatInfo"); SetOutputStream(audioDestination, formatInfo, headerInfo: false, closeStreamOnExit: false); } public void SetOutputToDefaultAudioDevice() { SetOutputStream(null, null, headerInfo: true, closeStreamOnExit: false); } public void SetOutputToNull() { if (_outputStream != Stream.Null) { VoiceSynthesizer.SetOutput(Stream.Null, null, headerInfo: true); } if (_outputStream != null && _closeStreamOnExit) { _outputStream.Close(); } _outputStream = Stream.Null; } public Prompt GetCurrentlySpokenPrompt() { return VoiceSynthesizer.Prompt; } public ReadOnlyCollection<InstalledVoice> GetInstalledVoices() { return VoiceSynthesizer.GetInstalledVoices(null); } public ReadOnlyCollection<InstalledVoice> GetInstalledVoices(CultureInfo culture) { Helpers.ThrowIfNull(culture, "culture"); if (culture.Equals(CultureInfo.InvariantCulture)) { throw new ArgumentException(SR.Get(SRID.InvariantCultureInfo), "culture"); } return VoiceSynthesizer.GetInstalledVoices(culture); } public void AddLexicon(Uri uri, string mediaType) { Helpers.ThrowIfNull(uri, "uri"); VoiceSynthesizer.AddLexicon(uri, mediaType); } public void RemoveLexicon(Uri uri) { Helpers.ThrowIfNull(uri, "uri"); VoiceSynthesizer.RemoveLexicon(uri); } private void SetOutputStream(Stream stream, SpeechAudioFormatInfo formatInfo, bool headerInfo, bool closeStreamOnExit) { SetOutputToNull(); _outputStream = stream; _closeStreamOnExit = closeStreamOnExit; VoiceSynthesizer.SetOutput(stream, formatInfo, headerInfo); } private void Dispose(bool disposing) { if (!_isDisposed && disposing && _voiceSynthesis != null) { _isDisposed = true; SpeakAsyncCancelAll(); if (_outputStream != null) { if (_closeStreamOnExit) { _outputStream.Close(); } else { _outputStream.Flush(); } _outputStream = null; } } if (_voiceSynthesis != null) { _voiceSynthesis.Dispose(); _voiceSynthesis = null; } _isDisposed = true; } } public enum SynthesizerState { Ready, Speaking, Paused } [Flags] public enum SynthesizerEmphasis { Stressed = 1, Emphasized = 2 } public class SpeakCompletedEventArgs : PromptEventArgs { internal SpeakCompletedEventArgs(Prompt prompt) : base(prompt) { } } public class SpeakProgressEventArgs : PromptEventArgs { private TimeSpan _audioPosition; private int _iWordPos; private int _cWordLen; private string _word; public TimeSpan AudioPosition => _audioPosition; public int CharacterPosition => _iWordPos; public int CharacterCount { get { return _cWordLen; } internal set { _cWordLen = value; } } public string Text { get { return _word; } internal set { _word = value; } } internal SpeakProgressEventArgs(Prompt prompt, TimeSpan audioPosition, int iWordPos, int cWordLen) : base(prompt) { _audioPosition = audioPosition; _iWordPos = iWordPos; _cWordLen = cWordLen; } } public class StateChangedEventArgs : EventArgs { private SynthesizerState _state; private SynthesizerState _previousState; public SynthesizerState State => _state; public SynthesizerState PreviousState => _previousState; internal StateChangedEventArgs(SynthesizerState state, SynthesizerState previousState) { _state = state; _previousState = previousState; } } [Serializable] [DebuggerDisplay("{(_name != null ? \"'\" + _name + \"' \" : \"\") + (_culture != null ? \" '\" + _culture.ToString () + \"' \" : \"\") + (_gender != VoiceGender.NotSet ? \" '\" + _gender.ToString () + \"' \" : \"\") + (_age != VoiceAge.NotSet ? \" '\" + _age.ToString () + \"' \" : \"\") + (_variant > 0 ? \" \" + _variant.ToString () : \"\")}")] public class VoiceInfo { private string _name; private CultureInfo _culture; private VoiceGender _gender; private VoiceAge _age; private int _variant = -1; [NonSerialized] private string _id; [NonSerialized] private string _registryKeyPath; [NonSerialized] private string _assemblyName; [NonSerialized] private string _clsid; [NonSerialized] private string _description; [NonSerialized] private ReadOnlyDictionary<string, string> _attributes; [NonSerialized] private ReadOnlyCollection<SpeechAudioFormatInfo> _audioFormats; public VoiceGender Gender => _gender; public VoiceAge Age => _age; public string Name => _name; public CultureInfo Culture => _culture; public string Id => _id; public string Description { get { if (_description == null) { return string.Empty; } return _description; } } [EditorBrowsable(EditorBrowsableState.Advanced)] public ReadOnlyCollection<SpeechAudioFormatInfo> SupportedAudioFormats => _audioFormats; [EditorBrowsable(EditorBrowsableState.Advanced)] public IDictionary<string, string> AdditionalInfo { get { if (_attributes == null) { _attributes = new ReadOnlyDictionary<string, string>(new Dictionary<string, string>(0)); } return _attributes; } } internal int Variant => _variant; internal string AssemblyName => _assemblyName; internal string Clsid => _clsid; internal string RegistryKeyPath => _registryKeyPath; internal VoiceInfo(string name) { Helpers.ThrowIfEmptyOrNull(name, "name"); _name = name; } internal VoiceInfo(CultureInfo culture) { Helpers.ThrowIfNull(culture, "culture"); if (culture.Equals(CultureInfo.InvariantCulture)) { throw new ArgumentException(SR.Get(SRID.InvariantCultureInfo), "culture"); } _culture = culture; } internal VoiceInfo(ObjectToken token) { _registryKeyPath = token._sKeyId; _id = token.Name; _description = token.Description; _name = token.TokenName(); SsmlParserHelpers.TryConvertAge(token.Age.ToLowerInvariant(), out _age); SsmlParserHelpers.TryConvertGender(token.Gender.ToLowerInvariant(), out _gender); if (token.Attributes.TryGetString("Language", out var value)) { _culture = SapiAttributeParser.GetCultureInfoFromLanguageString(value); } if (token.TryGetString("Assembly", out var value2)) { _assemblyName = value2; } if (token.TryGetString("CLSID", out var value3)) { _clsid = value3; } if (token.Attributes != null) { Dictionary<string, string> dictionary = new Dictionary<string, string>(); string[] valueNames = token.Attributes.GetValueNames(); foreach (string text in valueNames) { if (token.Attributes.TryGetString(text, out var value4)) { dictionary.Add(text, value4); } } _attributes = new ReadOnlyDictionary<string, string>(dictionary); } if (token.Attributes != null && token.Attributes.TryGetString("AudioFormats", out var value5)) { _audioFormats = new ReadOnlyCollection<SpeechAudioFormatInfo>(SapiAttributeParser.GetAudioFormatsFromString(value5)); } else { _audioFormats = new ReadOnlyCollection<SpeechAudioFormatInfo>(new List<SpeechAudioFormatInfo>()); } } internal VoiceInfo(VoiceGender gender) { _gender = gender; } internal VoiceInfo(VoiceGender gender, VoiceAge age) { _gender = gender; _age = age; } internal VoiceInfo(VoiceGender gender, VoiceAge age, int voiceAlternate) { if (voiceAlternate < 0) { throw new ArgumentOutOfRangeException("voiceAlternate", SR.Get(SRID.PromptBuilderInvalidVariant)); } _gender = gender; _age = age; _variant = voiceAlternate + 1; } public override bool Equals(object obj) { if (obj is VoiceInfo voiceInfo && _name == voiceInfo._name && (_age == voiceInfo._age || _age == VoiceAge.NotSet || voiceInfo._age == VoiceAge.NotSet) && (_gender == voiceInfo._gender || _gender == VoiceGender.NotSet || voiceInfo._gender == VoiceGender.NotSet)) { if (_culture != null && voiceInfo._culture != null) { return _culture.Equals(voiceInfo._culture); } return true; } return false; } public override int GetHashCode() { return _name.GetHashCode(); } internal static bool ValidateGender(VoiceGender gender) { if (gender != VoiceGender.Female && gender != VoiceGender.Male && gender != VoiceGender.Neutral) { return gender == VoiceGender.NotSet; } return true; } internal static bool ValidateAge(VoiceAge age) { if (age != VoiceAge.Adult && age != VoiceAge.Child && age != 0 && age != VoiceAge.Senior) { return age == VoiceAge.Teen; } return true; } } public class VoiceChangeEventArgs : PromptEventArgs { private VoiceInfo _voice; public VoiceInfo Voice => _voice; internal VoiceChangeEventArgs(Prompt prompt, VoiceInfo voice) : base(prompt) { _voice = voice; } } public class VisemeReachedEventArgs : PromptEventArgs { private int _currentViseme; private TimeSpan _audioPosition; private TimeSpan _duration; private SynthesizerEmphasis _emphasis; private int _nextViseme; public int Viseme => _currentViseme; public TimeSpan AudioPosition => _audioPosition; public TimeSpan Duration => _duration; public SynthesizerEmphasis Emphasis => _emphasis; public int NextViseme => _nextViseme; internal VisemeReachedEventArgs(Prompt speakPrompt, int currentViseme, TimeSpan audioPosition, TimeSpan duration, SynthesizerEmphasis emphasis, int nextViseme) : base(speakPrompt) { _currentViseme = currentViseme; _audioPosition = audioPosition; _duration = duration; _emphasis = emphasis; _nextViseme = nextViseme; } } public class PhonemeReachedEventArgs : PromptEventArgs { private string _currentPhoneme; private TimeSpan _audioPosition; private TimeSpan _duration; private SynthesizerEmphasis _emphasis; private string _nextPhoneme; public string Phoneme => _currentPhoneme; public TimeSpan AudioPosition => _audioPosition; public TimeSpan Duration => _duration; public SynthesizerEmphasis Emphasis => _emphasis; public string NextPhoneme => _nextPhoneme; internal PhonemeReachedEventArgs(Prompt prompt, string currentPhoneme, TimeSpan audioPosition, TimeSpan duration, SynthesizerEmphasis emphasis, string nextPhoneme) : base(prompt) { _currentPhoneme = currentPhoneme; _audioPosition = audioPosition; _duration = duration; _emphasis = emphasis; _nextPhoneme = nextPhoneme; } } } namespace System.Speech.Synthesis.TtsEngine { [ComImport] [Guid("A74D7C8E-4CC5-4F2F-A6EB-804DEE18500E")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface ITtsEngine { [PreserveSig] void Speak(SPEAKFLAGS dwSpeakFlags, ref Guid rguidFormatId, IntPtr pWaveFormatEx, IntPtr pTextFragList, IntPtr pOutputSite); [PreserveSig] void GetOutputFormat(ref Guid pTargetFmtId, IntPtr pTargetWaveFormatEx, out Guid pOutputFormatId, out IntPtr ppCoMemOutputWaveFormatEx); } [StructLayout(LayoutKind.Sequential)] internal class SPVTEXTFRAG { public IntPtr pNext; public SPVSTATE State; public IntPtr pTextStart; public int ulTextLen; public int ulTextSrcOffset; public GCHandle gcText; public GCHandle gcNext; public GCHandle gcPhoneme; public GCHandle gcSayAsCategory; } internal enum SPVSKIPTYPE { SPVST_SENTENCE = 1 } [ComConversionLoss] [TypeLibType(16)] internal struct SPVSTATE { public SPVACTIONS eAction; public short LangID; public short wReserved; public int EmphAdj; public int RateAdj; public int Volume; public SPVPITCH PitchAdj; public int SilenceMSecs; public IntPtr pPhoneIds; public SPPARTOFSPEECH ePartOfSpeech; public SPVCONTEXT Context; } [TypeLibType(16)] internal struct SPVCONTEXT { public IntPtr pCategory; public IntPtr pBefore; public IntPtr pAfter; } [TypeLibType(16)] internal struct SPVPITCH { public int MiddleAdj; public int RangeAdj; } internal static class SAPIGuids { internal static readonly Guid SPDFID_WaveFormatEx = new Guid("C31ADBAE-527F-4ff5-A230-F62BB61FF70C"); } [Flags] internal enum SPEAKFLAGS { SPF_DEFAULT = 0, SPF_ASYNC = 1, SPF_PURGEBEFORESPEAK = 2, SPF_IS_FILENAME = 4, SPF_IS_XML = 8, SPF_IS_NOT_XML = 0x10, SPF_PERSIST_XML = 0x20, SPF_NLP_SPEAK_PUNC = 0x40, SPF_PARSE_SAPI = 0x80, SPF_PARSE_SSML = 0x100 } [Flags] internal enum SPVESACTIONS { SPVES_CONTINUE = 0, SPVES_ABORT = 1, SPVES_SKIP = 2, SPVES_RATE = 4, SPVES_VOLUME = 8 } [TypeLibType(16)] internal enum SPVACTIONS { SPVA_Speak, SPVA_Silence, SPVA_Pronounce, SPVA_Bookmark, SPVA_SpellOut, SPVA_Section, SPVA_ParseUnknownTag } [TypeLibType(16)] internal enum SPPARTOFSPEECH { SPPS_NotOverriden = -1, SPPS_Unknown = 0, SPPS_Noun = 4096, SPPS_Verb = 8192, SPPS_Modifier = 12288, SPPS_Function = 16384, SPPS_Interjection = 20480, SPPS_SuppressWord = 61440 } public abstract class TtsEngineSsml { protected TtsEngineSsml(string registryKey) { } public abstract IntPtr GetOutputFormat(SpeakOutputFormat speakOutputFormat, IntPtr targetWaveFormat); public abstract void AddLexicon(Uri uri, string mediaType, ITtsEngineSite site); public abstract void RemoveLexicon(Uri uri, ITtsEngineSite site); public abstract void Speak(TextFragment[] fragment, IntPtr waveHeader, ITtsEngineSite site); } [ImmutableObject(true)] public struct SpeechEventInfo : IEquatable<SpeechEventInfo> { private short _eventId; private short _parameterType; private int _param1; private IntPtr _param2; public short EventId { get { return _eventId; } internal set { _eventId = value; } } public short ParameterType { get { return _parameterType; } internal set { _parameterType = value; } } public int Param1 { get { return _param1; } internal set { _param1 = value; } } public IntPtr Param2 { get { return _param2; } internal set { _param2 = value; } } public SpeechEventInfo(short eventId, short parameterType, int param1, IntPtr param2) { _eventId = eventId; _parameterType = parameterType; _param1 = param1; _param2 = param2; } public static bool operator ==(SpeechEventInfo event1, SpeechEventInfo event2) { if (event1.EventId == event2.EventId && event1.ParameterType == event2.ParameterType && event1.Param1 == event2.Param1) { return event1.Param2 == event2.Param2; } return false; } public static bool operator !=(SpeechEventInfo event1, SpeechEventInfo event2) { return !(event1 == event2); } public bool Equals(SpeechEventInfo other) { return this == other; } public override bool Equals(object obj) { if (!(obj is SpeechEventInfo)) { return false; } return Equals((SpeechEventInfo)obj); } public override int GetHashCode() { return base.GetHashCode(); } } public interface ITtsEngineSite { int EventInterest { get; } int Actions { get; } int Rate { get; } int Volume { get; } void AddEvents(SpeechEventInfo[] events, int count); int Write(IntPtr data, int count); SkipInfo GetSkipInfo(); void CompleteSkip(int skipped); Stream LoadResource(Uri uri, string mediaType); } public class SkipInfo { private int _type; private int _count; public int Type { get { return _type; } set { _type = value; } } public int Count { get { return _count; } set { _count = value; } } internal SkipInfo(int type, int count) { _type = type; _count = count; } public SkipInfo() { } } [StructLayout(LayoutKind.Sequential)] public class TextFragment { private FragmentState _state; [MarshalAs(UnmanagedType.LPWStr)] private string _textToSpeak = string.Empty; private int _textOffset; private int _textLength; public FragmentState State { get { return _state; } set { _state = value; } } public string TextToSpeak { get { return _textToSpeak; } set { Helpers.ThrowIfEmptyOrNull(value, "value"); _textToSpeak = value; } } public int TextOffset { get { return _textOffset; } set { _textOffset = value; } } public int TextLength { get { return _textLength; } set { _textLength = value; } } public TextFragment() { } internal TextFragment(FragmentState fragState) : this(fragState, null, null, 0, 0) { } internal TextFragment(FragmentState fragState, string textToSpeak) : this(fragState, textToSpeak, textToSpeak, 0, textToSpeak.Length) { } internal TextFragment(FragmentState fragState, string textToSpeak, string textFrag, int offset, int length) { if (fragState.Action == TtsEngineAction.Speak || fragState.Action == TtsEngineAction.Pronounce) { textFrag = textToSpeak; } if (!string.IsNullOrEmpty(textFrag)) { TextToSpeak = textFrag; } State = fragState; TextOffset = offset; TextLength = length; } } [ImmutableObject(true)] public struct FragmentState : IEquatable<FragmentState> { private TtsEngineAction _action; private int _langId; private int _emphasis; private int _duration; private SayAs _sayAs; private Prosody _prosody; private char[] _phoneme; public TtsEngineAction Action { get { return _action; } internal set { _action = value; } } public int LangId { get { return _langId; } internal set { _langId = value; } } public int Emphasis { get { return _emphasis; } internal set { _emphasis = value; } } public int Duration { get { return _duration; } internal set { _duration = value; } } public SayAs SayAs { get { return _sayAs; } internal set { Helpers.ThrowIfNull(value, "value"); _sayAs = value; } } public Prosody Prosody { get { return _prosody; } internal set { Helpers.ThrowIfNull(value, "value"); _prosody = value; } } public char[] Phoneme { get { return _phoneme; } internal set { Helpers.ThrowIfNull(value, "value"); _phoneme = value; } } public FragmentState(TtsEngineAction action, int langId, int emphasis, int duration, SayAs sayAs, Prosody prosody, char[] phonemes) { _action = action; _langId = langId; _emphasis = emphasis; _duration = duration; _sayAs = sayAs; _prosody = prosody; _phoneme = phonemes; } public static bool operator ==(FragmentState state1, FragmentState state2) { if (state1.Action == state2.Action && state1.LangId == state2.LangId && state1.Emphasis == state2.Emphasis && state1.Duration == state2.Duration && state1.SayAs == state2.SayAs && state1.Prosody == state2.Prosody) { return object.Equals(state1.Phoneme, state2.Phoneme); } return false; } public static bool operator !=(FragmentState state1, FragmentState state2) { return !(state1 == state2); } public bool Equals(FragmentState other) { return this == other; } public override bool Equals(object obj) { if (!(obj is FragmentState)) { return false; } return Equals((FragmentState)obj); } public override int GetHashCode() { return base.GetHashCode(); } } [StructLayout(LayoutKind.Sequential)] public class Prosody { internal ProsodyNumber _pitch; internal ProsodyNumber _range; internal ProsodyNumber _rate; internal int _duration; internal ProsodyNumber _volume; internal ContourPoint[] _contourPoints; public ProsodyNumber Pitch { get { return _pitch; } set { _pitch = value; } } public ProsodyNumber Range { get { return _range; } set { _range = value; } } public ProsodyNumber Rate { get { return _rate; } set { _rate = value; } } public int Duration { get { return _duration; } set { _duration = value; } } public ProsodyNumber Volume { get { return _volume; } set { _volume = value; } } public ContourPoint[] GetContourPoints() { return _contourPoints; } public void SetContourPoints(ContourPoint[] points) { Helpers.ThrowIfNull(points, "points"); _contourPoints = (ContourPoint[])points.Clone(); } public Prosody() { Pitch = new ProsodyNumber(0); Range = new ProsodyNumber(0); Rate = new ProsodyNumber(0); Volume = new ProsodyNumber(-1); } internal Prosody Clone() { Prosody prosody = new Prosody(); prosody._pitch = _pitch; prosody._range = _range; prosody._rate = _rate; prosody._duration = _duration; prosody._volume = _volume; return prosody; } } [ImmutableObject(true)] public struct ContourPoint : IEquatable<ContourPoint> { private float _start; private float _change; private ContourPointChangeType _changeType; public float Start => _start; public float Change => _change; public ContourPointChangeType ChangeType => _changeType; public ContourPoint(float start, float change, ContourPointChangeType changeType) { _start = start; _change = change; _changeType = changeType; } public static bool operator ==(ContourPoint point1, ContourPoint point2) { if (point1.Start.Equals(point2.Start) && point1.Change.Equals(point2.Change)) { return point1.ChangeType.Equals(point2.ChangeType); } return false; } public static bool operator !=(ContourPoint point1, ContourPoint point2) { return !(point1 == point2); } public bool Equals(ContourPoint other) { return this == other; } public override bool Equals(object obj) { if (!(obj is ContourPoint)) { return false; } return Equals((ContourPoint)obj); } public override int GetHashCode() { return base.GetHashCode(); } } [ImmutableObject(true)] public struct ProsodyNumber : IEquatable<ProsodyNumber> { public const int AbsoluteNumber = int.MaxValue; private int _ssmlAttributeId; private bool _isPercent; private float _number; private ProsodyUnit _unit; public int SsmlAttributeId { get { return _ssmlAttributeId; } internal set { _ssmlAttributeId = value; } } public bool IsNumberPercent { get { return _isPercent; } internal set { _isPercent = value; } } public float Number { get { return _number; } internal set { _number = value; } } public ProsodyUnit Unit { get { return _unit; } internal set { _unit = value; } } public ProsodyNumber(int ssmlAttributeId) { _ssmlAttributeId = ssmlAttributeId; _number = 1f; _isPercent = true; _unit = ProsodyUnit.Default; } public ProsodyNumber(float number) { _ssmlAttributeId = int.MaxValue; _number = number; _isPercent = false; _unit = ProsodyUnit.Default; } public static bool operator ==(ProsodyNumber prosodyNumber1, ProsodyNumber prosodyNumber2) { if (prosodyNumber1._ssmlAttributeId == prosodyNumber2._ssmlAttributeId && prosodyNumber1.Number.Equals(prosodyNumber2.Number) && prosodyNumber1.IsNumberPercent == prosodyNumber2.IsNumberPercent) { return prosodyNumber1.Unit == prosodyNumber2.Unit; } return false; } public static bool operator !=(ProsodyNumber prosodyNumber1, ProsodyNumber prosodyNumber2) { return !(prosodyNumber1 == prosodyNumber2); } public bool Equals(ProsodyNumber other) { return this == other; } public override bool Equals(object obj) { if (!(obj is ProsodyNumber)) { return false; } return Equals((ProsodyNumber)obj); } public override int GetHashCode() { return base.GetHashCode(); } } [StructLayout(LayoutKind.Sequential)] public class SayAs { [MarshalAs(UnmanagedType.LPWStr)] private string _interpretAs; [MarshalAs(UnmanagedType.LPWStr)] private string _format; [MarshalAs(UnmanagedType.LPWStr)] private string _detail; public string InterpretAs { get { return _interpretAs; } set { Helpers.ThrowIfEmptyOrNull(value, "value"); _interpretAs = value; } } public string Format { get { return _format; } set { Helpers.ThrowIfEmptyOrNull(value, "value"); _format = value; } } public string Detail { get { return _detail; } set { Helpers.ThrowIfEmptyOrNull(value, "value"); _detail = value; } } } public enum TtsEngineAction { Speak, Silence, Pronounce, Bookmark, SpellOut, StartSentence, StartParagraph, ParseUnknownTag } public enum EmphasisWord { Default, Strong, Moderate, None, Reduced } public enum EmphasisBreak { None = -1, ExtraWeak = -2, Weak = -3, Medium = -4, Strong = -5, ExtraStrong = -6, Default = -7 } public enum ProsodyPitch { Default, ExtraLow, Low, Medium, High, ExtraHigh } public enum ProsodyRange { Default, ExtraLow, Low, Medium, High, ExtraHigh } public enum ProsodyRate { Default, ExtraSlow, Slow, Medium, Fast, ExtraFast } public enum ProsodyVolume { Default = -1, Silent = -2, ExtraSoft = -3, Soft = -4, Medium = -5, Loud = -6, ExtraLoud = -7 } public enum ProsodyUnit { Default, Hz, Semitone } public enum TtsEventId { StartInputStream = 1, EndInputStream, VoiceChange, Bookmark, WordBoundary, Phoneme, SentenceBoundary, Viseme, AudioLevel } public enum EventParameterType { Undefined, Token, Object, Pointer, String } public enum SpeakOutputFormat { WaveFormat, Text } public enum ContourPointChangeType { Hz, Percentage } [ComImport] [Guid("2D0FA0DB-AEA2-4AE2-9F8A-7AFC7794E56B")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface ITtsEngineSsml { void GetOutputFormat(SpeakOutputFormat speakOutputFormat, IntPtr targetWaveFormat, out IntPtr waveHeader); void AddLexicon(string location, string mediaType, IntPtr site); void RemoveLexicon(string location, IntPtr site); void Speak(IntPtr fragments, int count, IntPtr waveHeader, IntPtr site); } internal struct TextFragmentInterop { internal FragmentStateInterop _state; [MarshalAs(UnmanagedType.LPWStr)] internal string _textToSpeak; internal int _textOffset; internal int _textLength; internal static IntPtr FragmentToPtr(List<TextFragment> textFragments, Collection<IntPtr> memoryBlocks) { TextFragmentInterop textFragmentInterop = default(TextFragmentInterop); int count = textFragments.Count; int num = Marshal.SizeOf((object)textFragmentInterop); IntPtr intPtr = Marshal.AllocCoTaskMem(num * count); memoryBlocks.Add(intPtr); for (int i = 0; i < count; i++) { textFragmentInterop._state.FragmentStateToPtr(textFragments[i].State, memoryBlocks); textFragmentInterop._textToSpeak = textFragments[i].TextToSpeak; textFragmentInterop._textOffset = textFragments[i].TextOffset; textFragmentInterop._textLength = textFragments[i].TextLength; Marshal.StructureToPtr((object)textFragmentInterop, (IntPtr)((long)intPtr + i * num), fDeleteOld: false); } return intPtr; } } internal struct FragmentStateInterop { internal TtsEngineAction _action; internal int _langId; internal int _emphasis; internal int _duration; internal IntPtr _sayAs; internal IntPtr _prosody; internal IntPtr _phoneme; internal void FragmentStateToPtr(FragmentState state, Collection<IntPtr> memoryBlocks) { _action = state.Action; _langId = state.LangId; _emphasis = state.Emphasis; _duration = state.Duration; if (state.SayAs != null) { _sayAs = Marshal.AllocCoTaskMem(Marshal.SizeOf((object)state.SayAs)); memoryBlocks.Add(_sayAs); Marshal.StructureToPtr((object)state.SayAs, _sayAs, fDeleteOld: false); } else { _sayAs = IntPtr.Zero; } if (state.Phoneme != null) { short[] array = new short[state.Phoneme.Length + 1]; for (uint num = 0u; num < state.Phoneme.Length; num++) { array[num] = (short)state.Phoneme[num]; } array[state.Phoneme.Length] = 0; int num2 = Marshal.SizeOf((object)array[0]); _phoneme = Marshal.AllocCoTaskMem(num2 * array.Length); memoryBlocks.Add(_phoneme); for (uint num3 = 0u; num3 < array.Length; num3++) { Marshal.Copy(array, 0, _phoneme, array.Length); } } else { _phoneme = IntPtr.Zero; } _prosody = ProsodyInterop.ProsodyToPtr(state.Prosody, memoryBlocks); } } internal struct ProsodyInterop { internal ProsodyNumber _pitch; internal ProsodyNumber _range; internal ProsodyNumber _rate; internal int _duration; internal ProsodyNumber _volume; internal IntPtr _contourPoints; internal static IntPtr ProsodyToPtr(Prosody prosody, Collection<IntPtr> memoryBlocks) { if (prosody == null) { return IntPtr.Zero; } ProsodyInterop prosodyInterop = default(ProsodyInterop); prosodyInterop._pitch = prosody.Pitch; prosodyInterop._range = prosody.Range; prosodyInterop._rate = prosody.Rate; prosodyInterop._duration = prosody.Duration; prosodyInterop._volume = prosody.Volume; ContourPoint[] contourPoints = prosody.GetContourPoints(); if (contourPoints != null) { int num = Marshal.SizeOf((object)contourPoints[0]); prosodyInterop._contourPoints = Marshal.AllocCoTaskMem(contourPoints.Length * num); memoryBlocks.Add(prosodyInterop._contourPoints); for (uint num2 = 0u; num2 < contourPoints.Length; num2++) { Marshal.StructureToPtr((object)contourPoints[num2], (IntPtr)((long)prosodyInterop._contourPoints + num * num2), fDeleteOld: false); } } else { prosodyInterop._contourPoints = IntPtr.Zero; } IntPtr intPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf((object)prosodyInterop)); memoryBlocks.Add(intPtr); Marshal.StructureToPtr((object)prosodyInterop, intPtr, fDeleteOld: false); return intPtr; } } } namespace System.Speech.Recognition { [DebuggerDisplay("Grammar: {(_uri != null ? \"uri=\" + _uri.ToString () + \" \" : \"\") + \"rule=\" + _ruleName }")] public class Grammar { private struct NameValuePair { internal string _name; internal string _value; } internal GrammarOptions _semanticTag; internal AppDomain _appDomain; internal AppDomainGrammarProxy _proxy; internal ScriptRef[] _scripts; private byte[] _cfgData; private Stream _appStream; private bool _isSrgsDocument; private SrgsDocument _srgsDocument; private GrammarBuilder _grammarBuilder; private IRecognizerInternal _recognizer; private GrammarState _grammarState; private Exception _loadException; private Uri _uri; private Uri _baseUri; private string _ruleName; private string _resources; private object[] _parameters; private string _onInitParameters; private bool _enabled = true; private bool _isStg; private bool _sapi53Only; private uint _sapiGrammarId; private float _weight = 1f; private int _priority; private InternalGrammarData _internalData; private string _grammarName = string.Empty; private Collection<Grammar> _ruleRefs; private static ResourceLoader _resourceLoader = new ResourceLoader(); public bool Enabled { get { return _enabled; } set { if (_grammarState != 0 && _enabled != value) { _recognizer.SetGrammarState(this, value); } _enabled = value; } } public float Weight { get { return _weight; } set { if ((double)value < 0.0 || (double)value > 1.0) { throw new ArgumentOutOfRangeException("value", SR.Get(SRID.GrammarInvalidWeight)); } if (_grammarState != 0 && !_weight.Equals(value)) { _recognizer.SetGrammarWeight(this, value); } _weight = value; } } public int Priority { get { return _priority; } set { if (value < -128 || value > 127) { throw new ArgumentOutOfRangeException("value", SR.Get(SRID.GrammarInvalidPriority)); } if (_grammarState != 0 && _priority != value) { _recognizer.SetGrammarPriority(this, value); } _priority = value; } } public string Name { get { return _grammarName; } set { if (value == null) { value = string.Empty; } _grammarName = value; } } public string RuleName => _ruleName; public bool Loaded => _grammarState == GrammarState.Loaded; internal Uri Uri => _uri; internal IRecognizerInternal Recognizer { get { return _recognizer; } set { _recognizer = value; } } internal GrammarState State { get { return _grammarState; } set { switch (value) { case GrammarState.Unloaded: _loadException = null; _recognizer = null; if (_appDomain != null) { AppDomain.Unload(_appDomain); _appDomain = null; } break; default: _ = 3; break; case GrammarState.Loaded: break; } _grammarState = value; } } internal Exception LoadException { get { return _loadException; } set { _loadException = value; } } internal byte[] CfgData => _cfgData; internal Uri BaseUri => _baseUri; internal bool Sapi53Only => _sapi53Only; internal uint SapiGrammarId { get { return _sapiGrammarId; } set { _sapiGrammarId = value; } } protected internal virtual bool IsStg => _isStg; internal bool IsSrgsDocument => _isSrgsDocument; internal InternalGrammarData InternalData { get { return _internalData; } set { _internalData = value; } } protected string ResourceName { get { return _resources; } set { Helpers.ThrowIfEmptyOrNull(value, "value"); _resources = value; } } public event EventHandler<SpeechRecognizedEventArgs> SpeechRecognized; internal Grammar(Uri uri, string ruleName, object[] parameters) { Helpers.ThrowIfNull(uri, "uri"); _uri = uri; InitialGrammarLoad(ruleName, parameters, isImportedGrammar: false); } public Grammar(string path) : this(path, (string)null, (object[])null) { } public Grammar(string path, string ruleName) : this(path, ruleName, null) { } public Grammar(string path, string ruleName, object[] parameters) { try { _uri = new Uri(path, UriKind.Relative); } catch (UriFormatException innerException) { throw new ArgumentException(SR.Get(SRID.RecognizerGrammarNotFound), "path", innerException); } InitialGrammarLoad(ruleName, parameters, isImportedGrammar: false); } public Grammar(SrgsDocument srgsDocument) : this(srgsDocument, null, null, null) { } public Grammar(SrgsDocument srgsDocument, string ruleName) : this(srgsDocument, ruleName, null, null) { } public Grammar(SrgsDocument srgsDocument, string ruleName, object[] parameters) : this(srgsDocument, ruleName, null, parameters) { } [EditorBrowsable(EditorBrowsableState.Advanced)] public Grammar(SrgsDocument srgsDocument, string ruleName, Uri baseUri) : this(srgsDocument, ruleName, baseUri, null) { } [EditorBrowsable(EditorBrowsableState.Advanced)] public Grammar(SrgsDocument srgsDocument, string ruleName, Uri baseUri, object[] parameters) { Helpers.ThrowIfNull(srgsDocument, "srgsDocument"); _srgsDocument = srgsDocument; _isSrgsDocument = srgsDocument != null; _baseUri = baseUri; InitialGrammarLoad(ruleName, parameters, isImportedGrammar: false); } public Grammar(Stream stream) : this(stream, null, null, null) { } public Grammar(Stream stream, string ruleName) : this(stream, ruleName, null, null) { } public Grammar(Stream stream, string ruleName, object[] parameters) : this(stream, ruleName, null, parameters) { } [EditorBrowsable(EditorBrowsableState.Advanced)] public Grammar(Stream stream, string ruleName, Uri baseUri) : this(stream, ruleName, baseUri, null) { } [EditorBrowsable(EditorBrowsableState.Advanced)] public Grammar(Stream stream, string ruleName, Uri baseUri, object[] parameters) { Helpers.ThrowIfNull(stream, "stream"); if (!stream.CanRead) { throw new ArgumentException(SR.Get(SRID.StreamMustBeReadable), "stream"); } _appStream = stream; _baseUri = baseUri; InitialGrammarLoad(ruleName, parameters, isImportedGrammar: false); } public Grammar(GrammarBuilder builder) { Helpers.ThrowIfNull(builder, "builder"); _grammarBuilder = builder; InitialGrammarLoad(null, null, isImportedGrammar: false); } private Grammar(string onInitParameters, Stream stream, string ruleName) { _appStream = stream; _onInitParameters = onInitParameters; InitialGrammarLoad(ruleName, null, isImportedGrammar: true); } protected Grammar() { } protected void StgInit(object[] parameters) { _parameters = parameters; LoadAndCompileCfgData(isImportedGrammar: false, stgInit: true); } public static Grammar LoadLocalizedGrammarFromType(Type type, params object[] onInitParameters) { Helpers.ThrowIfNull(type, "type"); if (type == typeof(Grammar) || !type.IsSubclassOf(typeof(Grammar))) { throw new ArgumentException(SR.Get(SRID.StrongTypedGrammarNotAGrammar), "type"); } Assembly assembly = Assembly.GetAssembly(type); Type[] types = assembly.GetTypes(); foreach (Type type2 in types) { string s = null; if ((!(type2 == type) && !type2.IsSubclassOf(type)) || !(type2.GetField("__cultureId") != null)) { continue; } try { s = (string)type2.InvokeMember("__cultureId", BindingFlags.GetField, null, null, null, null); } catch (Exception ex) { if (!(ex is MissingFieldException)) { throw; } } if (Helpers.CompareInvariantCulture(new CultureInfo(int.Parse(s, CultureInfo.InvariantCulture)), CultureInfo.CurrentUICulture)) { try { return (Grammar)assembly.CreateInstance(type2.FullName, ignoreCase: false, BindingFlags.CreateInstance, null, onInitParameters, null, null); } catch (MissingMemberException) { throw new ArgumentException(SR.Get(SRID.RuleScriptInvalidParameters, type2.Name, type2.Name)); } } } return null; } internal static Grammar Create(string grammarName, string ruleName, string onInitParameter, out Uri redirectUri) { redirectUri = null; grammarName = grammarName.Trim(); Uri result; bool flag = Uri.TryCreate(grammarName, UriKind.Absolute, out result); int num = grammarName.IndexOf(".dll", StringComparison.OrdinalIgnoreCase); if (!flag || (num > 0 && num == grammarName.Length - 4)) { Assembly assembly; if (flag) { if (!result.IsFile) { throw new InvalidOperationException(); } assembly = Assembly.LoadFrom(result.LocalPath); } else { assembly = Assembly.Load(grammarName); } return LoadGrammarFromAssembly(assembly, ruleName, onInitParameter); } try { string localPath; using Stream stream = _resourceLoader.LoadFile(result, out localPath, out redirectUri); try { return new Grammar(onInitParameter, stream, ruleName); } finally { _resourceLoader.UnloadFile(localPath); } } catch { Assembly assembly2 = Assembly.LoadFrom(grammarName); return LoadGrammarFromAssembly(assembly2, ruleName, onInitParameter); } } internal void OnRecognitionInternal(SpeechRecognizedEventArgs eventArgs) { this.SpeechRecognized?.Invoke(this, eventArgs); } internal static bool IsDictationGrammar(Uri uri) { if (uri == null || !uri.IsAbsoluteUri || uri.Scheme != "grammar" || !string.IsNullOrEmpty(uri.Host) || !string.IsNullOrEmpty(uri.Authority) || !string.IsNullOrEmpty(uri.Query) || uri.PathAndQuery != "dictation") { return false; } return true; } internal bool IsDictation(Uri uri) { bool flag = IsDictationGrammar(uri); if (!flag && this is DictationGrammar) { throw new ArgumentException(SR.Get(SRID.DictationInvalidTopic), "uri"); } return flag; } internal Grammar Find(long grammarId) { if (_ruleRefs != null) { foreach (Grammar ruleRef in _ruleRefs) { if (grammarId == ruleRef._sapiGrammarId) { return ruleRef; } Grammar result; if ((result = ruleRef.Find(grammarId)) != null) { return result; } } } return null; } internal Grammar Find(string ruleName) { if (_ruleRefs != null) { foreach (Grammar ruleRef in _ruleRefs) { if (ruleName == ruleRef.RuleName) { return ruleRef; } Grammar result; if ((result = ruleRef.Find(ruleName)) != null) { return result; } } } return null; } internal void AddRuleRef(Grammar ruleRef, uint grammarId) { if (_ruleRefs == null) { _ruleRefs = new Collection<Grammar>(); } _ruleRefs.Add(ruleRef); _sapiGrammarId = grammarId; } internal MethodInfo MethodInfo(string method) { return GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } private void LoadAndCompileCfgData(bool isImportedGrammar, bool stgInit) { Stream stream = (IsStg ? LoadCfgFromResource(stgInit) : LoadCfg(isImportedGrammar, stgInit)); SrgsRule[] array = RunOnInit(IsStg); if (array != null) { MemoryStream memoryStream = CombineCfg(_ruleName, stream, array); stream.Close(); stream = memoryStream; } _cfgData = Helpers.ReadStreamToByteArray(stream, (int)stream.Length); stream.Close(); _srgsDocument = null; _appStream = null; } private MemoryStream LoadCfg(bool isImportedGrammar, bool stgInit) { Uri uri = Uri; MemoryStream memoryStream = new MemoryStream(); if (uri != null) { string mimeType; string localPath; using (Stream stream = _resourceLoader.LoadFile(uri, out mimeType, out _baseUri, out localPath)) { stream.Position = 0L; SrgsGrammarCompiler.CompileXmlOrCopyCfg(stream, memoryStream, uri); } _resourceLoader.UnloadFile(localPath); } else if (_srgsDocument != null) { SrgsGrammarCompiler.Compile(_srgsDocument, memoryStream); if (_baseUri == null && _srgsDocument.BaseUri != null) { _baseUri = _srgsDocument.BaseUri; } } else if (_grammarBuilder != null) { _grammarBuilder.Compile(memoryStream); } else { SrgsGrammarCompiler.CompileXmlOrCopyCfg(_appStream, memoryStream, null); } memoryStream.Position = 0L; _ruleName = CheckRuleName(memoryStream, _ruleName, isImportedGrammar, stgInit, out _sapi53Only, out _semanticTag); CreateSandbox(memoryStream); memoryStream.Position = 0L; return memoryStream; } private static Grammar LoadGrammarFromAssembly(Assembly assembly, string ruleName, string onInitParameters) { Type typeFromHandle = typeof(Grammar); Type type = null; Type[] types = assembly.GetTypes(); foreach (Type type2 in types) { if (!type2.IsSubclassOf(typeFromHandle)) { continue; } string s = null; if (type2.Name == ruleName) { type = type2; } if ((!(type2 == type) && (!(type != null) || !type2.IsSubclassOf(type))) || !(type2.GetField("__cultureId") != null)) { continue; } try { s = (string)type2.InvokeMember("__cultureId", BindingFlags.GetField, null, null, null, null); } catch (Exception ex) { if (!(ex is MissingFieldException)) { throw; } } if (Helpers.CompareInvariantCulture(new CultureInfo(int.Parse(s, CultureInfo.InvariantCulture)), CultureInfo.CurrentUICulture)) { try { object[] args = MatchInitParameters(type2, onInitParameters, assembly.GetName().Name, ruleName); return (Grammar)assembly.CreateInstance(type2.FullName, ignoreCase: false, BindingFlags.CreateInstance, null, args, null, null); } catch (MissingMemberException) { throw new ArgumentException(SR.Get(SRID.RuleScriptInvalidParameters, type2.Name, type2.Name)); } } } return null; } private static object[] MatchInitParameters(Type type, string onInitParameters, string grammar, string rule) { ConstructorInfo[] constructors = type.GetConstructors(); NameValuePair[] array = ParseInitParams(onInitParameters); object[] array2 = new object[array.Length]; bool flag = false; for (int i = 0; i < constructors.Length; i++) { if (flag) { break; } ParameterInfo[] parameters = constructors[i].GetParameters(); if (parameters.Length > array.Length) { continue; } flag = true; for (int j = 0; j < array.Length &