Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of BoomboxTTS v1.0.1
BoomboxMod.dll
Decompiled 2 years 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 2 years 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 &