/// <summary>
            /// トークテキスト設定の実処理を行う。
            /// </summary>
            /// <param name="text">設定するトークテキスト。</param>
            /// <returns>成功したならば true 。そうでなければ false 。</returns>
            protected override async Task <bool> DoSetTalkText(string text)
            {
                var edit = this.TalkEdit;

                if (edit == null)
                {
                    return(false);
                }

                // 500文字あたり1ミリ秒をタイムアウト値に追加
                var timeout = UIControlTimeout + (text.Length / 500);

                try
                {
                    if (!(await Task.Run(() => edit.SetText(text, timeout))))
                    {
                        ThreadTrace.WriteLine(
                            @"トークテキスト設定処理がタイムアウトしました。 " +
                            nameof(text) + @".Length=" + text.Length);
                        return(false);
                    }
                }
                catch (Exception ex)
                {
                    ThreadTrace.WriteException(ex);
                    return(false);
                }

                return(true);
            }
            /// <summary>
            /// WAVEファイルパスをファイル名エディットへ設定する処理を行う。
            /// </summary>
            /// <param name="fileNameEdit">ファイル名エディット。</param>
            /// <param name="filePath">WAVEファイルパス。</param>
            /// <returns>成功したならば true 。そうでなければ false 。</returns>
            private async Task <bool> DoSetFilePathToEditTask(
                AutomationElement fileNameEdit,
                string filePath)
            {
                if (fileNameEdit == null || string.IsNullOrWhiteSpace(filePath))
                {
                    return(false);
                }

                // 入力可能状態まで待機
                if (!(await this.WhenForInputIdle()))
                {
                    ThreadTrace.WriteLine(@"入力可能状態になりません。");
                    return(false);
                }

                // フォーカス
                try
                {
                    fileNameEdit.SetFocus();
                }
                catch (Exception ex)
                {
                    ThreadTrace.WriteException(ex);
                    return(false);
                }

                // VOICEROID2ライクソフトウェアでは、
                // Windowsのフォルダーオプションで拡張子を表示しない設定にしていると、
                // ValuePattern.SetValue によるファイルパス設定が無視され、
                // 元々入力されている前回保存時の名前が参照されてしまう。
                // 一旦キー入力を行うことで回避できるようなので、適当な文字を送ってみる。

                // 適当な文字をファイル名エディットへ送信
                // 失敗しても先へ進む
                try
                {
                    var editWin =
                        new Win32Window(new IntPtr(fileNameEdit.Current.NativeWindowHandle));
                    editWin.SendMessage(
                        WM_CHAR,
                        new IntPtr('x'),
                        IntPtr.Zero,
                        UIControlTimeout);
                }
                catch { }

                // ファイルパス設定
                if (!SetElementValue(fileNameEdit, filePath))
                {
                    ThreadTrace.WriteLine(@"ファイルパスを設定できません。");
                    return(false);
                }

                return(true);
            }
Beispiel #3
0
            DoFindFileDialogElements(AutomationElement fileDialog)
            {
                // 入力可能状態まで待機
                if (!(await this.WhenForInputIdle()))
                {
                    ThreadTrace.WriteLine(@"入力可能状態になりません。");
                    return(null);
                }

                // OKボタン AutomationElement 検索
                var okButton =
                    await RepeatUntil(
                        () => FindFirstChildByAutomationId(fileDialog, @"1"),
                        elem => elem != null,
                        50);

                if (okButton == null)
                {
                    return(null);
                }

                // ファイル名エディットホスト AutomationElement 検索
                var editHost =
                    await RepeatUntil(
                        () =>
                        fileDialog.FindFirst(
                            TreeScope.Descendants,
                            new PropertyCondition(
                                AutomationElement.AutomationIdProperty,
                                @"FileNameControlHost")),
                        elem => elem != null,
                        50);

                if (editHost == null)
                {
                    return(null);
                }

                // ファイル名エディット AutomationElement 検索
                var edit =
                    await RepeatUntil(
                        () =>
                        FindFirstChild(
                            editHost,
                            AutomationElement.ClassNameProperty,
                            @"Edit"),
                        elem => elem != null,
                        50);

                if (edit == null)
                {
                    return(null);
                }

                return(Tuple.Create(okButton, edit));
            }
        /// <summary>
        /// 現在の <see cref="State"/> では処理を行えないことを示すメッセージを作成する。
        /// </summary>
        /// <returns>エラーメッセージ。アイドル状態である場合は null 。</returns>
        protected string MakeStateErrorMessage()
        {
            var state = this.State;

            if (state == TalkerState.Idle)
            {
                return(null);
            }

            var stateMessage = this.StateMessage;

            if (stateMessage != null)
            {
                return(stateMessage);
            }

            switch (state)
            {
            case TalkerState.None:
                return(@"本体が起動していません。");

            case TalkerState.Fail:
                return(@"不正な状態です。");

            case TalkerState.Startup:
                return(@"本体が起動完了していません。");

            case TalkerState.Cleanup:
                return(@"本体が終了処理中です。");

            case TalkerState.Speaking:
                return(@"トーク中は処理できません。");

            case TalkerState.Blocking:
                return(@"本体が処理できない状態です。");

            case TalkerState.FileSaving:
                return(@"本体の音声保存中は処理できません。");

            case TalkerState.Idle:
                return(null);

            default:
                break;
            }

            ThreadTrace.WriteLine($@"Invalid talker state. ({(int)this.State})");
            return(@"不正な状態です。");
        }
            /// <summary>
            /// WAVEファイルの保存確認処理を行う。
            /// </summary>
            /// <param name="filePath">WAVEファイルパス。</param>
            /// <returns>保存されているならば true 。そうでなければ false 。</returns>
            private async Task <bool> DoCheckFileSavedTask(string filePath)
            {
                if (string.IsNullOrWhiteSpace(filePath))
                {
                    return(false);
                }

                // ファイル保存完了 or ダイアログ表示 を待つ
                // ファイル保存完了なら null を返す
                var dialogs =
                    await RepeatUntil(
                        async() =>
                        File.Exists(filePath)?null : (await this.FindDialogs()),
                        dlgs => (dlgs == null || dlgs.Count > 0),
                        150);

                if (dialogs != null)
                {
                    // 保存進捗ダイアログ以外のダイアログが出ているなら失敗
                    if (!dialogs.ContainsKey(DialogType.SaveProgress))
                    {
                        ThreadTrace.WriteLine(
                            @"保存進捗ダイアログ以外のダイアログが開いています。 dialogs=" +
                            string.Join(
                                @",",
                                dialogs.Where(v => v.Value != null).Select(v => v.Key)));
                        return(false);
                    }

                    // 保存進捗ダイアログが閉じるまで待つ
                    await RepeatUntil(
                        () => this.FindDialog(DialogType.SaveProgress),
                        (Win32Window d) => d == null);

                    // 改めてファイル保存完了チェック
                    if (!(await RepeatUntil(() => File.Exists(filePath), f => f, 25)))
                    {
                        return(false);
                    }
                }

                // 同時にテキストファイルが保存される場合があるため少し待つ
                // 保存されていなくても失敗にはしない
                var txtPath = Path.ChangeExtension(filePath, @".txt");

                await RepeatUntil(() => File.Exists(txtPath), f => f, 10);

                return(true);
            }
            /// <summary>
            /// ファイル名エディットコントロールの入力内容確定処理を行う。
            /// </summary>
            /// <param name="okButton">ファイルダイアログのOKボタン。</param>
            /// <param name="fileDialogParent">ファイルダイアログの親。</param>
            /// <returns>成功したならば true 。そうでなければ false 。</returns>
            private async Task <bool> DoDecideFilePathTask(
                AutomationElement okButton,
                AutomationElement fileDialogParent)
            {
                if (okButton == null || fileDialogParent == null)
                {
                    return(false);
                }

                // 入力可能状態まで待機
                if (!(await this.WhenForInputIdle()))
                {
                    ThreadTrace.WriteLine(@"入力可能状態になりません。");
                    return(false);
                }

                // フォーカス
                try
                {
                    okButton.SetFocus();
                }
                catch (Exception ex)
                {
                    ThreadTrace.WriteException(ex);
                    return(false);
                }

                // OKボタン押下
                if (!InvokeElement(okButton))
                {
                    ThreadTrace.WriteLine(@"OKボタンを押下できません。");
                    return(false);
                }

                // ファイルダイアログが閉じるまで待つ
                try
                {
                    var closed =
                        await RepeatUntil(
                            () =>
                            !FindChildWindows(
                                fileDialogParent,
                                SaveFileDialogName)
                            .Any(),
                            f => f,
                            150);

                    if (!closed)
                    {
                        ThreadTrace.WriteLine(@"ファイルダイアログの終了を確認できません。");
                        return(false);
                    }
                }
                catch (Exception ex)
                {
                    ThreadTrace.WriteException(ex);
                    return(false);
                }

                return(true);
            }
            /// <summary>
            /// 音声保存オプションウィンドウのOKボタンを押下し、ファイルダイアログを取得する。
            /// </summary>
            /// <param name="dialog">音声保存オプションウィンドウ。</param>
            /// <returns>ファイルダイアログ。見つからなければ null 。</returns>
            private async Task <AutomationElement> DoPushOkButtonOfSaveOptionDialogTask(
                AutomationElement optionDialog)
            {
                if (optionDialog == null)
                {
                    throw new ArgumentNullException(nameof(optionDialog));
                }

                // 入力可能状態まで待機
                if (!(await this.WhenForInputIdle()))
                {
                    ThreadTrace.WriteLine(@"入力可能状態になりません。");
                    return(null);
                }

                // OKボタン検索
                var okButton =
                    await RepeatUntil(
                        () =>
                        FindFirstChild(
                            optionDialog,
                            AutomationElement.NameProperty,
                            @"OK"),
                        elem => elem != null,
                        50);

                if (okButton == null)
                {
                    ThreadTrace.WriteLine(@"OKボタンが見つかりません。");
                    return(null);
                }

                AutomationElement fileDialog = null;

                try
                {
                    // OKボタン押下
                    if (!InvokeElement(okButton))
                    {
                        ThreadTrace.WriteLine(@"OKボタンを押下できません。");
                        return(null);
                    }

                    // ファイルダイアログ検索
                    fileDialog =
                        await RepeatUntil(
                            () =>
                            FindFirstChildByControlType(
                                optionDialog,
                                ControlType.Window),
                            elem => elem != null,
                            150);

                    if (fileDialog?.Current.Name != SaveFileDialogName)
                    {
                        ThreadTrace.WriteLine(@"ファイルダイアログが見つかりません。");
                        return(null);
                    }
                }
                catch (Exception ex)
                {
                    ThreadTrace.WriteException(ex);
                    return(null);
                }

                return(fileDialog);
            }
Beispiel #8
0
        /// <summary>
        /// セリフデータグリッドのコンテキストメニューを初期化して取得する。
        /// </summary>
        /// <param name="dataGrid">セリフデータグリッド。</param>
        /// <returns>コンテキストメニュー。</returns>
        private static dynamic InitializeContextMenu(WpfDataGrid dataGrid)
        {
            var menu = dataGrid.Base.ContextMenu;

            if (menu == null)
            {
                ThreadTrace.WriteLine(@"speechDataGrid.ContextMenu is null.");
                return(null);
            }

            foreach (var item in menu.Items)
            {
                // メニュー項目名取得
                // Separator の場合があるので例外は握り潰す
                string header;
                try
                {
                    header = (string)item.Header;
                }
                catch
                {
                    continue;
                }

                // メニュー項目に null があるなら要初期化
                if (header == null)
                {
                    bool initOk = false;

                    // うまくいかないことがあるので何度か試す
                    for (int retry = 0; retry <= ContextMenuInitRetryCount; ++retry)
                    {
                        // 初期化
                        try
                        {
                            dataGrid.Focus();
                            menu.PlacementTarget = dataGrid.Base;
                            try
                            {
                                menu.IsOpen = true;
                            }
                            finally
                            {
                                menu.IsOpen = false;
                            }
                        }
                        finally
                        {
                            menu.PlacementTarget = null;
                        }

                        // 非 null になったか確認
                        if ((string)item.Header != null)
                        {
                            initOk = true;
#if DEBUG
                            if (retry > 0)
                            {
                                ThreadDebug.WriteLine(
                                    @"Retry count of initializing ContextMenu : " + retry);
                            }
#endif // DEBUG
                            break;
                        }

                        Thread.Yield();
                    }

                    if (!initOk)
                    {
                        ThreadTrace.WriteLine(@"item.Header is already null.");
                        return(null);
                    }

                    break;
                }
            }

            return(menu);
        }
            /// <summary>
            /// 保存ダイアログアイテムセットを検索する処理を行う。
            /// </summary>
            /// <param name="uiAutomationEnabled">
            /// UI Automation の利用を許可するならば true 。
            /// </param>
            /// <returns>
            /// 保存ダイアログアイテムセット。
            /// ファイル名エディットが見つからなければ null 。
            /// </returns>
            private async Task <SaveDialogItemSet> DoFindSaveDialogItemSetTask(
                bool uiAutomationEnabled)
            {
                // いずれかのダイアログが出るまで待つ
                var dialogs =
                    await RepeatUntil(this.FindDialogs, dlgs => dlgs.Count > 0, 150);

                if (dialogs.Count <= 0)
                {
                    ThreadTrace.WriteLine(@"ダイアログ検索処理がタイムアウトしました。");
                    return(null);
                }

                // 保存ダイアログ取得
                Win32Window dialog = null;

                if (!dialogs.TryGetValue(DialogType.Save, out dialog))
                {
                    ThreadTrace.WriteLine(@"音声保存ダイアログが見つかりません。");
                    return(null);
                }

                // AutomationElement 検索
                AutomationElement okButtonElem = null;
                AutomationElement editElem     = null;

                if (uiAutomationEnabled)
                {
                    var dialogElem = MakeElementFromHandle(dialog.Handle);
                    if (dialogElem != null)
                    {
                        var elems = await this.DoFindFileDialogElements(dialogElem);

                        if (elems != null)
                        {
                            okButtonElem = elems.Item1;
                            editElem     = elems.Item2;
                        }
                    }
                }

                // ファイル名エディットコントロール検索
                var editControl =
                    await RepeatUntil(
                        () => FindFileDialogFileNameEdit(dialog),
                        (Win32Window c) => c != null,
                        50);

                if (editControl == null)
                {
                    if (uiAutomationEnabled && okButtonElem == null)
                    {
                        ThreadTrace.WriteLine(@"OKボタンが見つかりません。");
                        return(null);
                    }
                    if (editElem == null)
                    {
                        ThreadTrace.WriteLine(@"ファイル名入力欄が見つかりません。");
                        return(null);
                    }
                }

                return
                    (new SaveDialogItemSet(
                         uiAutomationEnabled,
                         editElem,
                         okButtonElem,
                         editControl));
            }
Beispiel #10
0
        /// <summary>
        /// 再生処理を行う。
        /// </summary>
        /// <param name="parameter">コマンドパラメータ。</param>
        /// <returns>コマンドの戻り値。</returns>
        private async Task <CommandResult> ExecutePlay(CommandParameter parameter)
        {
            var process = parameter.Process;

            // 本体側のテキストを使わない場合のみテキスト設定を行う
            if (!parameter.UseTargetText)
            {
                // テキスト取得
                var text = parameter.TalkText;
                if (text == null)
                {
                    return
                        (MakeResult(
                             parameter,
                             AppStatusType.Fail,
                             @"再生処理を開始できませんでした。"));
                }

                // テキスト置換
                text = parameter.VoiceReplaceItems?.Replace(text) ?? text;

                // テキスト設定
                bool setOk = await process.SetTalkText(text);

                if (!setOk && process.Id.IsVoiceroid2LikeSoftware())
                {
                    // VOICEROID2ライクの場合、本体の入力欄が読み取り専用になることがある。
                    // 一旦 再生→停止 の操作を行うことで解除を試みる

                    if (!(await process.Play()))
                    {
                        ThreadTrace.WriteLine(@"VOICEROID2文章入力欄の復旧(再生)に失敗");

                        return
                            (MakeResult(
                                 parameter,
                                 AppStatusType.Fail,
                                 @"再生処理に失敗しました。"));
                    }

                    setOk = (await process.Stop()) && (await process.SetTalkText(text));
                    if (!setOk)
                    {
                        ThreadTrace.WriteLine(@"VOICEROID2文章入力欄の復旧に失敗");
                    }
                }
                if (!setOk)
                {
                    return
                        (MakeResult(
                             parameter,
                             AppStatusType.Fail,
                             @"文章の設定に失敗しました。"));
                }
            }

            // 再生
            var success = await process.Play();

            return
                (MakeResult(
                     parameter,
                     success ? AppStatusType.Success : AppStatusType.Fail,
                     success ? @"再生処理に成功しました。" : @"再生処理に失敗しました。"));
        }