Decompiled source of TextToSpeech v1.0.0

TextToSpeech.dll

Decompiled 17 hours ago
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("TextToSpeech")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Azumatt")]
[assembly: AssemblyProduct("TextToSpeech")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 TextToSpeech
{
	[HarmonyPatch(typeof(TextViewer), "ShowText")]
	public static class TextViewerShowTextPatch
	{
		private static void Postfix(TextViewer __instance, Style style, string topic, string textId, bool autoHide)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected I4, but got Unknown
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			UtilityMethods.GetPlayerAudioSource(out AudioSource playerSource);
			if (!((Object)(object)playerSource == (Object)null))
			{
				string text;
				string text2;
				switch ((int)style)
				{
				case 0:
					text = __instance.m_topic.text;
					text2 = __instance.m_text.text;
					break;
				case 1:
					text = __instance.m_introTopic.text;
					text2 = __instance.m_introText.text;
					break;
				case 2:
					text = __instance.m_ravenTopic.text;
					text2 = __instance.m_ravenText.text;
					break;
				default:
					text = Localization.instance.Localize(topic);
					text2 = Localization.instance.Localize(textId);
					break;
				}
				UtilityMethods.FireAndForget(TextToSpeechPlugin.Speak(UtilityMethods.StripRichText((topic.Length > 0) ? (text + " " + text2) : text2), playerSource));
			}
		}
	}
	[HarmonyPatch(typeof(Chat), "SetNpcText")]
	public static class ShowRavenMessagesChatSetNpcTextPatch
	{
		private static void Postfix(Chat __instance, GameObject talker, Vector3 offset, float cullDistance, float ttl, string topic, string text, bool large)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)Player.m_localPlayer == (Object)null))
			{
				UtilityMethods.GetPlayerAudioSource(out AudioSource playerSource);
				if (!((Object)(object)playerSource == (Object)null))
				{
					UtilityMethods.FireAndForget(TextToSpeechPlugin.Speak((topic.Length > 0) ? UtilityMethods.StripRichText(Localization.instance.Localize(topic) + Localization.instance.Localize(text)) : UtilityMethods.StripRichText(Localization.instance.Localize(text)), playerSource, playAtPoint: true, talker.transform.position));
				}
			}
		}
	}
	[HarmonyPatch(typeof(MessageHud), "ShowMessage")]
	public static class MessageHudShowMessagePatch
	{
		private static void Postfix(MessageHud __instance, MessageType type, string text)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)Player.m_localPlayer == (Object)null) && (int)type == 2)
			{
				UtilityMethods.GetPlayerAudioSource(out AudioSource playerSource);
				if (!((Object)(object)playerSource == (Object)null))
				{
					UtilityMethods.FireAndForget(TextToSpeechPlugin.Speak(UtilityMethods.StripRichText(Localization.instance.Localize(text)), playerSource));
				}
			}
		}
	}
	[HarmonyPatch(typeof(Terminal), "AddString", new Type[]
	{
		typeof(string),
		typeof(string),
		typeof(Type),
		typeof(bool)
	})]
	public static class TerminalAddStringPatch
	{
		private static void Postfix(string user, string text, Type type, bool timestamp)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected I4, but got Unknown
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			UtilityMethods.GetPlayerAudioSource(out AudioSource playerSource);
			if (!((Object)(object)playerSource == (Object)null))
			{
				string text2 = (int)type switch
				{
					3 => "pinged", 
					2 => "shouted", 
					0 => "whispered", 
					_ => "said", 
				};
				if (!(user == Player.m_localPlayer.GetPlayerName()) || !TextToSpeechPlugin.SkipSelf.Value.IsOn())
				{
					UtilityMethods.FireAndForget(TextToSpeechPlugin.Speak(UtilityMethods.StripRichText(user + " " + text2 + " in  chat: " + text), playerSource));
				}
			}
		}
	}
	[HarmonyPatch(typeof(DreamTexts), "GetRandomDreamText")]
	public static class DreamTextsGetRandomDreamTextPatch
	{
		private static void Postfix(DreamTexts __instance, ref DreamText? __result)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (__result != null && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				UtilityMethods.GetPlayerAudioSource(out AudioSource playerSource);
				if (!((Object)(object)playerSource == (Object)null))
				{
					UtilityMethods.FireAndForget(TextToSpeechPlugin.Speak(UtilityMethods.StripRichText(Localization.instance.Localize(__result.m_text)), playerSource));
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "SetLocalPlayer")]
	public static class PlayerSetLocalPlayerPatch
	{
		private static void Postfix(Player __instance)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)((Component)__instance).transform.Find("PiperTTS") != (Object)null))
			{
				GameObject val = new GameObject("PiperTTS");
				AudioSource obj = val.AddComponent<AudioSource>();
				obj.spatialBlend = 0f;
				obj.dopplerLevel = 0f;
				obj.rolloffMode = (AudioRolloffMode)1;
				obj.minDistance = 1f;
				obj.maxDistance = 100f;
				obj.ignoreListenerVolume = true;
				obj.ignoreListenerPause = true;
				obj.bypassEffects = true;
				obj.bypassReverbZones = true;
				val.transform.SetParent(((Component)__instance).transform);
			}
		}
	}
	[BepInPlugin("Azumatt.TextToSpeech", "TextToSpeech", "1.0.0")]
	public class TextToSpeechPlugin : BaseUnityPlugin
	{
		public enum Toggle
		{
			On = 1,
			Off = 0
		}

		internal const string ModName = "TextToSpeech";

		internal const string ModVersion = "1.0.0";

		internal const string Author = "Azumatt";

		private const string ModGUID = "Azumatt.TextToSpeech";

		private static string ConfigFileName = "Azumatt.TextToSpeech.cfg";

		private static string ConfigFileFullPath;

		internal static string ConnectionError;

		private readonly Harmony _harmony = new Harmony("Azumatt.TextToSpeech");

		public static readonly ManualLogSource TextToSpeechLogger;

		private FileSystemWatcher _watcher;

		private readonly object _reloadLock = new object();

		private DateTime _lastConfigReloadTime;

		private const long RELOAD_DELAY = 10000000L;

		private static readonly HttpClient httpClient;

		private const string PiperZipUrl = "https://github.com/rhasspy/piper/releases/download/2023.11.14-2/piper_windows_amd64.zip";

		private string _voiceModelConfigUrl = string.Empty;

		private static string _voiceModelConfigPath;

		private string _modDirectory = string.Empty;

		private string _piperFolder = string.Empty;

		private string _piperZipPath = string.Empty;

		private static string _piperExePath;

		private static string _voiceModelPath;

		internal static ConfigEntry<string> VoiceModelUrl;

		internal static ConfigEntry<Toggle> SkipSelf;

		public async void Awake()
		{
			bool saveOnSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			VoiceModelUrl = config("1 - General", "Voice Model URL", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_GB/alba/medium/en_GB-alba-medium.onnx", "URL for the voice model file.");
			SkipSelf = config("1 - Preferences", "Skip Self In Chat", Toggle.On, "Do not TTS your own messages in chat.");
			_voiceModelConfigUrl = VoiceModelUrl.Value.Replace("?download=true", string.Empty) + ".json";
			_modDirectory = ((!string.IsNullOrEmpty(((BaseUnityPlugin)this).Info.Location)) ? Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) : Environment.CurrentDirectory);
			_piperFolder = Path.Combine(_modDirectory, "Piper");
			Directory.CreateDirectory(_piperFolder);
			UpdateVoiceModelPaths();
			VoiceModelUrl.SettingChanged += delegate
			{
				UpdateVoiceModelPaths();
				EnsureVoiceModel();
				EnsureVoiceModelConfig();
			};
			_piperZipPath = Path.Combine(_piperFolder, "piper_windows_amd64.zip");
			_piperExePath = Path.Combine(_piperFolder, "piper", "piper.exe");
			await EnsurePiperFiles();
			await EnsureVoiceModel();
			await EnsureVoiceModelConfig();
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			_harmony.PatchAll(executingAssembly);
			SetupWatcher();
			((BaseUnityPlugin)this).Config.Save();
			if (saveOnSet)
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnSet;
			}
		}

		private async Task EnsurePiperFiles()
		{
			if (!File.Exists(_piperExePath))
			{
				TextToSpeechLogger.LogInfo((object)"Piper executable not found. Downloading Piper zip...");
				await DownloadFile("https://github.com/rhasspy/piper/releases/download/2023.11.14-2/piper_windows_amd64.zip", _piperZipPath);
				TextToSpeechLogger.LogInfo((object)"Extracting Piper zip...");
				try
				{
					ZipFile.ExtractToDirectory(_piperZipPath, _piperFolder);
					TextToSpeechLogger.LogInfo((object)"Piper extracted successfully.");
					File.Delete(_piperZipPath);
					return;
				}
				catch (Exception ex)
				{
					TextToSpeechLogger.LogError((object)("Failed to extract Piper: " + ex.Message));
					return;
				}
			}
			TextToSpeechLogger.LogInfo((object)"Piper already exists.");
		}

		private async Task EnsureVoiceModel()
		{
			if (!File.Exists(_voiceModelPath))
			{
				TextToSpeechLogger.LogInfo((object)"Voice model not found. Downloading voice model...");
				await DownloadFile(VoiceModelUrl.Value, _voiceModelPath);
			}
			else
			{
				TextToSpeechLogger.LogInfo((object)"Voice model already exists.");
			}
		}

		private async Task EnsureVoiceModelConfig()
		{
			if (!File.Exists(_voiceModelConfigPath))
			{
				TextToSpeechLogger.LogInfo((object)"Voice model config not found. Downloading voice model config...");
				await DownloadFile(_voiceModelConfigUrl, _voiceModelConfigPath);
			}
			else
			{
				TextToSpeechLogger.LogInfo((object)"Voice model config already exists.");
			}
		}

		private async Task DownloadFile(string url, string destinationPath)
		{
			try
			{
				File.WriteAllBytes(destinationPath, await httpClient.GetByteArrayAsync(url));
				TextToSpeechLogger.LogInfo((object)("Downloaded file from " + url + " to " + destinationPath));
			}
			catch (Exception ex)
			{
				TextToSpeechLogger.LogError((object)("Failed to download file from " + url + ": " + ex.Message));
			}
		}

		private void UpdateVoiceModelPaths()
		{
			if (Uri.TryCreate(VoiceModelUrl.Value, UriKind.Absolute, out Uri result))
			{
				string fileName = Path.GetFileName(result.LocalPath);
				if (string.IsNullOrEmpty(fileName))
				{
					TextToSpeechLogger.LogError((object)("Failed to extract file name from URL: " + VoiceModelUrl.Value));
					return;
				}
				_voiceModelPath = Path.Combine(_piperFolder, fileName);
				_voiceModelConfigPath = Path.Combine(_piperFolder, fileName + ".json");
				_voiceModelConfigUrl = VoiceModelUrl.Value.Replace("?download=true", string.Empty) + ".json";
				TextToSpeechLogger.LogDebug((object)("Updated voice model path to: " + _voiceModelPath + " and config path to: " + _voiceModelConfigPath));
			}
			else
			{
				TextToSpeechLogger.LogError((object)("Invalid voice model URL: " + VoiceModelUrl.Value));
			}
		}

		public static async Task Speak(string text, AudioSource? targetSource = null, bool playAtPoint = false, Vector3 position = default(Vector3))
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			_ = 1;
			try
			{
				string tempWavPath = Path.Combine(Path.GetTempPath(), $"piper_tts_{Guid.NewGuid()}.wav");
				ProcessStartInfo processStartInfo = new ProcessStartInfo();
				processStartInfo.FileName = _piperExePath;
				processStartInfo.Arguments = "--model \"" + _voiceModelPath + "\" --model_config \"" + _voiceModelConfigPath + "\" --output_file \"" + tempWavPath + "\"";
				processStartInfo.RedirectStandardInput = true;
				processStartInfo.RedirectStandardError = true;
				processStartInfo.UseShellExecute = false;
				processStartInfo.CreateNoWindow = true;
				ProcessStartInfo startInfo = processStartInfo;
				using (Process p = new Process
				{
					StartInfo = startInfo
				})
				{
					p.Start();
					await p.StandardInput.WriteLineAsync(text);
					p.StandardInput.Close();
					string text2 = await p.StandardError.ReadToEndAsync();
					p.WaitForExit();
					if (!string.IsNullOrEmpty(text2))
					{
						if (!text2.Contains("[info]"))
						{
							TextToSpeechLogger.LogError((object)("Piper error: " + text2));
						}
						else
						{
							TextToSpeechLogger.LogDebug((object)("Piper info: " + text2));
						}
					}
				}
				if (File.Exists(tempWavPath))
				{
					PlayAudio(File.ReadAllBytes(tempWavPath), targetSource, playAtPoint, position);
					File.Delete(tempWavPath);
				}
				else
				{
					TextToSpeechLogger.LogError((object)"Temporary WAV file was not created.");
				}
			}
			catch (Exception ex)
			{
				TextToSpeechLogger.LogError((object)("Error in Piper TTS: " + ex.Message));
			}
		}

		private static void PlayAudio(byte[] audioData, AudioSource? targetSource = null, bool playAtPoint = false, Vector3 position = default(Vector3))
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			AudioClip val = WavUtility.ToAudioClip(audioData);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			if ((Object)(object)targetSource != (Object)null)
			{
				targetSource.clip = val;
				if (playAtPoint)
				{
					UtilityMethods.PlayOneShotAtPointWithBypass(targetSource.clip, (position == default(Vector3)) ? ((Component)targetSource).transform.position : position, targetSource);
				}
				else
				{
					targetSource.Play();
				}
			}
			else
			{
				GameObject val2 = new GameObject("PiperTTS");
				AudioSource obj = val2.AddComponent<AudioSource>();
				obj.clip = val;
				obj.Play();
				Object.Destroy((Object)val2, val.length);
			}
		}

		private static void PlayRawAudio(byte[] rawData)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			int num = rawData.Length / 2;
			float[] array = new float[num];
			for (int i = 0; i < num; i++)
			{
				short num2 = BitConverter.ToInt16(rawData, i * 2);
				array[i] = (float)num2 / 32768f;
			}
			AudioClip val = AudioClip.Create("PiperTTS", num, 1, 22050, false);
			val.SetData(array, 0);
			GameObject val2 = new GameObject("PiperTTS");
			AudioSource obj = val2.AddComponent<AudioSource>();
			obj.clip = val;
			obj.Play();
			Object.Destroy((Object)val2, val.length);
		}

		private static void PlayAudio(byte[] audioData)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			AudioClip val = WavUtility.ToAudioClip(audioData);
			if (!((Object)(object)val == (Object)null))
			{
				GameObject val2 = new GameObject("PiperTTS");
				AudioSource obj = val2.AddComponent<AudioSource>();
				obj.clip = val;
				obj.Play();
				Object.Destroy((Object)val2, val.length);
			}
		}

		private void OnDestroy()
		{
			SaveWithRespectToConfigSet();
			_watcher?.Dispose();
		}

		private void SetupWatcher()
		{
			_watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName);
			_watcher.Changed += ReadConfigValues;
			_watcher.Created += ReadConfigValues;
			_watcher.Renamed += ReadConfigValues;
			_watcher.IncludeSubdirectories = true;
			_watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			_watcher.EnableRaisingEvents = true;
		}

		private void ReadConfigValues(object sender, FileSystemEventArgs e)
		{
			DateTime now = DateTime.Now;
			if (now.Ticks - _lastConfigReloadTime.Ticks < 10000000)
			{
				return;
			}
			lock (_reloadLock)
			{
				if (!File.Exists(ConfigFileFullPath))
				{
					TextToSpeechLogger.LogWarning((object)"Config file does not exist. Skipping reload.");
					return;
				}
				try
				{
					TextToSpeechLogger.LogDebug((object)"Reloading configuration...");
					SaveWithRespectToConfigSet(reload: true);
					TextToSpeechLogger.LogInfo((object)"Configuration reload complete.");
				}
				catch (Exception ex)
				{
					TextToSpeechLogger.LogError((object)("Error reloading configuration: " + ex.Message));
				}
			}
			_lastConfigReloadTime = now;
		}

		private void SaveWithRespectToConfigSet(bool reload = false)
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			if (reload)
			{
				((BaseUnityPlugin)this).Config.Reload();
			}
			((BaseUnityPlugin)this).Config.Save();
			if (saveOnConfigSet)
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description)
		{
			return ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, description);
		}

		private ConfigEntry<T> config<T>(string group, string name, T value, string description)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()));
		}

		static TextToSpeechPlugin()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			string configPath = Paths.ConfigPath;
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName;
			ConnectionError = "";
			TextToSpeechLogger = Logger.CreateLogSource("TextToSpeech");
			httpClient = new HttpClient();
			_voiceModelConfigPath = string.Empty;
			_piperExePath = string.Empty;
			_voiceModelPath = string.Empty;
			VoiceModelUrl = null;
			SkipSelf = null;
		}
	}
	public static class ToggleExtensions
	{
		public static bool IsOn(this TextToSpeechPlugin.Toggle toggle)
		{
			return toggle == TextToSpeechPlugin.Toggle.On;
		}

		public static bool IsOff(this TextToSpeechPlugin.Toggle toggle)
		{
			return toggle == TextToSpeechPlugin.Toggle.Off;
		}
	}
	public abstract class UtilityMethods
	{
		public static void GetPlayerAudioSource(out AudioSource? playerSource)
		{
			Player localPlayer = Player.m_localPlayer;
			object obj;
			if (localPlayer == null)
			{
				obj = null;
			}
			else
			{
				Transform obj2 = ((Component)localPlayer).transform.Find("PiperTTS");
				obj = ((obj2 != null) ? ((Component)obj2).GetComponent<AudioSource>() : null);
			}
			playerSource = (AudioSource?)obj;
			if ((Object)(object)playerSource == (Object)null)
			{
				TextToSpeechPlugin.TextToSpeechLogger.LogError((object)"Player AudioSource not found.");
			}
		}

		public static string StripRichText(string input)
		{
			if (!string.IsNullOrEmpty(input))
			{
				return Regex.Replace(input, "<[^>]+>", string.Empty, RegexOptions.IgnoreCase | RegexOptions.Compiled);
			}
			return input;
		}

		public static void FireAndForget(Task task)
		{
			task.ContinueWith(delegate(Task t)
			{
				if (t.Exception != null)
				{
					TextToSpeechPlugin.TextToSpeechLogger.LogError((object)t.Exception);
				}
			}, TaskContinuationOptions.OnlyOnFaulted);
		}

		public static void PlayOneShotAtPointWithBypass(AudioClip clip, Vector3 position, AudioSource? targetSource = null)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			if (!((Object)(object)clip == (Object)null) && !((Object)(object)targetSource == (Object)null))
			{
				GameObject val = new GameObject("TTSOneShotAudioAtPoint");
				val.transform.position = ((position == default(Vector3)) ? ((Component)targetSource).transform.position : position);
				AudioSource obj = val.AddComponent<AudioSource>();
				obj.clip = clip;
				obj.spatialBlend = 1f;
				obj.bypassReverbZones = true;
				obj.bypassListenerEffects = true;
				obj.bypassEffects = true;
				obj.volume = 1f;
				obj.Play();
				Object.Destroy((Object)val, clip.length * (((double)Time.timeScale < 0.009999999776482582) ? 0.01f : Time.timeScale));
			}
		}
	}
	public static class WavUtility
	{
		public static AudioClip? ToAudioClip(byte[] wavFile, string clipName = "wavClip", bool streaming = false)
		{
			if (wavFile == null || wavFile.Length < 44)
			{
				Debug.LogError((object)"Invalid or empty WAV file provided.");
				return null;
			}
			int num = BitConverter.ToInt16(wavFile, 22);
			int num2 = BitConverter.ToInt32(wavFile, 24);
			int num3 = BitConverter.ToInt16(wavFile, 34);
			int i;
			int num4;
			for (i = 12; i + 8 < wavFile.Length; i += 8 + num4)
			{
				string @string = Encoding.ASCII.GetString(wavFile, i, 4);
				num4 = BitConverter.ToInt32(wavFile, i + 4);
				if (@string == "data")
				{
					i += 8;
					break;
				}
			}
			if (i >= wavFile.Length)
			{
				Debug.LogError((object)"Data chunk not found in WAV file.");
				return null;
			}
			int num5 = (wavFile.Length - i) / (num3 / 8);
			int num6 = num5 / num;
			float[] array = new float[num5];
			switch (num3)
			{
			case 16:
			{
				for (int k = 0; k < num5; k++)
				{
					short num7 = BitConverter.ToInt16(wavFile, i + k * 2);
					array[k] = (float)num7 / 32768f;
				}
				break;
			}
			case 8:
			{
				for (int j = 0; j < num5; j++)
				{
					byte b = wavFile[i + j];
					array[j] = (float)(b - 128) / 128f;
				}
				break;
			}
			default:
				Debug.LogError((object)"WavUtility supports only 8-bit and 16-bit audio formats.");
				return null;
			}
			AudioClip obj = AudioClip.Create(clipName, num6, num, num2, streaming);
			obj.SetData(array, 0);
			return obj;
		}
	}
}