public static void PlayerThread() { Bridge.ALog("播放器已启动"); while (!Vars.CallPlayerStop) { if (fileList.Count != 0) { Bridge.ALog("启动播放,剩余数目:" + fileList.Count); var fileName = fileList[0].Filename; try { Play(fileName, true, fileList[0].DoNotDelete); } catch (Exception ex) { Bridge.ALog($"无法读取/播放文件 {Path.GetFileName(fileName)}, 放弃: {ex.Message}"); } if (fileList.Count > 0) { fileList.RemoveAt(0); } } try { Thread.Sleep(100); } catch { } } Bridge.ALog("播放器已停止"); Vars.CallPlayerStop = false; }
public async Task GiftRoute(object sender, ReceivedDanmakuArgs e) { // check user eligibility if (!Conf.CheckUserEligibility(e)) { return; } // check gift eligibility if (!Conf.CheckGiftEligibility(e)) { return; } Bridge.ALog("规则检查通过,准备朗读"); if (Vars.CurrentConf.VoiceReplyFirst) { var hitAnyRule = await TTSPlayer.PlayVoiceReply(e.Danmaku); if (!hitAnyRule || !Vars.CurrentConf.IgnoreIfHitVoiceReply) { await TTSPlayer.UnifiedPlay(ProcessGift(e)); } } else { var hitAnyRule = Vars.CurrentConf.VoiceReplyRules.Any(x => x.Matches(e.Danmaku)); if (!hitAnyRule || !Vars.CurrentConf.IgnoreIfHitVoiceReply) { await TTSPlayer.UnifiedPlay(ProcessGift(e)); } await TTSPlayer.PlayVoiceReply(e.Danmaku); } }
public static string Download(string content, string language) { var errorCount = 0; Retry: try { var fileName = Path.Combine(Vars.CacheDir, Conf.GetRandomFileName() + "GOOG.mp3"); Bridge.ALog("(E2) 正在下载 TTS, 文件名: " + fileName); var instance = new GoogleTTS(content, language); instance.WriteFile(fileName); // validate if file is playable using (var reader = new AudioFileReader(fileName)) { } return(fileName); } catch (Exception ex) { Bridge.ALog("(E2) TTS 下载失败: " + ex.Message); errorCount += 1; Vars.TotalFails++; if (errorCount <= Vars.CurrentConf.DownloadFailRetryCount) { goto Retry; } return(null); } }
public async Task InteractRoute(object sender, ReceivedMessageArgs e) { // check user eligibility if (!Conf.CheckUserEligibility(e)) { return; } Bridge.ALog("规则检查通过,正在朗读用户交互事件"); string result; switch (e.Message.MsgType) { case MsgTypeEnum.UserEnter: result = ProcessInteract(e.Message, Vars.CurrentConf.OnInteractEnter); break; case MsgTypeEnum.LiveShare: result = ProcessInteract(e.Message, Vars.CurrentConf.OnInteractShare); break; default: return; } await TTSPlayer.UnifiedPlay(result); }
public static async Task <string> Download(string content) { { var errorCount = 0; Retry: try { var fileName = Path.Combine(Vars.CacheDir, Conf.GetRandomFileName() + "YODO.mp3"); Bridge.ALog("(E4) 正在下载 TTS, 文件名: " + fileName); using (var downloader = new WebClient()) { downloader.Headers.Add(HttpRequestHeader.AcceptEncoding, "identity;q=1, *;q=0"); downloader.Headers.Add(HttpRequestHeader.Referer, "http://fanyi.youdao.com/"); downloader.Headers.Add(HttpRequestHeader.UserAgent, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36"); await downloader.DownloadFileTaskAsync(Vars.ApiYoudao.Replace("$TTSTEXT", content), fileName); // validate if file is playable using (var reader = new AudioFileReader(fileName)) { } return(fileName); } } catch (Exception ex) { Bridge.ALog("(E4) TTS 下载失败: " + ex.Message); errorCount += 1; Vars.TotalFails++; if (errorCount <= Vars.CurrentConf.DownloadFailRetryCount) { goto Retry; } return(null); } } }
public static async Task WelcomeRoute(object sender, ReceivedDanmakuArgs e) { // check user eligibility if (!Conf.CheckUserEligibility(e)) { return; } Bridge.ALog("规则检查通过,准备朗读"); await TTSPlayer.UnifiedPlay(Vars.CurrentConf.OnWelcome .Replace("$USER", e.Danmaku.UserName) , true ); }
public static async Task GuardBuyRoute(object sender, ReceivedDanmakuArgs e) { // check user eligibility if (!Conf.CheckUserEligibility(e)) { return; } Bridge.ALog("规则检查通过,准备朗读"); await TTSPlayer.UnifiedPlay(Vars.CurrentConf.OnGuardBuy .Replace("$COUNT", e.Danmaku.GiftCount.ToString()) .Replace("$USER", e.Danmaku.UserName) , true ); }
public static async Task <string> Download(string content, SpeechPerson person) { var errorCount = 0; Retry: try { var fileName = Path.Combine(Vars.CacheDir, Conf.GetRandomFileName() + "BDAI.mp3"); using (var downloader = new WebClient()) { if (string.IsNullOrEmpty(Vars.ApiBaiduAiAccessToken)) { Bridge.ALog("(E6) 正在获取 Access token..."); var rawJson = await downloader.DownloadStringTaskAsync( $"https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id={Vars.ApiBaiduAiAppKey}&client_secret={Vars.ApiBaiduAiSecretKey}" ); Vars.ApiBaiduAiAccessToken = JObject.Parse(rawJson)["access_token"].ToString(); Bridge.ALog("(E6) Token 获取成功,本次运行时或 30 天内有效"); } Bridge.ALog("(E6) 正在下载 TTS, 文件名: " + fileName); downloader.Headers.Add(HttpRequestHeader.AcceptEncoding, "identity;q=1, *;q=0"); downloader.Headers.Add(HttpRequestHeader.UserAgent, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36"); var url = Vars.ApiBaiduAi .Replace("$PERSON", ((int)person).ToString()) .Replace("$SPEED", ConvertSpeed(Vars.CurrentConf.ReadSpeed).ToString()) .Replace("$PITCH", ConvertSpeed(Vars.CurrentConf.SpeechPitch).ToString()) .Replace("$TOKEN", Vars.ApiBaiduAiAccessToken) .Replace("$TTSTEXT", content); await downloader.DownloadFileTaskAsync(url, fileName); // validate if file is playable using (var reader = new AudioFileReader(fileName)) { } return(fileName); } } catch (Exception ex) { Bridge.ALog("(E6) TTS 下载失败: " + ex.Message); errorCount += 1; Vars.TotalFails++; if (errorCount <= Vars.CurrentConf.DownloadFailRetryCount) { goto Retry; } return(null); } }
public async Task SuperChatRoute(object sender, ReceivedDanmakuArgs e) { // check user eligibility if (!Conf.CheckUserEligibility(e)) { return; } // check content eligibility if (!Conf.CheckKeywordEligibility(e)) { return; } // check length rule if (!Conf.CheckDanmakuLength(e)) { return; } Bridge.ALog("规则检查通过,准备朗读"); await TTSPlayer.UnifiedPlay(ProcessSuperChat(e), Vars.CurrentConf.SuperChatIgnoreRandomDitch); }
public static async Task <bool> PlayVoiceReply(MessageModel e, VoiceReplyRule rule, bool alwaysMatch = false, bool overrideReadInQueue = false) { if (alwaysMatch || rule.Matches(e)) { if ((VoiceReplyRule.ReplyMode)rule.ReplyingMode != VoiceReplyRule.ReplyMode.VoiceGeneration) { // play specific file: try { if (Vars.CurrentConf.InstantVoiceReply || overrideReadInQueue) { Play(rule.ReplyContent, false, true); } else { // add the file to queue fileList.Add(new TTSEntry(rule.ReplyContent, true)); } } catch (Exception ex) { Bridge.ALog($"无法读出语音答复: {ex.Message}"); } } else { // play by voice generation (and by default) // caution: different types of voice reply have different variables available await UnifiedPlay( Main.ProcessVoiceReply(e, rule), true, Vars.CurrentConf.InstantVoiceReply || overrideReadInQueue ); } return(true); } else { return(false); } }
public async Task InteractRoute(object sender, ReceivedDanmakuArgs e) { // check user eligibility if (!Conf.CheckUserEligibility(e)) { return; } Bridge.ALog("规则检查通过,正在朗读用户交互事件"); string result; switch (e.Danmaku.InteractType) { case InteractTypeEnum.Enter: result = ProcessInteract(e.Danmaku, Vars.CurrentConf.OnInteractEnter); break; case InteractTypeEnum.Follow: result = ProcessInteract(e.Danmaku, Vars.CurrentConf.OnInteractFollow); break; case InteractTypeEnum.MutualFollow: result = ProcessInteract(e.Danmaku, Vars.CurrentConf.OnInteractMutualFollow); break; case InteractTypeEnum.Share: result = ProcessInteract(e.Danmaku, Vars.CurrentConf.OnInteractShare); break; case InteractTypeEnum.SpecialFollow: result = ProcessInteract(e.Danmaku, Vars.CurrentConf.OnInteractSpecialFollow); break; default: return; } await TTSPlayer.UnifiedPlay(result); }
public async Task CommentRoute(object sender, ReceivedMessageArgs e) { // check user eligibility if (!Conf.CheckUserEligibility(e)) { return; } // check content eligibility if (!Conf.CheckKeywordEligibility(e)) { return; } // check length rule if (!Conf.CheckDanmakuLength(e)) { return; } Bridge.ALog("规则检查通过,准备朗读"); if (Vars.CurrentConf.VoiceReplyFirst) { var hitAnyRule = await TTSPlayer.PlayVoiceReply(e.Message); if (!hitAnyRule || !Vars.CurrentConf.IgnoreIfHitVoiceReply) { await TTSPlayer.UnifiedPlay(ProcessDanmaku(e)); } } else { var hitAnyRule = Vars.CurrentConf.VoiceReplyRules.Any(x => x.Matches(e.Message)); if (!hitAnyRule || !Vars.CurrentConf.IgnoreIfHitVoiceReply) { await TTSPlayer.UnifiedPlay(ProcessDanmaku(e)); } await TTSPlayer.PlayVoiceReply(e.Message); } }
/// <summary> /// Play something, other options are defined by current configurations /// </summary> /// <param name="content">Final TTS Content</param> /// <param name="ignoreRandomDitch">Specify true to ignore random ditching</param> public static async Task UnifiedPlay(string content, bool ignoreRandomDitch = false, bool overrideReadInQueue = false) { if (string.IsNullOrWhiteSpace(content)) { Bridge.ALog("放弃: 内容为空"); return; } Bridge.ALog("尝试朗读: " + content); if (!Conf.GetRandomBool(Vars.CurrentConf.ReadPossibility) && !ignoreRandomDitch) { Bridge.ALog("放弃: 已随机丢弃"); return; } string fileName; if (Vars.CurrentConf.EnableUrlEncode) { content = HttpUtility.UrlEncode(content); Bridge.ALog("URL 编码完成: " + content); } switch (Vars.CurrentConf.Engine) { default: fileName = await BaiduTTS.Download(content); break; case 1: if (!Vars.SystemSpeechAvailable) { Bridge.Log(".NET 框架引擎在此系统上不可用,无法朗读"); return; } fileName = FrameworkTTS.Download(content); break; case 2: fileName = GoogleTTS.Download(content, "zh-CN"); break; case 3: fileName = await BaiduTTS.Download(content, true); break; case 4: fileName = await YoudaoTTS.Download(content); break; case 5: fileName = await CustomTTS.Download(content); break; case 6: fileName = await BaiduTTS.AiApi.Download(content, BaiduTTS.AiApi.ParseToSpeechPerson(Vars.CurrentConf.SpeechPerson)); break; } if (fileName == null) { Bridge.ALog("下载失败,丢弃"); return; } if (Vars.CurrentConf.ReadInQueue && !overrideReadInQueue) { Bridge.ALog($"正在添加下列文件到播放列表: {fileName}"); fileList.Add(new TTSEntry(fileName)); } else { Bridge.ALog($"正在直接播放: {fileName}"); Play(fileName, false); } }
public static void Play(string filename, bool wait = true, bool forceKeepCache = false) { var frame = new DispatcherFrame(); var thread = new Thread(() => { try { using (var reader = new AudioFileReader(filename)) { bool exists = false; var targetDevice = Vars.CurrentConf.DeviceGuid; foreach (var dev in DirectSoundOut.Devices) { if (dev.Guid == Vars.CurrentConf.DeviceGuid) { exists = true; break; } } if (!exists) { if (Vars.CurrentConf.AutoFallback) { Bridge.ALog($"设备 {Vars.CurrentConf.DeviceGuid} 不存在,正在自动回落到默认设备。"); targetDevice = Conf.DefaultDeviceGuid; } else { throw new ArgumentOutOfRangeException($"设备 {Vars.CurrentConf.DeviceGuid} 不存在。"); } } using (var waveOut = new DirectSoundOut(targetDevice)) { waveOut.Init(reader); reader.Volume = Volume; Bridge.ALog($"音量设置为: {Volume}"); waveOut.Play(); Vars.TotalPlayed++; if (!wait) { frame.Continue = false; } while (waveOut.PlaybackState != PlaybackState.Stopped) { if (Vars.CallPlayerStop) { waveOut.Stop(); } if (!reader.Volume.IsNearEnough(Volume, 0.02f)) { Bridge.ALog($"同步音量: {Volume}"); reader.Volume = Volume; } Thread.Sleep(50); } } } } catch (Exception ex) { Bridge.ALog($"播放过程中发生错误: {Path.GetFileName(filename)}: {ex.Message}"); } finally { try { if (Vars.CurrentConf.DoNotKeepCache && !forceKeepCache) { Bridge.ALog($"正在删除缓存文件: {filename}"); File.Delete(filename); } } catch (Exception ex) { Bridge.ALog($"无法删除缓存文件 {filename}: {ex.Message}"); } } frame.Continue = false; }); thread.SetApartmentState(ApartmentState.STA); thread.Start(); Dispatcher.PushFrame(frame); }
public void OnReceivedRoomCount(object sender, ReceivedRoomCountArgs e) { Vars.RoomCount = e.UserCount; Bridge.ALog("OnReceivedRoomCount: " + e.UserCount); }
public static async Task <string> Download(string content) { var errorCount = 0; Retry: try { var fileName = Path.Combine(Vars.CacheDir, Conf.GetRandomFileName() + "USER.mp3"); Bridge.ALog($"(E5) 正在下载 TTS, 文件名: {fileName}, 方法: {Vars.CurrentConf.ReqType}"); if (Vars.CurrentConf.ReqType == RequestType.ApplicationXWwwFormUrlencoded || Vars.CurrentConf.ReqType == RequestType.TextPlain) { Bridge.ALog("POST 模式: text/plain / application/x-www-form-urlencoded"); // 配置 Headers var uploader = WebRequest.CreateHttp(Vars.CurrentConf.CustomEngineURL); uploader.Method = "POST"; if (Vars.CurrentConf.HttpAuth) { uploader.Credentials = new NetworkCredential(Vars.CurrentConf.HttpAuthUsername, Vars.CurrentConf.HttpAuthPassword); uploader.PreAuthenticate = true; } foreach (var header in Vars.CurrentConf.Headers) { Bridge.ALog($"添加 Header: {header.Name}, 值 {header.Value}"); uploader.Headers.Add(header.Name, header.Value); } uploader.ContentType = Vars.CurrentConf.ReqType == RequestType.ApplicationXWwwFormUrlencoded ? "application/x-www-form-urlencoded" : "text/plain"; // 准备数据 var data = Encoding.UTF8.GetBytes(Vars.CurrentConf.PostData); var dataStream = uploader.GetRequestStream(); dataStream.Write(data, 0, data.Length); dataStream.Close(); uploader.ContentLength = data.Length; // 等响应 using (var res = uploader.GetResponse()) { // 写数据 using (var writer = new StreamWriter(File.OpenWrite(fileName)) { AutoFlush = true }) { byte[] responseData = new byte[res.ContentLength]; res.GetResponseStream().Read(responseData, 0, responseData.Length); writer.Write(responseData); } } } else if (Vars.CurrentConf.ReqType == RequestType.MultipartFormData) { using (var httpClient = new HttpClient()) { var form = new MultipartFormDataContent(); // 配置 Headers if (Vars.CurrentConf.HttpAuth) { var authArray = Encoding.UTF8.GetBytes($"{Vars.CurrentConf.HttpAuthUsername}:{Vars.CurrentConf.HttpAuthPassword}"); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authArray)); } foreach (var header in Vars.CurrentConf.Headers) { Bridge.ALog($"添加 Header: {header.Name}, 值 {header.Value}"); httpClient.DefaultRequestHeaders.Add(header.Name, header.Value); } // 转换数据 var postItems = JsonConvert.DeserializeObject <List <Header> >(Vars.CurrentConf.PostData); foreach (var item in postItems) { var data = Convert.FromBase64String(item.Value); form.Add(new ByteArrayContent(data, 0, data.Length), item.Name); } // 抓结果 using (var res = await httpClient.PostAsync(Vars.CurrentConf.CustomEngineURL, form)) { res.EnsureSuccessStatusCode(); // 写数据 using (var writer = new StreamWriter(File.OpenWrite(fileName)) { AutoFlush = true }) { var responseData = await res.Content.ReadAsByteArrayAsync(); writer.Write(responseData); } } } } else { Bridge.ALog("GET 模式"); using (var downloader = new WebClient()) { downloader.Headers.Add(HttpRequestHeader.UserAgent, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36"); foreach (var header in Vars.CurrentConf.Headers) { Bridge.ALog($"添加 Header: {header.Name}, 值 {header.Value}"); downloader.Headers.Add(header.Name, header.Value); } if (Vars.CurrentConf.HttpAuth) { downloader.Credentials = new NetworkCredential(Vars.CurrentConf.HttpAuthUsername, Vars.CurrentConf.HttpAuthPassword); } await downloader.DownloadFileTaskAsync(Vars.CurrentConf.CustomEngineURL.Replace("$TTSTEXT", content), fileName); } } // validate if file is playable using (var reader = new AudioFileReader(fileName)) { } return(fileName); } catch (Exception ex) { Bridge.ALog($"(E5) TTS 下载失败: {ex.Message}"); errorCount += 1; Vars.TotalFails++; if (errorCount <= Vars.CurrentConf.DownloadFailRetryCount) { goto Retry; } return(null); } }