using Codeer.Friendly; using Codeer.Friendly.Dynamic; using Codeer.Friendly.Windows; using Codeer.Friendly.Windows.Grasp; using Codeer.Friendly.Windows.NativeStandardControls; using Ong.Friendly.FormsStandardControls; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ScDriver.AItalk3 { public class ScDeviceDriver : ScBaseDriver, IScBaseDriver { private const string DrvName = "AItalk3.Driver@echoseika.hgotoh.jp"; private const string DrvVersion = "20210327/c"; private const string DrvProdName = "AITalk3"; private const int DrvTextMaxLength = 0; private const int CidBase = 6000; private const int MaxAvators = 10; private const int SplitLine = 6; private Process AItalk3Process = null; private SemaphoreSlim Semaphore = new SemaphoreSlim(1, 1); private WindowsAppFriend _app = null; private WindowControl uiTreeTop = null; private FormsComboBox AvatorListx = null; class titles { public string Avator { get; private set; } public int FixedCid { get; private set; } public int AvatorIndex { get; private set; } public titles(string avator, int cid) { Avator = avator; FixedCid = cid; AvatorIndex = 0; } } private readonly titles[] AITalk2Names = { new titles( "あんず", CidBase + 1), new titles( "かほ", CidBase + 2), new titles( "ななこ", CidBase + 3), new titles( "のぞみ", CidBase + 4), new titles( "せいじ", CidBase + 5) // new titles( "みやび", CidBase + 6), // new titles( "やまと", CidBase + 7) }; public ScDeviceDriver() { ScDrvName = DrvName; ScDrvVersion = DrvVersion; ScDrvProdName = DrvProdName; ScDrvTextMaxLength = DrvTextMaxLength; CidBaseIndex = CidBase; AvatorParams = new Dictionary(); IsAlive = false; AItalk3Process = GetAItalk3Process(); if (AItalk3Process != null) { // 認識対象外、もしくはユーザ定義プリセットに割り当てるcidのカウンター int cbaseCounter = 1 + AITalk2Names.Select(v => v.FixedCid).Max(); if (cbaseCounter < (CidBase + SplitLine)) cbaseCounter = CidBase + SplitLine; try { _app = new WindowsAppFriend(AItalk3Process); uiTreeTop = WindowControl.FromZTop(_app); // 音声効果タブ選択 FormsTabControl ci = new FormsTabControl(uiTreeTop.IdentifyFromZIndex(2, 0, 0, 1, 0, 0, 1)); ci.EmulateTabSelect(0); // 音声効果 Thread.Sleep(10); // リストボックスから話者一覧を取得して処理 titles ent = null; AvatorListx = new FormsComboBox(uiTreeTop.IdentifyFromZIndex(2, 0, 0, 1, 0, 0, 0, 0, 0)); for (int avatorIdx = 0; avatorIdx < AvatorListx.ItemCount; avatorIdx++) { if (cbaseCounter >= (CidBase + MaxAvators)) break; AvatorListx.EmulateChangeSelect(avatorIdx); AItalk3.AvatorParam avator = new AItalk3.AvatorParam(); avator.AvatorIndex = avatorIdx; avator.IndexOfUserList = avatorIdx; avator.AvatorUI = new AItalk3.AvatorUIParam(); avator.AvatorUI.TalkTextBox = new WindowControl(uiTreeTop.Dynamic().ay); avator.AvatorUI.PlayButton = new FormsButton(uiTreeTop.Dynamic().a2); avator.AvatorUI.SaveButton = new FormsButton(uiTreeTop.Dynamic().a4); avator.AvatorUI.VolumeText = new FormsTextBox(uiTreeTop.Dynamic().cs); avator.AvatorUI.SpeedText = new FormsTextBox(uiTreeTop.Dynamic().cr); avator.AvatorUI.PitchText = new FormsTextBox(uiTreeTop.Dynamic().cq); avator.AvatorUI.IntonationText = new FormsTextBox(uiTreeTop.Dynamic().cp); avator.AvatorName = AvatorListx.Dynamic().SelectedItem.ItemText; // 認識している話者名ならば固定のcidを設定 try { ent = AITalk2Names.Where(v => v.Avator == avator.AvatorName).First(); avator.FixedCid = ent.FixedCid; } catch (Exception) { avator.FixedCid = cbaseCounter; cbaseCounter++; } // このタイミングで一旦登録する AvatorParams.Add(avator.FixedCid, avator); avator.VoiceEffects_default = new Dictionary { { EnumVoiceEffect.volume, new EffectValueInfo(GetSliderValue(avator.FixedCid, EnumVoiceEffect.volume), 0.0m, 2.0m, 0.01m)}, { EnumVoiceEffect.speed, new EffectValueInfo(GetSliderValue(avator.FixedCid, EnumVoiceEffect.speed), 0.5m, 4.0m, 0.01m)}, { EnumVoiceEffect.pitch, new EffectValueInfo(GetSliderValue(avator.FixedCid, EnumVoiceEffect.pitch), 0.5m, 2.0m, 0.01m)}, { EnumVoiceEffect.intonation, new EffectValueInfo(GetSliderValue(avator.FixedCid, EnumVoiceEffect.intonation), 0.0m, 2.0m, 0.01m)} }; avator.VoiceEffects = new Dictionary { { EnumVoiceEffect.volume, new EffectValueInfo(GetSliderValue(avator.FixedCid, EnumVoiceEffect.volume), 0.0m, 2.0m, 0.01m)}, { EnumVoiceEffect.speed, new EffectValueInfo(GetSliderValue(avator.FixedCid, EnumVoiceEffect.speed), 0.5m, 4.0m, 0.01m)}, { EnumVoiceEffect.pitch, new EffectValueInfo(GetSliderValue(avator.FixedCid, EnumVoiceEffect.pitch), 0.5m, 2.0m, 0.01m)}, { EnumVoiceEffect.intonation, new EffectValueInfo(GetSliderValue(avator.FixedCid, EnumVoiceEffect.intonation), 0.0m, 2.0m, 0.01m)} }; avator.VoiceEmotions_default = new Dictionary(); avator.VoiceEmotions = new Dictionary(); } } catch (Exception e) { ThrowException(string.Format(@"{0} {1}", e.Message, e.StackTrace)); } } IsAlive = AvatorParams.Count != 0; // cidエイリアス用 if (IsAlive) { AItalk3.AvatorParam avator = new AItalk3.AvatorParam(); int firsFindtCid = AvatorParams.First().Value.FixedCid; avator.FixedCid = CidBase; avator.AvatorIndex = AvatorParams.Count; avator.AvatorName = AvatorParams[firsFindtCid].AvatorName; avator.AliasCode = true; avator.IndexOfUserList = (AvatorParams[firsFindtCid] as AItalk3.AvatorParam).IndexOfUserList; avator.AvatorUI = (AvatorParams[firsFindtCid] as AItalk3.AvatorParam).AvatorUI; avator.VoiceEffects = (AvatorParams[firsFindtCid] as AItalk3.AvatorParam).VoiceEffects; avator.VoiceEmotions = (AvatorParams[firsFindtCid] as AItalk3.AvatorParam).VoiceEmotions; avator.VoiceEffects_default = (AvatorParams[firsFindtCid] as AItalk3.AvatorParam).VoiceEffects_default; avator.VoiceEmotions_default = (AvatorParams[firsFindtCid] as AItalk3.AvatorParam).VoiceEmotions_default; AvatorParams.Add(avator.FixedCid, avator); } } private Process GetAItalk3Process() { string WinTitle1 = "かんたん! AITalk 3"; string WinTitle2 = WinTitle1 + "*"; int RetryCount = 3; int RetryWaitms = 500; Process p = null; for (int i = 0; i < 3; i++) { Process[] ps = Process.GetProcesses(); foreach (Process pitem in ps) { if ((pitem.MainWindowHandle != IntPtr.Zero) && ((pitem.MainWindowTitle.Equals(WinTitle1)) || (pitem.MainWindowTitle.Equals(WinTitle2)))) { p = pitem; break; } } if (p != null) break; if (i < (RetryCount - 1)) Thread.Sleep(RetryWaitms); } return p; } public void Dispose() { Dispose(true); } /// /// 指定話者で指定テキストで発声 /// /// 話者CID /// 発声させるテキスト /// 発声にかかった時間(ミリ秒) public override double Play(int cid, string talkText) { Stopwatch stopWatch = new Stopwatch(); int avatorIdx = ConvertAvatorIndex(cid); AItalk3.AvatorParam avator = AvatorParams[avatorIdx] as AItalk3.AvatorParam; Semaphore.Wait(); if (avator.AvatorUI.PlayButton == null) return 0.0; if (avator.AvatorUI.SaveButton == null) return 0.0; if (avator.AvatorUI.TalkTextBox == null) return 0.0; dynamic AItalk3UiTab = new FormsTabControl(uiTreeTop.IdentifyFromZIndex(2, 0, 0, 1, 0, 0, 1)); AItalk3UiTab.EmulateTabSelect(0); AvatorListx.EmulateChangeSelect(avator.IndexOfUserList); ApplyEffectParameters(avator.FixedCid); ApplyEmotionParameters(avator.FixedCid); // 再生中なので再生終了を待つ(音声保存ボタンがEnableになるのを待つ) if (!avator.AvatorUI.SaveButton.Enabled) { while (!avator.AvatorUI.SaveButton.Enabled) { Thread.Sleep(10); } } avator.AvatorUI.TalkTextBox["Text"](talkText); Thread.Sleep(10); stopWatch.Start(); avator.AvatorUI.PlayButton.EmulateClick(); // 再生開始を待つ(音声保存ボタンがDisableになるのを待つ) if (avator.AvatorUI.SaveButton.Enabled) { while (avator.AvatorUI.SaveButton.Enabled) { Thread.Sleep(10); } } // 再生終了を待つ(音声保存ボタンがEnableになるのを待つ) if (!avator.AvatorUI.SaveButton.Enabled) { while (!avator.AvatorUI.SaveButton.Enabled) { Thread.Sleep(10); } } stopWatch.Stop(); Semaphore.Release(); return stopWatch.ElapsedMilliseconds; } /// /// 指定話者で指定テキストで発声 /// /// 話者CID /// 発声させるテキスト /// 発声にかかった時間(ミリ秒) public override double Play(int cid, string[] talkTexts) { string s = String.Join("", talkTexts); return Play(cid, s); } /// /// 指定話者で指定テキストで発声 /// /// 話者CID /// 発声させるテキスト public override void PlayAsync(int cid, string talkText) { int avatorIdx = ConvertAvatorIndex(cid); AItalk3.AvatorParam avator = AvatorParams[avatorIdx] as AItalk3.AvatorParam; Task.Run(() => { Semaphore.Wait(); if (avator.AvatorUI.PlayButton == null) return; if (avator.AvatorUI.SaveButton == null) return; if (avator.AvatorUI.TalkTextBox == null) return; dynamic AItalk3UiTabUiTab = new FormsTabControl(uiTreeTop.IdentifyFromZIndex(2, 0, 0, 1, 0, 0, 1)); AItalk3UiTabUiTab.EmulateTabSelect(0); AvatorListx.EmulateChangeSelect(avator.IndexOfUserList); ApplyEffectParameters(avator.FixedCid); ApplyEmotionParameters(avator.FixedCid); // 再生中なので再生終了を待つ(音声保存ボタンがEnableになるのを待つ) if (!avator.AvatorUI.SaveButton.Enabled) { while (!avator.AvatorUI.SaveButton.Enabled) { Thread.Sleep(10); } } avator.AvatorUI.TalkTextBox["Text"](talkText); Thread.Sleep(10); avator.AvatorUI.PlayButton.EmulateClick(); // 再生開始を待つ(音声保存ボタンがDisableになるのを待つ) if (avator.AvatorUI.SaveButton.Enabled) { while (avator.AvatorUI.SaveButton.Enabled) { Thread.Sleep(10); } } // 再生終了を待つ(音声保存ボタンがEnableになるのを待つ) if (!avator.AvatorUI.SaveButton.Enabled) { while (!avator.AvatorUI.SaveButton.Enabled) { Thread.Sleep(10); } } Semaphore.Release(); }); } /// /// 指定話者で指定テキストで発声 /// /// 話者CID /// 発声させるテキスト public override void PlayAsync(int cid, string[] talkTexts) { string s = String.Join("", talkTexts); PlayAsync(cid, s); } /// /// 指定話者で指定テキストで発声した結果をファイルに保存 /// /// 話者CID /// 発声させるテキスト /// 保存先ファイル名 /// 0.0ミリ秒固定 public override double Save(int cid, string talkText, string saveFilename) { int avatorIdx = ConvertAvatorIndex(cid); AItalk3.AvatorParam avator = AvatorParams[avatorIdx] as AItalk3.AvatorParam; if (avator.AvatorUI.PlayButton == null) return 0.0; if (avator.AvatorUI.SaveButton == null) return 0.0; if (avator.AvatorUI.TalkTextBox == null) return 0.0; dynamic AItalk3UiTabUiTab = new FormsTabControl(uiTreeTop.IdentifyFromZIndex(2, 0, 0, 1, 0, 0, 1)); AItalk3UiTabUiTab.EmulateTabSelect(0); AvatorListx.EmulateChangeSelect(avator.IndexOfUserList); ApplyEffectParameters(avator.FixedCid); ApplyEmotionParameters(avator.FixedCid); if (!avator.AvatorUI.SaveButton.Enabled) { while (!avator.AvatorUI.SaveButton.Enabled) { Thread.Sleep(10); } } avator.AvatorUI.TalkTextBox["Text"](talkText); Thread.Sleep(10); avator.AvatorUI.SaveButton.EmulateClick(new Async()); bool finish_savefileSetup = false; while (finish_savefileSetup == false) { //名前を付けて保存 ダイアログで名前を設定 var FileDlgs = WindowControl.GetFromWindowText(_app, "音声ファイルの保存"); try { if ((FileDlgs.Length != 0) && (FileDlgs[0].WindowClassName == "#32770")) { // https://github.com/mikoto2000/TTSController UI特定の記述を参照 NativeButton OkButton = new NativeButton(FileDlgs[0].IdentifyFromDialogId(1)); NativeEdit SaveNameText = new NativeEdit(FileDlgs[0].IdentifyFromZIndex(11, 0, 4, 0, 0)); //ファイル名を設定 SaveNameText.EmulateChangeText(saveFilename); Thread.Sleep(100); //OKボタンを押す OkButton.EmulateClick(new Async()); Thread.Sleep(100); // 上書き確認ダイアログがある? var FileDlgs2 = WindowControl.GetFromWindowText(_app, "音声ファイルの保存"); if ((FileDlgs2.Length != 0) && (FileDlgs2[0].WindowClassName == "#32770")) { // https://github.com/mikoto2000/TTSController UI特定の記述を参照 NativeButton YesButton = new NativeButton(FileDlgs2[0].IdentifyFromDialogId(6)); // YES button //YESボタンを押す YesButton.EmulateClick(new Async()); Thread.Sleep(10); } finish_savefileSetup = true; } } catch (Exception) { // } Thread.Sleep(10); } return 0.0; } /// /// 指定話者で指定テキストで発声した結果をファイルに保存 /// /// 話者CID /// 発声させるテキスト /// 保存先ファイル名 /// 0.0ミリ秒固定 public override double Save(int cid, string[] talkTexts, string saveFilename) { string s = String.Join("", talkTexts); return Save(cid, s, saveFilename); } /// /// 感情パラメタをデフォルト値に戻す /// /// 話者CID public override void ResetVoiceEmotion(int cid) { int avatorIdx = ConvertAvatorIndex(cid); AvatorParam avator = AvatorParams[avatorIdx] as AvatorParam; foreach (var emotion in avator.VoiceEmotions_default) { avator.VoiceEmotions[emotion.Key].value = emotion.Value.value; } ApplyEmotionParameters(avatorIdx); } /// /// 音声効果をデフォルト値に戻す /// /// 話者CID public override void ResetVoiceEffect(int cid) { int avatorIdx = ConvertAvatorIndex(cid); AvatorParam avator = AvatorParams[avatorIdx] as AvatorParam; foreach (var effect in avator.VoiceEffects_default) { avator.VoiceEffects[effect.Key].value = effect.Value.value; } ApplyEffectParameters(avatorIdx); } /// /// 話者切り替え /// /// 話者CID public override void SetAvator(int cid) { int avatorIdx = ConvertAvatorIndex(cid); AvatorParam avator = AvatorParams[avatorIdx] as AvatorParam; AvatorListx.EmulateChangeSelect(avator.IndexOfUserList); } public override void Dispose(bool disposing) { if (Disposed) return; if (disposing) { foreach (var item in AvatorParams) { ResetVoiceEffect(item.Key + CidBase); ResetVoiceEmotion(item.Key + CidBase); } _app.Dispose(); AvatorParams.Clear(); //throw new NotImplementedException(); } Disposed = true; } private void ApplyEmotionParameters(int avatorIndex) { // } private void ApplyEffectParameters(int avatorIdx) { FormsTextBox TargetTextBox = null; double value = 0.00; AItalk3.AvatorParam avator = AvatorParams[avatorIdx] as AItalk3.AvatorParam; foreach (var effect in avator.VoiceEffects) { switch (effect.Key) { case EnumVoiceEffect.volume: TargetTextBox = avator.AvatorUI.VolumeText; break; case EnumVoiceEffect.speed: TargetTextBox = avator.AvatorUI.SpeedText; break; case EnumVoiceEffect.pitch: TargetTextBox = avator.AvatorUI.PitchText; break; case EnumVoiceEffect.intonation: TargetTextBox = avator.AvatorUI.IntonationText; break; } value = Convert.ToDouble(avator.VoiceEffects[effect.Key].value); if (TargetTextBox != null) { TargetTextBox.EmulateChangeText(string.Format("{0:0.00}", value)); } } } private decimal GetSliderValue(int avatorIdx, EnumVoiceEffect effect) { decimal value = 0.00m; FormsTextBox TargetTextBox = null; AItalk3.AvatorParam avator = AvatorParams[avatorIdx] as AItalk3.AvatorParam; switch (effect) { case EnumVoiceEffect.volume: TargetTextBox = avator.AvatorUI.VolumeText; break; case EnumVoiceEffect.speed: TargetTextBox = avator.AvatorUI.SpeedText; break; case EnumVoiceEffect.pitch: TargetTextBox = avator.AvatorUI.PitchText; break; case EnumVoiceEffect.intonation: TargetTextBox = avator.AvatorUI.IntonationText; break; } if (TargetTextBox != null) { value = Convert.ToDecimal(TargetTextBox.Text); } return value; } } }