├── .gitignore
├── LICENSE
├── README.md
└── src
├── Speech
├── Controller
│ ├── AITalk3Controller.cs
│ ├── AITalk3Enumerator.cs
│ ├── AIVOICEController.cs
│ ├── AIVOICEEnumerator.cs
│ ├── COEIROINKController.cs
│ ├── COEIROINKEnumerator.cs
│ ├── CeVIO64Controller.cs
│ ├── CeVIO64Enumerator.cs
│ ├── CeVIOAIController.cs
│ ├── CeVIOAIEnumerator.cs
│ ├── CeVIOController.cs
│ ├── CeVIOEnumerator.cs
│ ├── GynoidTalkController.cs
│ ├── GynoidTalkEnumerator.cs
│ ├── ISpeechController.cs
│ ├── ISpeechEnumerator.cs
│ ├── OtomachiUnaTalkController.cs
│ ├── OtomachiUnaTalkEnumerator.cs
│ ├── SAPI5Controller.cs
│ ├── SAPI5Enumerator.cs
│ ├── SHAREVOXController.cs
│ ├── SHAREVOXEnumerator.cs
│ ├── VOICEPEAKController.cs
│ ├── VOICEPEAKEnumerator.cs
│ ├── VOICEVOXController.cs
│ ├── VOICEVOXEnumerator.cs
│ ├── Voiceroid2Controller.cs
│ ├── Voiceroid2Enumerator.cs
│ ├── Voiceroid64Controller.cs
│ ├── Voiceroid64Enumerator.cs
│ ├── VoiceroidPlusController.cs
│ └── VoiceroidPlusEnumerator.cs
├── Effect
│ ├── Wave.cs
│ └── Whisper.cs
├── EngineParameters.cs
├── Properties
│ └── AssemblyInfo.cs
├── SoundPlayer.cs
├── SoundRecorder.cs
├── Speech.csproj
├── SpeechController.cs
├── SpeechEngineInfo.cs
├── app.config
└── packages.config
├── SpeechSample
├── App.config
├── Options.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
└── SpeechSample.csproj
├── SpeechWebServer
├── App.config
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── SpeechWebServer.csproj
├── app.manifest
├── html
│ └── index.html
└── packages.config
├── TTSController.sln
└── TTSController_vs2019.sln
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | build/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studo 2015 cache/options directory
26 | .vs/
27 |
28 | # MSTest test Results
29 | [Tt]est[Rr]esult*/
30 | [Bb]uild[Ll]og.*
31 |
32 | # NUNIT
33 | *.VisualState.xml
34 | TestResult.xml
35 |
36 | # Build Results of an ATL Project
37 | [Dd]ebugPS/
38 | [Rr]eleasePS/
39 | dlldata.c
40 |
41 | *_i.c
42 | *_p.c
43 | *_i.h
44 | *.ilk
45 | *.meta
46 | *.obj
47 | *.pch
48 | *.pdb
49 | *.pgc
50 | *.pgd
51 | *.rsp
52 | *.sbr
53 | *.tlb
54 | *.tli
55 | *.tlh
56 | *.tmp
57 | *.tmp_proj
58 | *.log
59 | *.vspscc
60 | *.vssscc
61 | .builds
62 | *.pidb
63 | *.svclog
64 | *.scc
65 |
66 | # Chutzpah Test files
67 | _Chutzpah*
68 |
69 | # Visual C++ cache files
70 | ipch/
71 | *.aps
72 | *.ncb
73 | *.opensdf
74 | *.sdf
75 | *.cachefile
76 | *.db
77 | *.opendb
78 |
79 | # Visual Studio profiler
80 | *.psess
81 | *.vsp
82 | *.vspx
83 |
84 | # TFS 2012 Local Workspace
85 | $tf/
86 |
87 | # Guidance Automation Toolkit
88 | *.gpState
89 |
90 | # ReSharper is a .NET coding add-in
91 | _ReSharper*/
92 | *.[Rr]e[Ss]harper
93 | *.DotSettings.user
94 |
95 | # JustCode is a .NET coding addin-in
96 | .JustCode
97 |
98 | # TeamCity is a build add-in
99 | _TeamCity*
100 |
101 | # DotCover is a Code Coverage Tool
102 | *.dotCover
103 |
104 | # NCrunch
105 | _NCrunch_*
106 | .*crunch*.local.xml
107 |
108 | # MightyMoose
109 | *.mm.*
110 | AutoTest.Net/
111 |
112 | # Web workbench (sass)
113 | .sass-cache/
114 |
115 | # Installshield output folder
116 | [Ee]xpress/
117 |
118 | # DocProject is a documentation generator add-in
119 | DocProject/buildhelp/
120 | DocProject/Help/*.HxT
121 | DocProject/Help/*.HxC
122 | DocProject/Help/*.hhc
123 | DocProject/Help/*.hhk
124 | DocProject/Help/*.hhp
125 | DocProject/Help/Html2
126 | DocProject/Help/html
127 |
128 | # Click-Once directory
129 | publish/
130 |
131 | # Publish Web Output
132 | *.[Pp]ublish.xml
133 | *.azurePubxml
134 | # TODO: Comment the next line if you want to checkin your web deploy settings
135 | # but database connection strings (with potential passwords) will be unencrypted
136 | *.pubxml
137 | *.publishproj
138 |
139 | # NuGet Packages
140 | *.nupkg
141 | # The packages folder can be ignored because of Package Restore
142 | **/packages/*
143 | # except build/, which is used as an MSBuild target.
144 | !**/packages/build/
145 | # Uncomment if necessary however generally it will be regenerated when needed
146 | #!**/packages/repositories.config
147 |
148 | # Windows Azure Build Output
149 | csx/
150 | *.build.csdef
151 |
152 | # Windows Store app package directory
153 | AppPackages/
154 |
155 | # Others
156 | *.[Cc]ache
157 | ClientBin/
158 | [Ss]tyle[Cc]op.*
159 | ~$*
160 | *~
161 | *.dbmdl
162 | *.dbproj.schemaview
163 | *.pfx
164 | *.publishsettings
165 | node_modules/
166 | bower_components/
167 |
168 | # RIA/Silverlight projects
169 | Generated_Code/
170 |
171 | # Backup & report files from converting an old project file
172 | # to a newer Visual Studio version. Backup files are not needed,
173 | # because we have git ;-)
174 | _UpgradeReport_Files/
175 | Backup*/
176 | UpgradeLog*.XML
177 | UpgradeLog*.htm
178 |
179 | # SQL Server files
180 | *.mdf
181 | *.ldf
182 |
183 | # Business Intelligence projects
184 | *.rdl.data
185 | *.bim.layout
186 | *.bim_*.settings
187 |
188 | # Microsoft Fakes
189 | FakesAssemblies/
190 |
191 | # Node.js Tools for Visual Studio
192 | .ntvs_analysis.dat
193 |
194 | # Visual Studio 6 build log
195 | *.plg
196 |
197 | # Visual Studio 6 workspace options file
198 | *.opt
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TTSController
2 | 各種 Text-to-Speech エンジンを統一的に操作するライブラリです。VOICEROIDなどを自動化する簡易Webサーバもあります。
3 |
4 | ## 対応プラットフォーム
5 | - Windows 10/11 (64bit)
6 |
7 | ## 対応音声合成ライブラリ
8 | - VOICEROID+ 各種
9 | - 音街ウナTalkEx
10 | - VOICEROID2 各種 (x86, x64)
11 | - A.I.VOICE (要x64ビルド)
12 | - ガイノイドTALK 各種
13 | - かんたん!AITalk3 / 関西風 / Lite
14 | - CeVIO CS6 / CS7 (x86, x64)
15 | - CeVIO AI (要x64ビルド)
16 | - VOICEVOX
17 | - COEIROINK
18 | - SAPI5 (Windows10標準の音声合成機能。スタートメニュー>設定>時刻と言語>音声認識>音声の管理>音声の追加から各国語の音声が追加できます。API仕様により追加しても列挙されない音声があります。)
19 | - VOICEPEAK
20 |
21 | ### 動作確認済みリスト
22 | ライブラリ名はインストールされたフォルダなどを参照して機械的に抽出しているため、リストにないものでも音声合成エンジンが共通であれば動作する可能性が高いです。
23 | |音声合成エンジン|ライブラリ名|
24 | |---|---|
25 | |VOICEROID+ EX|民安ともえ EX, 東北ずん子, 東北きりたん, 京町セイカ|
26 | |音街ウナTalkEx|音街ウナ|
27 | |VOICEROID2|琴葉 茜, 琴葉 葵, 結月ゆかり, 紲星あかり, 東北イタコ, 桜乃そら, ついなちゃん(標準語), ついなちゃん(関西弁)|
28 | |VOICEROID2 (VOICEROID+ EX からのアップグレード)|民安ともえ(v1), 東北ずん子(v1), 東北きりたん(v1), 京町セイカ(v1)|
29 | |かんたん!AITalk3|あんず, かほ, ななこ, のぞみ, せいじ|
30 | |かんたん!AITalk3 関西風|みやび, やまと|
31 | |かんたん!AITalk3 LITE|あんず(LITE), かほ(LITE), ななこ(LITE), のぞみ(LITE), せいじ(LITE)|
32 | |ガイノイドTALK|鳴花ヒメ, 鳴花ミコト|
33 | |A.I.VOICE|琴葉 茜,琴葉 茜(蕾),琴葉 茜,琴葉 茜(蕾),Kotonoha Akane (English),Kotonoha Aoi (English),結城 香, 足立 レイ,栗田まろん|
34 | |CeVIO CS6 / CS7|さとうささら, すずきつづみ, タカハシ, IA, ONE|
35 | |CeVIO AI|さとうささら, 小春六花, 弦巻マキ (英), 弦巻マキ (日) ※ライセンスエラーが出る場合は [#5](https://github.com/ksasao/TTSController/issues/5) へお知らせください|
36 | |VOICEVOX|四国めたん,ずんだもん,春日部つむぎ,雨晴はう,波音リツ,玄野武宏,白上虎太郎,青山龍星,冥鳴ひまり,九州そら|
37 | |COEIROINK|つくよみちゃん,MANA,おふとんP,ディアちゃん,アルマちゃん|
38 | |SAPI5|Microsoft Haruka Desktop, Microsoft David Desktop, Microsoft Zira Desktop, Microsoft Irina Desktop|
39 | |VOICEPEAK(1.2.1以降)|Frimomen, Tohoku Zunko, Zundamon, Japanese Female Child, Japanese Male 1, Japanese Male 2, Japanese Male 3, Japanese Female 1, Japanese Female 2, Japanese Female 3|
40 |
41 | ## ブラウザで音声合成する
42 | この実装は簡易実装であり、音声合成ライブラリと同一のPC上で実行することを想定しています。インターネット上への公開は、セキュリティ上のリスクや音声合成ライブラリのライセンス上の問題がある可能性があります。
43 |
44 | - [ビルド済み実行ファイル64bit版(v0.1.0)](https://github.com/ksasao/TTSController/releases/download/v0.1.0/SpeechWebServer_x64_v0.1.0.zip) (2022/3/15更新)
45 | - [ビルド済み実行ファイル32bit版(v0.1.0)](https://github.com/ksasao/TTSController/releases/download/v0.1.0/SpeechWebServer_x86_v0.1.0.zip) (2022/3/15更新)
46 |
47 | ### 準備
48 | - SpeechWebServer のプロジェクトを Visual Studio 2019 でビルドして ```SpeechWebServer.exe``` を実行します(管理者権限が必要です)
49 |
50 | ### 利用方法
51 | - ブラウザで http://localhost:1000/ を開くと現在の時刻を発話します
52 | - http://localhost:1000/?text=こんにちは を開くと「こんにちは」と発話します。「こんにちは」の部分は任意の文字列を指定できます
53 | - http://localhost:1000/?text=おはようございます&range=1.2&volume=1.0&pitch=0.8&speed=0.8 のように、音量(volume), 話速(speed), 高さ(pitch), 抑揚(range) を指定できます (かんたん!AITalk3 LITE, CeVIO, SAPI5を除く)
54 | - VOICEROID+ 東北きりたんがインストールされている場合、http://localhost:1000/?name=東北きりたん&text=こんばんは を開くと東北きりたんの声で発話します。他の音声合成エンジンを利用する場合は、アプリ起動時に表示される「インストール済み音声合成ライブラリ」の表記を参考に、適宜 name の引数を変更してください。なお、VOICEVOX, COEIROINK を利用する場合は、あらかじめアプリケーションを起動しておいてください。
55 | - 複数の音声合成エンジンがインストールされており、同一のnameが利用されている環境では、http://localhost:1000/?text=アカネチャンやでー&name=琴葉%20茜&engine=AIVOICE のように engine で区別をします
56 | - http://localhost:1000/?text=おはよう&speaker=和室 のように音声を再生するスピーカー名を指定することができます。カッコ内の文字列を前方一致で検索します。なお、Google Home デバイスは Windows から Bluetoothスピーカーとして接続ができ、任意の名前(「和室」など)を付けることが可能です。
57 | - http://localhost:1000/?text=ささやき声なのだ&name=ずんだもん&whisper=0.02&speed=0.8 のようにwhisperを設定することで、任意の音声をささやき声に変換できます(ささやき声化した音声は whisper.wav として自動的に保存されます)。音声がおかしく聞こえる場合は &volume=0.6 などのオプションを指定して音量を小さくしてみてください。
58 |
59 | 
60 |
61 | ## TODO
62 |
63 | ### 制御機能
64 | - [x] 話者の一覧取得
65 | - [x] 話者に応じたTTS切り替え
66 | - [ ] Bluetooth スピーカーの安定動作のための無音区間挿入
67 | - [ ] 同時起動対応(先に起動しているほうに処理を委譲)
68 |
69 | ### 音声コントロール
70 | - [x] 再生
71 | - [x] 音量の取得・変更
72 | - [x] 話速の取得・変更
73 | - [x] ピッチの取得・変更
74 | - [x] 抑揚の取得・変更
75 | - [x] 発話中の音声停止
76 | - [x] 合成した音声の保存
77 | - [ ] 連続して文字列が入力されたときの対応
78 | - [ ] 音声合成対象の文字列の途中に .wav ファイルを差し込み
79 | - [ ] 音声合成対象の文字列の途中に音声コントロールを埋め込み
80 | - [x] 音声出力デバイス選択
81 |
82 |
--------------------------------------------------------------------------------
/src/Speech/Controller/AITalk3Controller.cs:
--------------------------------------------------------------------------------
1 | using Codeer.Friendly;
2 | using Codeer.Friendly.Windows;
3 | using Codeer.Friendly.Windows.Grasp;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.ComponentModel;
7 | using System.Diagnostics;
8 | using System.IO;
9 | using System.Linq;
10 | using System.Runtime.InteropServices;
11 | using System.Text;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 | using System.Windows.Forms;
15 | using System.Windows.Threading;
16 |
17 | namespace Speech
18 | {
19 | ///
20 | /// AITalk3 操作クラス
21 | ///
22 | public class AITalk3Controller : VoiceroidPlusController
23 | {
24 | System.Timers.Timer _timer; // 状態監視のためのタイマー
25 | bool _playStarting = false;
26 | int _voiceIndex = 0;
27 |
28 |
29 | ///
30 | /// AITalk3 のフルパス
31 | ///
32 | public string AITalk3Path { get; private set; }
33 |
34 |
35 | public AITalk3Controller(SpeechEngineInfo info) : base(info)
36 | {
37 | Info = info;
38 | AITalk3Path = info.EnginePath;
39 | _timer = new System.Timers.Timer(100);
40 | _timer.Elapsed += timer_Elapsed;
41 |
42 | AITalk3Enumerator aitalk3Enumerator = new AITalk3Enumerator();
43 | var list = aitalk3Enumerator.GetSpeechEngineInfo();
44 | int count = 0;
45 | string exePath = "";
46 | for(int i=0; i
83 | /// AITalk3 に入力された文字列を再生します
84 | ///
85 | public override void Play()
86 | {
87 | // 話者選択
88 | WindowControl comboBox = _root.IdentifyFromZIndex(2, 0, 0, 1, 0, 0, 0, 0, 0);
89 | AppVar combo = comboBox.AppVar;
90 | combo["SelectedIndex"](_voiceIndex);
91 |
92 | // 再生ボタンをクリック
93 | WindowControl playButton = _root.IdentifyFromZIndex(2, 0, 0, 1, 0, 1, 0, 2);
94 | AppVar button = playButton.AppVar;
95 | string text = (string)button["Text"]().Core;
96 | if(text.Trim() == "再生")
97 | {
98 | button["PerformClick"]();
99 | _playStarting = true;
100 | _timer.Start();
101 | }
102 | }
103 | ///
104 | /// AITalk3 の再生を停止します(停止ボタンを押す)
105 | ///
106 | public override void Stop()
107 | {
108 | WindowControl stopButton = _root.IdentifyFromZIndex(2, 0, 0, 1, 0, 1, 0, 1);
109 | AppVar button = stopButton.AppVar;
110 | button["PerformClick"]();
111 | }
112 |
113 |
114 | protected override void SetEffect(EffectType t, float value)
115 | {
116 | if (Info.LibraryName.IndexOf("LITE") > 0)
117 | {
118 | // LITEは各種操作が出来ない
119 | return;
120 | }
121 | ChangeToVoiceEffect();
122 | int index = (int)t;
123 | WindowControl control = _root.IdentifyFromZIndex(2, 0, 0, 1, 0, 0, 1, 0, 0, index);
124 | AppVar v = control.AppVar;
125 | v["Focus"]();
126 | v["Text"](string.Format("{0:0.00}", value));
127 |
128 | Thread.Sleep(100);
129 | SendKeys.SendWait("{TAB}");
130 | }
131 | protected override float GetEffect(EffectType t)
132 | {
133 | if (Info.LibraryName.IndexOf("LITE") > 0)
134 | {
135 | // LITEは各種操作が出来ない
136 | return 1.0f;
137 | }
138 | ChangeToVoiceEffect();
139 | int index = (int)t;
140 | WindowControl control = _root.IdentifyFromZIndex(2, 0, 0, 1, 0, 0, 1, 0, 0, index);
141 | AppVar v = control.AppVar;
142 | return Convert.ToSingle((string)v["Text"]().Core);
143 | }
144 |
145 |
146 | ///
147 | /// 音声効果タブを選択します
148 | ///
149 | protected override void ChangeToVoiceEffect()
150 | {
151 | RestoreMinimizedWindow();
152 | WindowControl tabControl = _root.IdentifyFromZIndex(2, 0, 0, 1, 0, 0, 1);
153 | tabControl.SetFocus();
154 | AppVar tab = tabControl.AppVar;
155 | tab["SelectedIndex"]((int)0);
156 | }
157 |
158 | }
159 | }
--------------------------------------------------------------------------------
/src/Speech/Controller/AITalk3Enumerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Xml.Linq;
8 |
9 | namespace Speech
10 | {
11 |
12 | public class AITalk3Enumerator : VoiceroidPlusEnumerator
13 | {
14 |
15 | public AITalk3Enumerator() : base()
16 | {
17 | Initialize();
18 | }
19 |
20 | private void Initialize()
21 | {
22 | EngineName = "AITalk3";
23 | List voiceData = new List();
24 | string basePath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86) + @"\AI\AITalk3";
25 | if (Directory.Exists(basePath))
26 | {
27 | string[] dirs = Directory.GetDirectories(basePath);
28 | foreach (var d in dirs)
29 | {
30 | voiceData.AddRange(FindAITalk(d));
31 | }
32 | }
33 | _info = voiceData.ToArray();
34 | }
35 |
36 | private List FindAITalk(string path)
37 | {
38 | List data = new List();
39 | try
40 | {
41 | string[] dirs = Directory.GetDirectories(Path.Combine(path, "voice"));
42 | Array.Sort(dirs); // AITalkの話者の表示順序は英語フォルダ名の辞書式順序
43 |
44 | for (int i = 0; i < dirs.Length; i++)
45 | {
46 | string xmlFile = Path.Combine(dirs[i], "dbconf.xml");
47 | if (File.Exists(xmlFile))
48 | {
49 | Data d = new Data();
50 | var xml = XElement.Load(Path.Combine(xmlFile));
51 | d.Name = xml.Element("profile").Attribute("name").Value;
52 | if (path.IndexOf("AITalkLite") > 0)
53 | {
54 | d.Name += "(LITE)";
55 | }
56 | d.Path = Directory.GetFiles(path,"*.exe")[0];
57 | data.Add(d);
58 | }
59 | }
60 | }
61 | catch
62 | {
63 | // 初期化に途中で失敗した場合はうまく処理できたところまで返す
64 | }
65 |
66 | return data;
67 | }
68 | public override ISpeechController GetControllerInstance(SpeechEngineInfo info)
69 | {
70 | return EngineName == info.EngineName ? new AITalk3Controller(info) : null;
71 | }
72 |
73 |
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Speech/Controller/AIVOICEController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Diagnostics;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Reflection;
8 | using System.Runtime.InteropServices;
9 | using System.Runtime.Serialization.Json;
10 | using System.Text;
11 | using System.Threading;
12 | using System.Threading.Tasks;
13 |
14 | namespace Speech
15 | {
16 | public class AIVOICEController : MarshalByRefObject, IDisposable, ISpeechController
17 | {
18 | public class Master
19 | {
20 | public float Volume { get; set; } = 1;
21 | public float Speed { get; set; } = 1;
22 | public float Pitch { get; set; } = 1;
23 | public float PitchRange { get; set; } = 1;
24 | public bool IsPauseEnabled { get; set; } = true;
25 | public int MiddlePause { get; set; } = 150;
26 | public int LongPause { get; set; } = 370;
27 | public int SentencePause { get; set; } = 800;
28 | public float VolumeDecibel { get; set; } = 0;
29 | public float PitchCent { get; set; } = 0;
30 | public float PitchHalfTone { get; set; } = 0;
31 | public float PitchRangePercent { get; set; } = 100;
32 | }
33 |
34 | Process _process;
35 | System.Timers.Timer _timer; // 状態監視のためのタイマー
36 | Queue _queue = new Queue();
37 | dynamic _ttsControl = null;
38 |
39 | public delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lparam);
40 | static int _pid = 0;
41 |
42 | public SpeechEngineInfo Info { get; private set; }
43 |
44 | ///
45 | /// A.I.VOICE のフルパス
46 | ///
47 | public string AIVOICEPath { get; private set; }
48 |
49 | string _libraryName;
50 | string _promptString;
51 | bool _isPlaying = false;
52 | public AIVOICEController(SpeechEngineInfo info)
53 | {
54 | Info = info;
55 |
56 | var aivoice = new AIVOICEEnumerator();
57 | _promptString = aivoice.PromptString;
58 |
59 | AIVOICEPath = info.EnginePath;
60 | _libraryName = info.LibraryName;
61 | _timer = new System.Timers.Timer(100);
62 | _timer.Elapsed += timer_Elapsed;
63 | }
64 |
65 | object _lockObject = new object();
66 |
67 | private void timer_Elapsed(object sender, EventArgs e)
68 | {
69 | _timer.Stop(); // 途中の処理が重いため、タイマーをいったん止める
70 | if (_queue.Count == 0)
71 | {
72 | StopSpeech();
73 | return; // タイマーが止まったまま終了
74 | }
75 | else
76 | {
77 | // 喋るべき内容が残っているときは再開
78 | string t = _queue.Dequeue();
79 | _ttsControl.Text = t;
80 | Play();
81 | _isPlaying = true;
82 | }
83 | _timer.Start();
84 |
85 | }
86 |
87 | private void StopSpeech()
88 | {
89 | _timer.Stop();
90 | lock (_lockObject)
91 | {
92 | if (_isPlaying)
93 | {
94 | _isPlaying = false;
95 | OnFinished();
96 | }
97 | }
98 | }
99 |
100 | ///
101 | /// 音声再生が完了したときに発生するイベント
102 | ///
103 | public event EventHandler Finished;
104 | protected virtual void OnFinished()
105 | {
106 | EventArgs se = new EventArgs();
107 | Finished?.Invoke(this, se);
108 | }
109 |
110 | ///
111 | /// A.I.VOICE が起動中かどうかを確認
112 | ///
113 | /// 起動中であれば true
114 | public bool IsActive()
115 | {
116 | string name = Path.GetFileNameWithoutExtension(AIVOICEPath);
117 | Process[] localByName = Process.GetProcessesByName(name);
118 |
119 | if (localByName.Length > 0)
120 | {
121 | // A.I.VOICE は2重起動しないはずなので 0番目を参照する
122 | _process = localByName[0];
123 | _pid = _process.Id;
124 | return true;
125 | }
126 | return false;
127 | }
128 |
129 | ///
130 | /// A.I.VOICEを起動する。すでに起動している場合には起動しているものを操作対象とする。
131 | ///
132 | public void Activate()
133 | {
134 | string path = Path.Combine(Path.GetDirectoryName(AIVOICEPath), "AI.Talk.Editor.Api.dll");
135 | Assembly assembly = Assembly.LoadFrom(path);
136 | Type type = assembly.GetType("AI.Talk.Editor.Api.TtsControl");
137 | _ttsControl = Activator.CreateInstance(type, new object[] { });
138 |
139 | var names = _ttsControl.GetAvailableHostNames();
140 | _ttsControl.Initialize(names[0]); // names[0] = "A.I.VOICE Editor"
141 |
142 | if (!IsActive())
143 | {
144 | _ttsControl.StartHost();
145 | }
146 | _ttsControl.Connect();
147 | }
148 |
149 | ///
150 | /// 指定した文字列を再生します
151 | ///
152 | /// 再生する文字列
153 | public void Play(string text)
154 | {
155 | SetText(text);
156 | }
157 | internal void SetText(string text)
158 | {
159 | text = text.Trim() == "" ? "." : text;
160 | string t = _libraryName + _promptString + text;
161 | if (_queue.Count == 0)
162 | {
163 | _ttsControl.Text = t;
164 | Play();
165 | }
166 | else
167 | {
168 | _queue.Enqueue(t);
169 | }
170 | }
171 |
172 | ///
173 | /// A.I.VOICE に入力された文字列を再生します
174 | ///
175 | public void Play()
176 | {
177 | long ms = _ttsControl.GetPlayTime();
178 | _ttsControl.Play();
179 | _isPlaying = true;
180 | _timer.Interval = ms;
181 | _timer.Start();
182 | }
183 |
184 | private string ConvertToJson(Master master)
185 | {
186 | // この順序の JSON でないと正しくUIに反映されない模様...
187 | return "{ \"Volume\" : " + master.Volume + ", " +
188 | "\"Pitch\" : " + master.Pitch + ", " +
189 | "\"Speed\" : " + master.Speed + ", " +
190 | "\"PitchRange\" : " + master.PitchRange + ", " +
191 | "\"MiddlePause\" : " + master.MiddlePause + ", " +
192 | "\"LongPause\" : " + master.LongPause + ", " +
193 | "\"SentencePause\" : " + master.SentencePause + " }";
194 | }
195 | private Master ConvertToMaster(string json)
196 | {
197 | var serializer = new DataContractJsonSerializer(typeof(Master));
198 | using (var mst = new MemoryStream(Encoding.UTF8.GetBytes(json)))
199 | {
200 | return (Master)serializer.ReadObject(mst);
201 | }
202 | }
203 |
204 | private Master GetMaster()
205 | {
206 | var json = _ttsControl.MasterControl;
207 | Master master = ConvertToMaster(json);
208 | return master;
209 | }
210 |
211 | private void SetMaster(Master master)
212 | {
213 | string json = ConvertToJson(master);
214 | _ttsControl.MasterControl = json;
215 | }
216 |
217 | ///
218 | /// A.I.VOICE の再生を停止します
219 | ///
220 | public void Stop()
221 | {
222 | StopSpeech();
223 | _ttsControl.Stop();
224 | }
225 |
226 | enum EffectType { Volume = 0, Speed = 1, Pitch = 2, PitchRange = 3 }
227 | ///
228 | /// 音量を設定します
229 | ///
230 | /// 0.0~2.0
231 | public void SetVolume(float value)
232 | {
233 | Master master = GetMaster();
234 | master.Volume = value;
235 | SetMaster(master);
236 | }
237 | ///
238 | /// 音量を取得します
239 | ///
240 | /// 音量
241 | public float GetVolume()
242 | {
243 | return GetMaster().Volume;
244 | }
245 | ///
246 | /// 話速を設定します
247 | ///
248 | /// 0.5~4.0
249 | public void SetSpeed(float value)
250 | {
251 | Master master = GetMaster();
252 | master.Speed = value;
253 | SetMaster(master);
254 | }
255 | ///
256 | /// 話速を取得します
257 | ///
258 | /// 話速
259 | public float GetSpeed()
260 | {
261 | return GetMaster().Speed;
262 | }
263 |
264 | ///
265 | /// 高さを設定します
266 | ///
267 | /// 0.5~2.0
268 | public void SetPitch(float value)
269 | {
270 | Master master = GetMaster();
271 | master.Pitch = value;
272 | SetMaster(master);
273 | }
274 | ///
275 | /// 高さを取得します
276 | ///
277 | /// 高さ
278 | public float GetPitch()
279 | {
280 | return GetMaster().Pitch;
281 | }
282 | ///
283 | /// 抑揚を設定します
284 | ///
285 | /// 0.0~2.0
286 | public void SetPitchRange(float value)
287 | {
288 | Master master = GetMaster();
289 | master.PitchRange = value;
290 | SetMaster(master);
291 | }
292 | ///
293 | /// 抑揚を取得します
294 | ///
295 | /// 抑揚
296 | public float GetPitchRange()
297 | {
298 | return GetMaster().PitchRange;
299 | }
300 |
301 | #region IDisposable Support
302 | private bool disposedValue = false;
303 |
304 | protected virtual void Dispose(bool disposing)
305 | {
306 | if (!disposedValue)
307 | {
308 | if (disposing)
309 | {
310 | if (_ttsControl != null)
311 | {
312 | _ttsControl.Disconnect();
313 | }
314 | }
315 | disposedValue = true;
316 | }
317 | }
318 |
319 | public void Dispose()
320 | {
321 | Dispose(true);
322 | }
323 | #endregion
324 | }
325 | }
326 |
--------------------------------------------------------------------------------
/src/Speech/Controller/AIVOICEEnumerator.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Win32;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Xml.Linq;
9 |
10 | namespace Speech
11 | {
12 | class AIVOICEEnumerator : Voiceroid2Enumerator
13 | {
14 | public AIVOICEEnumerator()
15 | {
16 | // A.I.VOICEの一覧は下記で取得できる
17 | // 下記ファイルはAIVOICE Editor終了時に生成されるため、一度 起動・終了
18 | // しておくこと
19 | string path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
20 | + @"\AI\A.I.VOICE Editor\1.0\Standard.settings";
21 | Initialize(path, "AIVOICE");
22 | }
23 |
24 | internal override string GetInstalledPath()
25 | {
26 | string installPath = @"SOFTWARE\AI\AIVoice\AIVoiceEditor\1.0";
27 |
28 | string result = "";
29 | RegistryKey key64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
30 | RegistryKey install = key64.OpenSubKey(installPath);
31 | if (install != null)
32 | {
33 | var key = install.GetValue("InstallDir");
34 | if (key != null)
35 | {
36 | var location = key.ToString();
37 | result = Path.Combine(location, @"AIVoiceEditor.exe");
38 | }
39 | }
40 | return result;
41 | }
42 | public override SpeechEngineInfo[] GetSpeechEngineInfo()
43 | {
44 | List info = new List();
45 | string path = GetInstalledPath();
46 | // インストール先のパスが見つからない場合はSpeechEngineInfoを追加しない
47 | if (!string.IsNullOrEmpty(path))
48 | {
49 | foreach (var v in _name)
50 | {
51 | info.Add(new SpeechEngineInfo { EngineName = EngineName, EnginePath = path, LibraryName = v, Is64BitProcess = true }) ;
52 | }
53 | }
54 | return info.ToArray();
55 | }
56 | public override ISpeechController GetControllerInstance(SpeechEngineInfo info)
57 | {
58 | return EngineName == info.EngineName ? new AIVOICEController(info) : null;
59 | }
60 |
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Speech/Controller/COEIROINKController.cs:
--------------------------------------------------------------------------------
1 | using Codeer.Friendly;
2 | using Codeer.Friendly.Windows;
3 | using Codeer.Friendly.Windows.Grasp;
4 | using RM.Friendly.WPFStandardControls;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.ComponentModel;
8 | using System.Diagnostics;
9 | using System.IO;
10 | using System.Linq;
11 | using System.Net;
12 | using System.Net.Http;
13 | using System.Reflection;
14 | using System.Runtime.InteropServices;
15 | using System.Text;
16 | using System.Threading;
17 | using System.Threading.Tasks;
18 | using System.Windows.Forms;
19 | using System.Windows.Media.Animation;
20 | using System.Windows.Threading;
21 |
22 | namespace Speech
23 | {
24 | ///
25 | /// COEIROINK 操作クラス
26 | ///
27 | public class COEIROINKController : VOICEVOXController
28 | {
29 | public COEIROINKController(SpeechEngineInfo info) :base(info)
30 | {
31 | Info = info;
32 | _enumerator = new COEIROINKEnumerator();
33 | _baseUrl = _enumerator.BaseUrl;
34 | _libraryName = info.LibraryName;
35 | }
36 |
37 | }
38 | }
--------------------------------------------------------------------------------
/src/Speech/Controller/COEIROINKEnumerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Net.Http;
7 | using System.Runtime.Serialization.Json;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 | using System.Xml.Linq;
11 |
12 | namespace Speech
13 | {
14 | public class COEIROINKEnumerator : VOICEVOXEnumerator
15 | {
16 | public COEIROINKEnumerator()
17 | {
18 | Initialize("COEIROINK", "http://127.0.0.1:50031");
19 | }
20 | public override ISpeechController GetControllerInstance(SpeechEngineInfo info)
21 | {
22 | return EngineName == info.EngineName ? new COEIROINKController(info) : null;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Speech/Controller/CeVIO64Controller.cs:
--------------------------------------------------------------------------------
1 | using Codeer.Friendly;
2 | using Codeer.Friendly.Windows;
3 | using Codeer.Friendly.Windows.Grasp;
4 | using RM.Friendly.WPFStandardControls;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.ComponentModel;
8 | using System.Diagnostics;
9 | using System.IO;
10 | using System.Linq;
11 | using System.Reflection;
12 | using System.Runtime.InteropServices;
13 | using System.Text;
14 | using System.Threading;
15 | using System.Threading.Tasks;
16 | using System.Windows.Forms;
17 | using System.Windows.Media.Animation;
18 | using System.Windows.Threading;
19 |
20 | namespace Speech
21 | {
22 | ///
23 | /// CeVIO 操作クラス
24 | ///
25 | public class CeVIO64Controller : IDisposable, ISpeechController
26 | {
27 | public SpeechEngineInfo Info { get; private set; }
28 |
29 | string _libraryName;
30 | dynamic _talker = null;
31 | Assembly _assembly;
32 | Type _serviceControl;
33 |
34 | CeVIO64Enumerator _cevio;
35 |
36 | public CeVIO64Controller(SpeechEngineInfo info)
37 | {
38 | Info = info;
39 |
40 | _cevio = new CeVIO64Enumerator();
41 | _libraryName = info.LibraryName;
42 | }
43 |
44 | ///
45 | /// 音声再生が完了したときに発生するイベント
46 | ///
47 | public event EventHandler Finished;
48 | protected virtual void OnFinished()
49 | {
50 | EventArgs se = new EventArgs();
51 | Finished?.Invoke(this, se);
52 | }
53 |
54 | ///
55 | /// CeVIO が起動中かどうかを確認
56 | ///
57 | /// 起動中であれば true
58 | public bool IsActive()
59 | {
60 | string name = Path.GetFileNameWithoutExtension(Info.EnginePath);
61 | Process[] localByName = Process.GetProcessesByName(name);
62 |
63 | if (localByName.Length > 0)
64 | {
65 | return true;
66 | }
67 | return false;
68 | }
69 |
70 | ///
71 | /// CeVIO を起動する。すでに起動している場合には起動しているものを操作対象とする。
72 | ///
73 | public void Activate()
74 | {
75 | _assembly = Assembly.LoadFrom(_cevio.AssemblyPath);
76 | _serviceControl = _assembly.GetType("CeVIO.Talk.RemoteService.ServiceControl");
77 |
78 | //// 【CeVIO Creative Studio】起動
79 | //ServiceControl.StartHost(false);
80 | MethodInfo startHost = _serviceControl.GetMethod("StartHost");
81 | startHost.Invoke(null, new object[] { false });
82 |
83 | _talker = Activator.CreateInstance(_assembly.GetType("CeVIO.Talk.RemoteService.Talker"), new object[] { Info.LibraryName });
84 | }
85 |
86 | ///
87 | /// 指定した文字列を再生します
88 | ///
89 | /// 再生する文字列
90 | public void Play(string text)
91 | {
92 | var state = _talker.Speak(text);
93 | state.Wait();
94 | OnFinished();
95 | }
96 |
97 | ///
98 | /// このメソッドは無効です。発話する文字列を指定してください。
99 | ///
100 | public void Play()
101 | {
102 | }
103 | ///
104 | /// 再生を停止します
105 | ///
106 | public void Stop()
107 | {
108 | }
109 |
110 | enum EffectType { Volume = 0, Speed = 1, Pitch = 2, PitchRange = 3}
111 | ///
112 | /// 音量を設定します
113 | ///
114 | /// 0.0~2.0
115 | public void SetVolume(float value)
116 | {
117 | if (value > 2)
118 | {
119 | value = 2;
120 | }
121 | else if (value < 0)
122 | {
123 | value = 0;
124 | }
125 | _talker.Volume = (uint)(value * 50);
126 | }
127 | ///
128 | /// 音量を取得します
129 | ///
130 | /// 音量
131 | public float GetVolume()
132 | {
133 | return _talker.Volume / 50f;
134 | }
135 | ///
136 | /// 話速を設定します
137 | ///
138 | /// 0.5~4.0
139 | public void SetSpeed(float value)
140 | {
141 | if (value > 2)
142 | {
143 | value = 2;
144 | }
145 | else if (value < 0)
146 | {
147 | value = 0;
148 | }
149 | _talker.Speed = (uint)(value * 50);
150 | }
151 | ///
152 | /// 話速を取得します
153 | ///
154 | /// 話速
155 | public float GetSpeed()
156 | {
157 | return _talker.Speed / 50f;
158 | }
159 |
160 | ///
161 | /// 高さを設定します
162 | ///
163 | /// 0.5~2.0
164 | public void SetPitch(float value)
165 | {
166 | if (value > 2)
167 | {
168 | value = 2;
169 | }
170 | else if (value < 0)
171 | {
172 | value = 0;
173 | }
174 | _talker.Tone = (uint)(value * 50);
175 | }
176 | ///
177 | /// 高さを取得します
178 | ///
179 | /// 高さ
180 | public float GetPitch()
181 | {
182 | return _talker.Tone / 50f;
183 | }
184 | ///
185 | /// 抑揚を設定します
186 | ///
187 | /// 0.0~2.0
188 | public void SetPitchRange(float value)
189 | {
190 | if (value > 2)
191 | {
192 | value = 2;
193 | }
194 | else if (value < 0)
195 | {
196 | value = 0;
197 | }
198 | _talker.ToneScale = (uint)(value * 50);
199 | }
200 | ///
201 | /// 抑揚を取得します
202 | ///
203 | /// 抑揚
204 | public float GetPitchRange()
205 | {
206 | return _talker.ToneScale / 50f;
207 | }
208 |
209 | ///
210 | /// 声色を設定します
211 | ///
212 | /// パラメータ名
213 | /// 0~100
214 | public void SetVoiceParam(string Name, uint value)
215 | {
216 | if (value > 100)
217 | {
218 | value = 100;
219 | }
220 | else if (value < 0)
221 | {
222 | value = 0;
223 | }
224 | _talker.Components.ByName(Name).Value = (uint)(value);
225 | }
226 | ///
227 | /// 声色を取得します
228 | ///
229 | /// パラメータ名
230 | /// パラメータ値
231 | public uint GetVoiceParam(string Name)
232 | {
233 | return _talker.Components.ByName(Name).Value;
234 | }
235 | ///
236 | /// 声質を設定します
237 | ///
238 | /// 0.0~100.0
239 | public void SetVoiceQuality(uint value)
240 | {
241 | if (value > 100)
242 | {
243 | value = 100;
244 | }
245 | else if (value < 0)
246 | {
247 | value = 0;
248 | }
249 | _talker.Alpha = (uint)(value);
250 | }
251 | ///
252 | /// 声質を取得します
253 | ///
254 | /// パラメータ名
255 | /// パラメータ値
256 | public uint GetVoiceQuality()
257 | {
258 | return _talker.Alpha;
259 | }
260 |
261 | #region IDisposable Support
262 | private bool disposedValue = false;
263 |
264 | protected virtual void Dispose(bool disposing)
265 | {
266 | if (!disposedValue)
267 | {
268 | if (disposing)
269 | {
270 | // CeVIO を終了する場合はコメントを外す
271 | //MethodInfo closeHost = _serviceControl.GetMethod("CloseHost");
272 | //var hostCloseMode = _assembly.GetType("CeVIO.Talk.RemoteService.HostCloseMode");
273 | //var mode = Enum.Parse(hostCloseMode, "Interrupt"); // Default, Interrupt, NotCancelable
274 | //closeHost.Invoke(null, new object[] { mode });
275 | }
276 | disposedValue = true;
277 | }
278 | }
279 |
280 | public void Dispose()
281 | {
282 | Dispose(true);
283 | }
284 | #endregion
285 | }
286 | }
--------------------------------------------------------------------------------
/src/Speech/Controller/CeVIO64Enumerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Xml.Linq;
8 |
9 | namespace Speech
10 | {
11 | public class CeVIO64Enumerator : ISpeechEnumerator
12 | {
13 | string[] _name = new string[0];
14 | string _installedPath = "";
15 | public const string EngineName = "CeVIO64";
16 | public CeVIO64Enumerator()
17 | {
18 | Initialize();
19 | }
20 |
21 | public string AssemblyPath { get; private set; }
22 | private void Initialize()
23 | {
24 | List presetName = new List();
25 |
26 | // CeVIO CS7 を探す
27 | string cevioPath = Environment.ExpandEnvironmentVariables("%ProgramW6432%")
28 | + @"\CeVIO\CeVIO Creative Studio (64bit)";
29 | string cevio32Path = Environment.ExpandEnvironmentVariables("%ProgramW6432%")
30 | + @"\CeVIO\CeVIO Creative Studio";
31 | _installedPath = cevioPath + @"\CeVIO Creative Studio.exe";
32 |
33 | if (Directory.Exists(cevioPath) && File.Exists(_installedPath))
34 | {
35 | AssemblyPath = cevioPath + @"\CeVIO.Talk.RemoteService.dll";
36 | // CeVIOを起動せずにインストールされた音源一覧を取得する
37 | string[] talkDirectory = Directory.GetDirectories(Path.Combine(cevioPath, @"Configuration\VocalSource\Talk"));
38 | foreach (var d in talkDirectory)
39 | {
40 | string config = Path.Combine(d, "setting.cfg");
41 | if (File.Exists(config))
42 | {
43 | var xml = XDocument.Load(config);
44 | var doc = xml.Element("VocalSource");
45 | string name = doc.Attribute("Name").Value;
46 | presetName.Add(name);
47 | }
48 | }
49 | // IA/ONEはフォルダが異なる
50 | // https://github.com/ksasao/TTSController/issues/11
51 | string[] talkDirectoryIAONE = Directory.GetDirectories(Path.Combine(cevio32Path, @"Configuration\VocalSource\Talk"));
52 | foreach (var d in talkDirectoryIAONE)
53 | {
54 | string config = Path.Combine(d, "setting.cfg");
55 | if (File.Exists(config))
56 | {
57 | var xml = XDocument.Load(config);
58 | var doc = xml.Element("VocalSource");
59 | string name = doc.Attribute("Name").Value;
60 | presetName.Add(name);
61 | }
62 | }
63 | }
64 |
65 | _name = presetName.ToArray();
66 | }
67 | public SpeechEngineInfo[] GetSpeechEngineInfo()
68 | {
69 | List info = new List();
70 | foreach (var v in _name)
71 | {
72 | info.Add(new SpeechEngineInfo {
73 | EngineName = EngineName,
74 | EnginePath = _installedPath,
75 | LibraryName = v,
76 | Is64BitProcess = true
77 | });
78 | }
79 | return info.ToArray();
80 | }
81 |
82 | public ISpeechController GetControllerInstance(SpeechEngineInfo info)
83 | {
84 | return EngineName == info.EngineName ? new CeVIO64Controller(info) : null;
85 | }
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/src/Speech/Controller/CeVIOAIController.cs:
--------------------------------------------------------------------------------
1 | using Codeer.Friendly;
2 | using Codeer.Friendly.Windows;
3 | using Codeer.Friendly.Windows.Grasp;
4 | using RM.Friendly.WPFStandardControls;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.ComponentModel;
8 | using System.Diagnostics;
9 | using System.IO;
10 | using System.Linq;
11 | using System.Reflection;
12 | using System.Runtime.InteropServices;
13 | using System.Text;
14 | using System.Threading;
15 | using System.Threading.Tasks;
16 | using System.Windows.Forms;
17 | using System.Windows.Media.Animation;
18 | using System.Windows.Threading;
19 |
20 | namespace Speech
21 | {
22 | ///
23 | /// CeVIO 操作クラス
24 | ///
25 | public class CeVIOAIController : IDisposable, ISpeechController
26 | {
27 | public SpeechEngineInfo Info { get; private set; }
28 |
29 | string _libraryName;
30 | dynamic _talker = null;
31 | Assembly _assembly;
32 | Type _serviceControl;
33 |
34 | CeVIOAIEnumerator _cevio;
35 |
36 | public CeVIOAIController(SpeechEngineInfo info)
37 | {
38 | Info = info;
39 |
40 | _cevio = new CeVIOAIEnumerator();
41 | _libraryName = info.LibraryName;
42 | }
43 |
44 | ///
45 | /// 音声再生が完了したときに発生するイベント
46 | ///
47 | public event EventHandler Finished;
48 | protected virtual void OnFinished()
49 | {
50 | EventArgs se = new EventArgs();
51 | Finished?.Invoke(this, se);
52 | }
53 |
54 | ///
55 | /// CeVIO が起動中かどうかを確認
56 | ///
57 | /// 起動中であれば true
58 | public bool IsActive()
59 | {
60 | string name = Path.GetFileNameWithoutExtension(Info.EnginePath);
61 | Process[] localByName = Process.GetProcessesByName(name);
62 |
63 | if (localByName.Length > 0)
64 | {
65 | return true;
66 | }
67 | return false;
68 | }
69 |
70 | ///
71 | /// CeVIO を起動する。すでに起動している場合には起動しているものを操作対象とする。
72 | ///
73 | public void Activate()
74 | {
75 | _assembly = Assembly.LoadFrom(_cevio.AssemblyPath);
76 | _serviceControl = _assembly.GetType("CeVIO.Talk.RemoteService2.ServiceControl2");
77 |
78 | //// 【CeVIO AI】起動
79 | //ServiceControl.StartHost(false);
80 | MethodInfo startHost = _serviceControl.GetMethod("StartHost");
81 | startHost.Invoke(null, new object[] { false });
82 |
83 | _talker = Activator.CreateInstance(_assembly.GetType("CeVIO.Talk.RemoteService2.Talker2"), new object[] { Info.LibraryName });
84 | }
85 |
86 | ///
87 | /// 指定した文字列を再生します
88 | ///
89 | /// 再生する文字列
90 | public void Play(string text)
91 | {
92 | var state = _talker.Speak(text);
93 | state.Wait();
94 | OnFinished();
95 | }
96 |
97 | ///
98 | /// このメソッドは無効です。発話する文字列を指定してください。
99 | ///
100 | public void Play()
101 | {
102 | }
103 | ///
104 | /// 再生を停止します
105 | ///
106 | public void Stop()
107 | {
108 | }
109 |
110 | enum EffectType { Volume = 0, Speed = 1, Pitch = 2, PitchRange = 3}
111 | ///
112 | /// 音量を設定します
113 | ///
114 | /// 0.0~2.0
115 | public void SetVolume(float value)
116 | {
117 | if (value > 2)
118 | {
119 | value = 2;
120 | }
121 | else if (value < 0)
122 | {
123 | value = 0;
124 | }
125 | _talker.Volume = (uint)(value * 50);
126 | }
127 | ///
128 | /// 音量を取得します
129 | ///
130 | /// 音量
131 | public float GetVolume()
132 | {
133 | return _talker.Volume / 50f;
134 | }
135 | ///
136 | /// 話速を設定します
137 | ///
138 | /// 0.5~4.0
139 | public void SetSpeed(float value)
140 | {
141 | if (value > 2)
142 | {
143 | value = 2;
144 | }
145 | else if (value < 0)
146 | {
147 | value = 0;
148 | }
149 | _talker.Speed = (uint)(value * 50);
150 | }
151 | ///
152 | /// 話速を取得します
153 | ///
154 | /// 話速
155 | public float GetSpeed()
156 | {
157 | return _talker.Speed / 50f;
158 | }
159 |
160 | ///
161 | /// 高さを設定します
162 | ///
163 | /// 0.5~2.0
164 | public void SetPitch(float value)
165 | {
166 | if (value > 2)
167 | {
168 | value = 2;
169 | }
170 | else if (value < 0)
171 | {
172 | value = 0;
173 | }
174 | _talker.Tone = (uint)(value * 50);
175 | }
176 | ///
177 | /// 高さを取得します
178 | ///
179 | /// 高さ
180 | public float GetPitch()
181 | {
182 | return _talker.Tone / 50f;
183 | }
184 | ///
185 | /// 抑揚を設定します
186 | ///
187 | /// 0.0~2.0
188 | public void SetPitchRange(float value)
189 | {
190 | if (value > 2)
191 | {
192 | value = 2;
193 | }
194 | else if (value < 0)
195 | {
196 | value = 0;
197 | }
198 | _talker.ToneScale = (uint)(value * 50);
199 | }
200 | ///
201 | /// 抑揚を取得します
202 | ///
203 | /// 抑揚
204 | public float GetPitchRange()
205 | {
206 | return _talker.ToneScale / 50f;
207 | }
208 |
209 | ///
210 | /// 声色を設定します
211 | ///
212 | /// パラメータ名
213 | /// 0~100
214 | public void SetVoiceParam(string Name, uint value)
215 | {
216 | if (value > 100)
217 | {
218 | value = 100;
219 | }
220 | else if (value < 0)
221 | {
222 | value = 0;
223 | }
224 | _talker.Components.ByName(Name).Value = (uint)(value);
225 | }
226 | ///
227 | /// 声色を取得します
228 | ///
229 | /// パラメータ名
230 | /// パラメータ値
231 | public uint GetVoiceParam(string Name)
232 | {
233 | return _talker.Components.ByName(Name).Value;
234 | }
235 |
236 | ///
237 | /// 声質を設定します
238 | ///
239 | /// 0.0~100.0
240 | public void SetVoiceQuality(uint value)
241 | {
242 | if (value > 100)
243 | {
244 | value = 100;
245 | }
246 | else if (value < 0)
247 | {
248 | value = 0;
249 | }
250 | _talker.Alpha = (uint)(value);
251 | }
252 | ///
253 | /// 声質を取得します
254 | ///
255 | /// パラメータ名
256 | /// パラメータ値
257 | public uint GetVoiceQuality()
258 | {
259 | return _talker.Alpha;
260 | }
261 |
262 |
263 | #region IDisposable Support
264 | private bool disposedValue = false;
265 |
266 | protected virtual void Dispose(bool disposing)
267 | {
268 | if (!disposedValue)
269 | {
270 | if (disposing)
271 | {
272 | // CeVIO を終了する場合はコメントを外す
273 | //MethodInfo closeHost = _serviceControl.GetMethod("CloseHost");
274 | //var hostCloseMode = _assembly.GetType("CeVIO.Talk.RemoteService2.HostCloseMode");
275 | //var mode = Enum.Parse(hostCloseMode, "Interrupt"); // Default, Interrupt, NotCancelable
276 | //closeHost.Invoke(null, new object[] { mode });
277 | }
278 | disposedValue = true;
279 | }
280 | }
281 |
282 | public void Dispose()
283 | {
284 | Dispose(true);
285 | }
286 | #endregion
287 | }
288 | }
--------------------------------------------------------------------------------
/src/Speech/Controller/CeVIOAIEnumerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Xml.Linq;
8 |
9 | namespace Speech
10 | {
11 | public class CeVIOAIEnumerator : ISpeechEnumerator
12 | {
13 | string[] _name = new string[0];
14 | string _installedPath = "";
15 | public const string EngineName = "CeVIOAI";
16 | public CeVIOAIEnumerator()
17 | {
18 | Initialize();
19 | }
20 |
21 | public string AssemblyPath { get; private set; }
22 | private void Initialize()
23 | {
24 | List presetName = new List();
25 |
26 | // CeVIO AI を探す
27 | string cevioPath = Environment.ExpandEnvironmentVariables("%ProgramW6432%")
28 | + @"\CeVIO\CeVIO AI\";
29 | _installedPath = cevioPath + @"\CeVIO AI.exe";
30 |
31 | if (Directory.Exists(cevioPath) && File.Exists(_installedPath))
32 | {
33 | AssemblyPath = cevioPath + @"\CeVIO.Talk.RemoteService2.dll";
34 | // CeVIOを起動せずにインストールされた音源一覧を取得する
35 | string[] talkDirectory = Directory.GetDirectories(Path.Combine(cevioPath, @"Configuration\VocalSource\Talk"));
36 | foreach (var d in talkDirectory)
37 | {
38 | string config = Path.Combine(d, "setting.cfg");
39 | if (File.Exists(config))
40 | {
41 | var xml = XDocument.Load(config);
42 | var doc = xml.Element("VocalSource");
43 | string name = doc.Attribute("Name").Value;
44 | // see https://github.com/ksasao/TTSController/issues/5
45 | if (name.IndexOf("Tsurumaki Maki (EN)") >= 0)
46 | {
47 | name = "弦巻マキ (英)";
48 | }
49 | presetName.Add(name);
50 | }
51 | }
52 | }
53 |
54 | _name = presetName.ToArray();
55 | }
56 | public SpeechEngineInfo[] GetSpeechEngineInfo()
57 | {
58 | List info = new List();
59 | foreach (var v in _name)
60 | {
61 | info.Add(new SpeechEngineInfo { EngineName = EngineName, EnginePath = _installedPath, LibraryName = v, Is64BitProcess = true });
62 | }
63 | return info.ToArray();
64 | }
65 |
66 | public ISpeechController GetControllerInstance(SpeechEngineInfo info)
67 | {
68 | return EngineName == info.EngineName ? new CeVIOAIController(info) : null;
69 | }
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/src/Speech/Controller/CeVIOController.cs:
--------------------------------------------------------------------------------
1 | using Codeer.Friendly;
2 | using Codeer.Friendly.Windows;
3 | using Codeer.Friendly.Windows.Grasp;
4 | using RM.Friendly.WPFStandardControls;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.ComponentModel;
8 | using System.Diagnostics;
9 | using System.IO;
10 | using System.Linq;
11 | using System.Reflection;
12 | using System.Runtime.InteropServices;
13 | using System.Text;
14 | using System.Threading;
15 | using System.Threading.Tasks;
16 | using System.Windows.Forms;
17 | using System.Windows.Media.Animation;
18 | using System.Windows.Threading;
19 |
20 | namespace Speech
21 | {
22 | ///
23 | /// CeVIO 操作クラス
24 | ///
25 | public class CeVIOController : IDisposable, ISpeechController
26 | {
27 | public SpeechEngineInfo Info { get; private set; }
28 |
29 | string _libraryName;
30 | dynamic _talker = null;
31 | Assembly _assembly;
32 | Type _serviceControl;
33 |
34 | CeVIOEnumerator _cevio;
35 |
36 | public CeVIOController(SpeechEngineInfo info)
37 | {
38 | Info = info;
39 |
40 | _cevio = new CeVIOEnumerator();
41 | _libraryName = info.LibraryName;
42 | }
43 |
44 | ///
45 | /// 音声再生が完了したときに発生するイベント
46 | ///
47 | public event EventHandler Finished;
48 | protected virtual void OnFinished()
49 | {
50 | EventArgs se = new EventArgs();
51 | Finished?.Invoke(this, se);
52 | }
53 |
54 | ///
55 | /// CeVIO が起動中かどうかを確認
56 | ///
57 | /// 起動中であれば true
58 | public bool IsActive()
59 | {
60 | string name = Path.GetFileNameWithoutExtension(Info.EnginePath);
61 | Process[] localByName = Process.GetProcessesByName(name);
62 |
63 | if (localByName.Length > 0)
64 | {
65 | return true;
66 | }
67 | return false;
68 | }
69 |
70 | ///
71 | /// CeVIO を起動する。すでに起動している場合には起動しているものを操作対象とする。
72 | ///
73 | public void Activate()
74 | {
75 | _assembly = Assembly.LoadFrom(_cevio.AssemblyPath);
76 | _serviceControl = _assembly.GetType("CeVIO.Talk.RemoteService.ServiceControl");
77 |
78 | //// 【CeVIO Creative Studio】起動
79 | //ServiceControl.StartHost(false);
80 | MethodInfo startHost = _serviceControl.GetMethod("StartHost");
81 | startHost.Invoke(null, new object[] { false });
82 |
83 | _talker = Activator.CreateInstance(_assembly.GetType("CeVIO.Talk.RemoteService.Talker"), new object[] { Info.LibraryName });
84 | }
85 |
86 | ///
87 | /// 指定した文字列を再生します
88 | ///
89 | /// 再生する文字列
90 | public void Play(string text)
91 | {
92 | var state = _talker.Speak(text);
93 | state.Wait();
94 | OnFinished();
95 | }
96 |
97 | ///
98 | /// このメソッドは無効です。発話する文字列を指定してください。
99 | ///
100 | public void Play()
101 | {
102 | }
103 | ///
104 | /// 再生を停止します
105 | ///
106 | public void Stop()
107 | {
108 | }
109 |
110 | enum EffectType { Volume = 0, Speed = 1, Pitch = 2, PitchRange = 3}
111 | ///
112 | /// 音量を設定します
113 | ///
114 | /// 0.0~2.0
115 | public void SetVolume(float value)
116 | {
117 | if (value > 2)
118 | {
119 | value = 2;
120 | }
121 | else if (value < 0)
122 | {
123 | value = 0;
124 | }
125 | _talker.Volume = (uint)(value * 50);
126 | }
127 | ///
128 | /// 音量を取得します
129 | ///
130 | /// 音量
131 | public float GetVolume()
132 | {
133 | return _talker.Volume / 50f;
134 | }
135 | ///
136 | /// 話速を設定します
137 | ///
138 | /// 0.5~4.0
139 | public void SetSpeed(float value)
140 | {
141 | if (value > 2)
142 | {
143 | value = 2;
144 | }
145 | else if (value < 0)
146 | {
147 | value = 0;
148 | }
149 | _talker.Speed = (uint)(value * 50);
150 | }
151 | ///
152 | /// 話速を取得します
153 | ///
154 | /// 話速
155 | public float GetSpeed()
156 | {
157 | return _talker.Speed / 50f;
158 | }
159 |
160 | ///
161 | /// 高さを設定します
162 | ///
163 | /// 0.5~2.0
164 | public void SetPitch(float value)
165 | {
166 | if (value > 2)
167 | {
168 | value = 2;
169 | }
170 | else if (value < 0)
171 | {
172 | value = 0;
173 | }
174 | _talker.Tone = (uint)(value * 50);
175 | }
176 | ///
177 | /// 高さを取得します
178 | ///
179 | /// 高さ
180 | public float GetPitch()
181 | {
182 | return _talker.Tone / 50f;
183 | }
184 | ///
185 | /// 抑揚を設定します
186 | ///
187 | /// 0.0~2.0
188 | public void SetPitchRange(float value)
189 | {
190 | if (value > 2)
191 | {
192 | value = 2;
193 | }
194 | else if (value < 0)
195 | {
196 | value = 0;
197 | }
198 | _talker.ToneScale = (uint)(value * 50);
199 | }
200 | ///
201 | /// 抑揚を取得します
202 | ///
203 | /// 抑揚
204 | public float GetPitchRange()
205 | {
206 | return _talker.ToneScale / 50f;
207 | }
208 |
209 | ///
210 | /// 声色を設定します
211 | ///
212 | /// パラメータ名
213 | /// 0~100
214 | public void SetVoiceParam(string Name, uint value)
215 | {
216 | if (value > 100)
217 | {
218 | value = 100;
219 | }
220 | else if (value < 0)
221 | {
222 | value = 0;
223 | }
224 | _talker.Components.ByName(Name).Value = (uint)(value);
225 | }
226 | ///
227 | /// 声色を取得します
228 | ///
229 | /// パラメータ名
230 | /// パラメータ値
231 | public uint GetVoiceParam(string Name)
232 | {
233 | return _talker.Components.ByName(Name).Value;
234 | }
235 | ///
236 | /// 声質を設定します
237 | ///
238 | /// 0.0~100.0
239 | public void SetVoiceQuality(uint value)
240 | {
241 | if (value > 100)
242 | {
243 | value = 100;
244 | }
245 | else if (value < 0)
246 | {
247 | value = 0;
248 | }
249 | _talker.Alpha = (uint)(value);
250 | }
251 | ///
252 | /// 声質を取得します
253 | ///
254 | /// パラメータ名
255 | /// パラメータ値
256 | public uint GetVoiceQuality()
257 | {
258 | return _talker.Alpha;
259 | }
260 |
261 | #region IDisposable Support
262 | private bool disposedValue = false;
263 |
264 | protected virtual void Dispose(bool disposing)
265 | {
266 | if (!disposedValue)
267 | {
268 | if (disposing)
269 | {
270 | // CeVIO を終了する場合はコメントを外す
271 | //MethodInfo closeHost = _serviceControl.GetMethod("CloseHost");
272 | //var hostCloseMode = _assembly.GetType("CeVIO.Talk.RemoteService.HostCloseMode");
273 | //var mode = Enum.Parse(hostCloseMode, "Interrupt"); // Default, Interrupt, NotCancelable
274 | //closeHost.Invoke(null, new object[] { mode });
275 | }
276 | disposedValue = true;
277 | }
278 | }
279 |
280 | public void Dispose()
281 | {
282 | Dispose(true);
283 | }
284 | #endregion
285 | }
286 | }
--------------------------------------------------------------------------------
/src/Speech/Controller/CeVIOEnumerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Xml.Linq;
8 |
9 | namespace Speech
10 | {
11 | public class CeVIOEnumerator : ISpeechEnumerator
12 | {
13 | string[] _name = new string[0];
14 | string _installedPath = "";
15 | public const string EngineName = "CeVIO";
16 | public CeVIOEnumerator()
17 | {
18 | Initialize();
19 | }
20 |
21 | public string AssemblyPath { get; private set; }
22 | private void Initialize()
23 | {
24 | List presetName = new List();
25 |
26 | // CeVIO CS6 以前(32bit) を探す
27 | string cevioPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)
28 | + @"\CeVIO\CeVIO Creative Studio";
29 | _installedPath = cevioPath + @"\CeVIO Creative Studio.exe";
30 |
31 | if (Directory.Exists(cevioPath) && File.Exists(_installedPath))
32 | {
33 | AssemblyPath = cevioPath + @"\CeVIO.Talk.RemoteService.dll";
34 | // CeVIOを起動せずにインストールされた音源一覧を取得する
35 | string[] talkDirectory = Directory.GetDirectories(Path.Combine(cevioPath, @"Configuration\VocalSource\Talk"));
36 | foreach (var d in talkDirectory)
37 | {
38 | string config = Path.Combine(d, "setting.cfg");
39 | if (File.Exists(config))
40 | {
41 | var xml = XDocument.Load(config);
42 | var doc = xml.Element("VocalSource");
43 | string name = doc.Attribute("Name").Value;
44 | presetName.Add(name);
45 | }
46 | }
47 | }
48 |
49 | _name = presetName.ToArray();
50 | }
51 | public SpeechEngineInfo[] GetSpeechEngineInfo()
52 | {
53 | List info = new List();
54 | foreach (var v in _name)
55 | {
56 | info.Add(new SpeechEngineInfo {
57 | EngineName = EngineName,
58 | EnginePath = _installedPath,
59 | LibraryName = v,
60 | Is64BitProcess = false});
61 | }
62 | return info.ToArray();
63 | }
64 |
65 | public ISpeechController GetControllerInstance(SpeechEngineInfo info)
66 | {
67 | return EngineName == info.EngineName ? new CeVIOController(info) : null;
68 | }
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/Speech/Controller/GynoidTalkController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Speech
8 | {
9 | public class GynoidTalkController : Voiceroid2Controller
10 | {
11 | public GynoidTalkController(SpeechEngineInfo info) : base(info)
12 | {
13 | }
14 |
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Speech/Controller/GynoidTalkEnumerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Xml.Linq;
8 |
9 | namespace Speech
10 | {
11 | class GynoidTalkEnumerator : Voiceroid2Enumerator
12 | {
13 | public GynoidTalkEnumerator()
14 | {
15 | // ガイノイドトークの一覧は下記で取得できる
16 | // 下記ファイルはガイノイドトーク終了時に生成されるため、一度 ガイノイドトークを起動・終了
17 | // しておくこと
18 | string path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
19 | + @"\Gynoid\GynoidTalk\1.0\Standard.settings";
20 | Initialize(path, "GynoidTalk");
21 | }
22 |
23 | internal override string GetInstalledPath()
24 | {
25 | string uninstall_path = @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\";
26 | // 32bit の場合 SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
27 |
28 | string result = "";
29 | Microsoft.Win32.RegistryKey uninstall = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(uninstall_path, false);
30 | if (uninstall != null)
31 | {
32 | foreach (string subKey in uninstall.GetSubKeyNames())
33 | {
34 | Microsoft.Win32.RegistryKey appkey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(uninstall_path + "\\" + subKey, false);
35 | var key = appkey.GetValue("DisplayName");
36 | if (key != null && key.ToString() == "ガイノイドTalk Editor")
37 | {
38 | var location = appkey.GetValue("InstallLocation").ToString();
39 | result = Path.Combine(location, @"GynoidTalkEditor.exe");
40 | break;
41 | }
42 | }
43 | }
44 | return result;
45 | }
46 |
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Speech/Controller/ISpeechController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Speech
8 | {
9 | public interface ISpeechController
10 | {
11 | ///
12 | /// 音声合成エンジンの情報を取得します
13 | ///
14 | SpeechEngineInfo Info { get; }
15 | event EventHandler Finished;
16 | ///
17 | /// 音声合成エンジンを有効化します
18 | ///
19 | void Activate();
20 | ///
21 | /// 音声合成エンジンに設定済みのテキストを再生します
22 | ///
23 | void Play();
24 | ///
25 | /// 文字列を再生します
26 | ///
27 | /// 再生する文字列
28 | void Play(string text);
29 | ///
30 | /// 再生を停止します
31 | ///
32 | void Stop();
33 | void Dispose();
34 | ///
35 | /// 音量を取得します
36 | ///
37 | /// 取得した音量
38 | float GetVolume();
39 | ///
40 | /// 音量を設定します
41 | ///
42 | /// 設定する音量
43 | void SetVolume(float value);
44 | ///
45 | /// 話速を取得します
46 | ///
47 | /// 取得した話速
48 | float GetSpeed();
49 | ///
50 | /// 話速を設定します
51 | ///
52 | /// 設定する話速
53 | void SetSpeed(float value);
54 | ///
55 | /// 高さを取得します
56 | ///
57 | /// 取得した高さ
58 | float GetPitch();
59 | ///
60 | /// 高さを設定します
61 | ///
62 | /// 設定する高さ
63 | void SetPitch(float value);
64 | ///
65 | /// 抑揚を取得します
66 | ///
67 | /// 設定する抑揚
68 | float GetPitchRange();
69 | ///
70 | /// 抑揚を設定します
71 | ///
72 | /// 設定する抑揚
73 | void SetPitchRange(float value);
74 | ///
75 | /// アプリケーションが起動中かどうかを取得します
76 | ///
77 | /// 起動していれば true
78 | bool IsActive();
79 |
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/Speech/Controller/ISpeechEnumerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Speech
8 | {
9 | interface ISpeechEnumerator
10 | {
11 | SpeechEngineInfo[] GetSpeechEngineInfo();
12 | ISpeechController GetControllerInstance(SpeechEngineInfo info);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Speech/Controller/OtomachiUnaTalkController.cs:
--------------------------------------------------------------------------------
1 | using Codeer.Friendly;
2 | using Codeer.Friendly.Windows;
3 | using Codeer.Friendly.Windows.Grasp;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.ComponentModel;
7 | using System.Diagnostics;
8 | using System.IO;
9 | using System.Linq;
10 | using System.Runtime.InteropServices;
11 | using System.Text;
12 | using System.Threading.Tasks;
13 | using System.Windows.Forms;
14 | using System.Windows.Threading;
15 |
16 | namespace Speech
17 | {
18 | ///
19 | /// 音街ウナTalk Ex 操作クラス
20 | ///
21 | public class OtomachiUnaTalkController : IDisposable, ISpeechController
22 | {
23 | WindowsAppFriend _app;
24 | Process _process;
25 | WindowControl _root;
26 | System.Timers.Timer _timer; // 状態監視のためのタイマー
27 | bool _playStarting = false;
28 |
29 | [DllImport("User32.dll")]
30 | static extern int SetForegroundWindow(IntPtr hWnd);
31 | [DllImport("user32.dll", CharSet = CharSet.Auto)]
32 | private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
33 |
34 |
35 | ///
36 | /// 音街ウナTalk Ex のフルパス
37 | ///
38 | public string OtomachiUnaPath { get; private set; }
39 |
40 | public SpeechEngineInfo Info { get; private set; }
41 |
42 | public OtomachiUnaTalkController(SpeechEngineInfo info)
43 | {
44 | Info = info;
45 | OtomachiUnaPath = info.EnginePath;
46 | _timer = new System.Timers.Timer(100);
47 | _timer.Elapsed += timer_Elapsed;
48 | }
49 |
50 | private void timer_Elapsed(object sender, EventArgs e)
51 | {
52 | WindowControl playButton = _root.IdentifyFromZIndex(2, 0, 0, 1, 0, 1, 0, 3);
53 | AppVar button = playButton.AppVar;
54 | string text = (string)button["Text"]().Core;
55 | if (!_playStarting && text.Trim() == "再生")
56 | {
57 | _timer.Stop();
58 | OnFinished();
59 | }
60 | _playStarting = false;
61 | }
62 |
63 | ///
64 | /// 音声再生が完了したときに発生するイベント
65 | ///
66 | public event EventHandler Finished;
67 | protected virtual void OnFinished()
68 | {
69 | EventArgs se = new EventArgs();
70 | Finished?.Invoke(this, se);
71 | }
72 |
73 | ///
74 | /// 起動中かどうかを確認
75 | ///
76 | /// 起動中であれば true
77 | public bool IsActive()
78 | {
79 | string name = Path.GetFileNameWithoutExtension(OtomachiUnaPath);
80 | Process[] localByName = Process.GetProcessesByName(name);
81 | foreach(var p in localByName)
82 | {
83 | if(p.MainModule.FileName == OtomachiUnaPath)
84 | {
85 | _process = p;
86 | return true;
87 | }
88 | }
89 | return false;
90 | }
91 |
92 | ///
93 | /// 起動する。すでに起動している場合には起動しているものを操作対象とする。
94 | ///
95 | public void Activate()
96 | {
97 | if (IsActive())
98 | {
99 | _app = new WindowsAppFriend(_process);
100 | }
101 | else
102 | {
103 | _process = Process.Start(OtomachiUnaPath);
104 | _app = new WindowsAppFriend(_process);
105 | }
106 | _root = WindowControl.GetTopLevelWindows(_app)[0];
107 | }
108 |
109 | ///
110 | /// 指定した文字列を再生します
111 | ///
112 | /// 再生する文字列
113 | public void Play(string text)
114 | {
115 | WindowControl speechTextBox = _root.IdentifyFromZIndex(2, 0, 0, 1, 0, 1, 1);
116 | AppVar textbox = speechTextBox.AppVar;
117 | textbox["Text"](text);
118 | Play();
119 | }
120 | ///
121 | /// 音街ウナTalk に入力された文字列を再生します
122 | ///
123 | public void Play()
124 | {
125 | WindowControl playButton = _root.IdentifyFromZIndex(2, 0, 0, 1, 0, 1, 0, 3);
126 | AppVar button = playButton.AppVar;
127 | string text = (string)button["Text"]().Core;
128 | if(text.Trim() == "再生")
129 | {
130 | button["PerformClick"]();
131 | _playStarting = true;
132 | _timer.Start();
133 | }
134 | }
135 | ///
136 | /// 音街ウナTalk の再生を停止します(停止ボタンを押す)
137 | ///
138 | public void Stop()
139 | {
140 | WindowControl stopButton = _root.IdentifyFromZIndex(2, 0, 0, 1, 0, 1, 0, 2);
141 | AppVar button = stopButton.AppVar;
142 | button["PerformClick"]();
143 | }
144 |
145 | enum EffectType { Volume = 8, Speed = 9, Pitch = 10, PitchRange = 11}
146 | ///
147 | /// 音量を設定します
148 | ///
149 | /// 0.0~2.0
150 | public void SetVolume(float value)
151 | {
152 | SetEffect(EffectType.Volume, value);
153 | }
154 | ///
155 | /// 音量を取得します
156 | ///
157 | /// 音量
158 | public float GetVolume()
159 | {
160 | return GetEffect(EffectType.Volume);
161 | }
162 | ///
163 | /// 話速を設定します
164 | ///
165 | /// 0.5~4.0
166 | public void SetSpeed(float value)
167 | {
168 | SetEffect(EffectType.Speed,value);
169 | }
170 | ///
171 | /// 話速を取得します
172 | ///
173 | /// 話速
174 | public float GetSpeed()
175 | {
176 | return GetEffect(EffectType.Speed);
177 | }
178 |
179 | ///
180 | /// 高さを設定します
181 | ///
182 | /// 0.5~2.0
183 | public void SetPitch(float value)
184 | {
185 | SetEffect(EffectType.Pitch, value);
186 | ChangeToVoiceEffect();
187 | }
188 | ///
189 | /// 高さを取得します
190 | ///
191 | /// 高さ
192 | public float GetPitch()
193 | {
194 | return GetEffect(EffectType.Pitch);
195 | }
196 | ///
197 | /// 抑揚を設定します
198 | ///
199 | /// 0.0~2.0
200 | public void SetPitchRange(float value)
201 | {
202 | SetEffect(EffectType.PitchRange, value);
203 | }
204 | ///
205 | /// 抑揚を取得します
206 | ///
207 | /// 抑揚
208 | public float GetPitchRange()
209 | {
210 | return GetEffect(EffectType.PitchRange);
211 | }
212 |
213 | private void SetEffect(EffectType t, float value)
214 | {
215 | ChangeToVoiceEffect();
216 | int index = (int)t;
217 | WindowControl control = _root.IdentifyFromZIndex(2, 0, 0, 0, 0, 0, 0, index);
218 | AppVar v = control.AppVar;
219 | v["Focus"]();
220 | v["Text"](string.Format("{0:0.00}", value));
221 |
222 | // TODO: 音街ウナTalkでは数値を変更するだけでは変更が行われないため何らかの方法が必要
223 |
224 | }
225 | private float GetEffect(EffectType t)
226 | {
227 | ChangeToVoiceEffect();
228 | int index = (int)t;
229 | WindowControl control = _root.IdentifyFromZIndex(2, 0, 0, 0, 0, 0, 0, index);
230 | AppVar v = control.AppVar;
231 | return Convert.ToSingle((string)v["Text"]().Core);
232 | }
233 |
234 |
235 | ///
236 | /// 音声効果タブを選択します
237 | ///
238 | private void ChangeToVoiceEffect()
239 | {
240 | RestoreMinimizedWindow();
241 | WindowControl tabControl = _root.IdentifyFromZIndex(2, 0, 0, 0, 0);
242 | AppVar tab = tabControl.AppVar;
243 | tab["SelectedIndex"](2);
244 | }
245 | private void RestoreMinimizedWindow()
246 | {
247 | const uint WM_SYSCOMMAND = 0x0112;
248 | const int SC_RESTORE = 0xF120;
249 | FormWindowState state = (FormWindowState)_root["WindowState"]().Core;
250 | if (state == FormWindowState.Minimized)
251 | {
252 | SendMessage(_root.Handle, WM_SYSCOMMAND,
253 | new IntPtr(SC_RESTORE), IntPtr.Zero);
254 | }
255 | }
256 |
257 | #region IDisposable Support
258 | private bool disposedValue = false;
259 |
260 | protected virtual void Dispose(bool disposing)
261 | {
262 | if (!disposedValue)
263 | {
264 | if (disposing)
265 | {
266 | if(_app != null)
267 | {
268 | _app.Dispose();
269 | }
270 | }
271 | disposedValue = true;
272 | }
273 | }
274 |
275 | public void Dispose()
276 | {
277 | Dispose(true);
278 | }
279 | #endregion
280 | }
281 | }
--------------------------------------------------------------------------------
/src/Speech/Controller/OtomachiUnaTalkEnumerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Xml.Linq;
8 |
9 | namespace Speech
10 | {
11 |
12 | public class OtomachiUnaTalkEnumerator : ISpeechEnumerator
13 | {
14 |
15 | class Data
16 | {
17 | public string Name { get; internal set; }
18 | public string Path { get; internal set; }
19 | }
20 | Data[] _info;
21 | public const string EngineName = "OtomachiUna_Talk_Ex";
22 |
23 |
24 | public OtomachiUnaTalkEnumerator()
25 | {
26 | Initialize();
27 | }
28 | private void Initialize()
29 | {
30 | // 音街ウナTalkExのパス
31 | string path = System.Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)
32 | + @"\INTERNET Co.,Ltd\OtomachiUnaTalk Ex\";
33 | List data = new List();
34 | if (Directory.Exists(path))
35 | {
36 | Data d = new Data();
37 | d.Name = "音街ウナ";
38 | d.Path = Path.Combine(path, "OtomachiUnaTalkEx.exe");
39 | data.Add(d);
40 | }
41 | _info = data.ToArray();
42 | }
43 |
44 | public SpeechEngineInfo[] GetSpeechEngineInfo()
45 | {
46 | List info = new List();
47 | foreach (var v in _info)
48 | {
49 | info.Add(new SpeechEngineInfo { EngineName = EngineName, EnginePath = v.Path, LibraryName = v.Name });
50 | }
51 | return info.ToArray();
52 | }
53 | public ISpeechController GetControllerInstance(SpeechEngineInfo info)
54 | {
55 | return EngineName == info.EngineName ? new OtomachiUnaTalkController(info) : null;
56 | }
57 |
58 |
59 |
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Speech/Controller/SAPI5Controller.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Diagnostics;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Runtime.InteropServices;
8 | using System.Speech.Synthesis;
9 | using System.Text;
10 | using System.Threading;
11 | using System.Threading.Tasks;
12 | using System.Windows.Forms;
13 |
14 | namespace Speech
15 | {
16 | ///
17 | /// SAPI5 操作クラス
18 | ///
19 | public class SAPI5Controller : IDisposable, ISpeechController
20 | {
21 | SpeechSynthesizer synthesizer = null;
22 | string _voiceName;
23 | string _lastText="";
24 | public SpeechEngineInfo Info { get; private set; }
25 |
26 | ///
27 | /// 音声再生が完了したときに発生するイベント
28 | ///
29 | public event EventHandler Finished;
30 | protected virtual void OnFinished()
31 | {
32 | EventArgs se = new EventArgs();
33 | Finished?.Invoke(this, se);
34 | }
35 |
36 | public SAPI5Controller(SpeechEngineInfo info)
37 | {
38 | Info = info;
39 | _voiceName = info.LibraryName;
40 | }
41 |
42 | ///
43 | /// 音声合成が有効かどうかをチェックする
44 | ///
45 | /// 起動中であれば true
46 | public bool IsActive()
47 | {
48 | return synthesizer != null;
49 | }
50 |
51 | ///
52 | /// SAPI5を起動する
53 | ///
54 | public void Activate()
55 | {
56 | if (!IsActive())
57 | {
58 | synthesizer = new SpeechSynthesizer();
59 | var voice = synthesizer.GetInstalledVoices();
60 | for (int i = 0; i < voice.Count; i++)
61 | {
62 | var v = voice[i].VoiceInfo.Name;
63 | if (v.IndexOf(_voiceName) >= 0)
64 | {
65 | synthesizer.SelectVoice(v);
66 | break;
67 | }
68 | }
69 | }
70 | }
71 |
72 | ///
73 | /// 指定した文字列を再生します
74 | ///
75 | /// 再生する文字列
76 | public async void Play(string text)
77 | {
78 | text = text ?? "";
79 | text = text.Trim();
80 | if (text == "")
81 | {
82 | OnFinished();
83 | return;
84 | }
85 | await SpeechAsync(text);
86 | }
87 |
88 | private async Task SpeechAsync(string text)
89 | {
90 | await Task.Run(() =>
91 | {
92 | _lastText = text;
93 | synthesizer.Speak(text);
94 | OnFinished();
95 | });
96 |
97 | }
98 | ///
99 | /// 最後に入力された文字列を再生します
100 | ///
101 | public void Play()
102 | {
103 | Play(_lastText);
104 | }
105 | ///
106 | /// 再生を停止します
107 | ///
108 | public void Stop()
109 | {
110 | // not implemented
111 | }
112 |
113 | ///
114 | /// 音量を設定します
115 | ///
116 | /// 0.0~2.0
117 | public void SetVolume(float value)
118 | {
119 | synthesizer.Volume = (int)(value * 100f);
120 | }
121 | ///
122 | /// 音量を取得します
123 | ///
124 | /// 音量(0.0~1.0)
125 | public float GetVolume()
126 | {
127 | return synthesizer.Volume / 100f; // SAPI5の音量は0-100で指定
128 | }
129 | ///
130 | /// 話速を設定します
131 | ///
132 | /// 0.5~4.0
133 | public void SetSpeed(float value)
134 | {
135 | synthesizer.Rate = (int)((value - 1f) * 10f);
136 | }
137 | ///
138 | /// 話速を取得します
139 | ///
140 | /// 話速
141 | public float GetSpeed()
142 | {
143 | return (synthesizer.Rate+10)/10f; //Rate: -10 ~ 10 (default:0)
144 | }
145 |
146 | ///
147 | /// 高さを設定します。SAPI5では無効です。
148 | ///
149 | /// 0.5~2.0
150 | public void SetPitch(float value)
151 | {
152 | // 何もしない
153 | }
154 | ///
155 | /// 高さを取得します。SAPI5では無効です。
156 | ///
157 | /// 高さ
158 | public float GetPitch()
159 | {
160 | return 1f;
161 | }
162 | ///
163 | /// 抑揚を設定します。SAPI5では無効です。
164 | ///
165 | /// 0.0~2.0
166 | public void SetPitchRange(float value)
167 | {
168 | // 何もしない
169 | }
170 | ///
171 | /// 抑揚を取得します。SAPI5では無効です。
172 | ///
173 | /// 抑揚
174 | public float GetPitchRange()
175 | {
176 | return 1f;
177 | }
178 |
179 | #region IDisposable Support
180 | private bool disposedValue = false;
181 |
182 | protected virtual void Dispose(bool disposing)
183 | {
184 | if (!disposedValue)
185 | {
186 | if (disposing)
187 | {
188 | if(synthesizer != null)
189 | {
190 | while(synthesizer.State == SynthesizerState.Speaking)
191 | {
192 | Thread.Sleep(100);
193 | }
194 | synthesizer.Dispose();
195 | }
196 | }
197 | disposedValue = true;
198 | }
199 | }
200 |
201 | public void Dispose()
202 | {
203 | Dispose(true);
204 | }
205 | #endregion
206 | }
207 | }
--------------------------------------------------------------------------------
/src/Speech/Controller/SAPI5Enumerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Speech.Synthesis;
7 |
8 | namespace Speech
9 | {
10 | public class SAPI5Enumerator : ISpeechEnumerator
11 | {
12 | class Data
13 | {
14 | public string Name { get; internal set; }
15 | public string Path { get; internal set; }
16 | }
17 | public const string EngineName = "SAPI5";
18 |
19 | Data[] _info;
20 |
21 | SpeechSynthesizer synthesizer = null;
22 | public SAPI5Enumerator()
23 | {
24 | Initialize();
25 | }
26 |
27 | private void Initialize()
28 | {
29 | List sapi5 = new List();
30 |
31 | synthesizer = new SpeechSynthesizer();
32 | var voice = synthesizer.GetInstalledVoices();
33 | for (int i = 0; i < voice.Count; i++)
34 | {
35 | var v = voice[i].VoiceInfo.Name;
36 | if (v.StartsWith("CeVIO"))
37 | {
38 | // CeVIOは 64bit Windows での SAPI経由での動作保証をしていないためスキップ
39 | // http://guide2.project-cevio.com/interface
40 | continue;
41 | }
42 | sapi5.Add(new Data { Name = v, Path = "" });
43 | }
44 | _info = sapi5.ToArray();
45 | }
46 | public SpeechEngineInfo[] GetSpeechEngineInfo()
47 | {
48 | List info = new List();
49 | foreach (var v in _info)
50 | {
51 | info.Add(new SpeechEngineInfo { EngineName = EngineName, EnginePath = v.Path, LibraryName = v.Name });
52 | }
53 | return info.ToArray();
54 | }
55 | public ISpeechController GetControllerInstance(SpeechEngineInfo info)
56 | {
57 | return EngineName == info.EngineName ? new SAPI5Controller(info) : null;
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Speech/Controller/SHAREVOXController.cs:
--------------------------------------------------------------------------------
1 | using Codeer.Friendly;
2 | using Codeer.Friendly.Windows;
3 | using Codeer.Friendly.Windows.Grasp;
4 | using RM.Friendly.WPFStandardControls;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.ComponentModel;
8 | using System.Diagnostics;
9 | using System.IO;
10 | using System.Linq;
11 | using System.Net;
12 | using System.Net.Http;
13 | using System.Reflection;
14 | using System.Runtime.InteropServices;
15 | using System.Text;
16 | using System.Threading;
17 | using System.Threading.Tasks;
18 | using System.Windows.Forms;
19 | using System.Windows.Media.Animation;
20 | using System.Windows.Threading;
21 |
22 | namespace Speech
23 | {
24 | ///
25 | /// COEIROINK 操作クラス
26 | ///
27 | public class SHAREVOXController : VOICEVOXController
28 | {
29 | public SHAREVOXController(SpeechEngineInfo info) :base(info)
30 | {
31 | Info = info;
32 | _enumerator = new SHAREVOXEnumerator();
33 | _baseUrl = _enumerator.BaseUrl;
34 | _libraryName = info.LibraryName;
35 | }
36 |
37 | }
38 | }
--------------------------------------------------------------------------------
/src/Speech/Controller/SHAREVOXEnumerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Net.Http;
7 | using System.Runtime.Serialization.Json;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 | using System.Xml.Linq;
11 |
12 | namespace Speech
13 | {
14 | public class SHAREVOXEnumerator : VOICEVOXEnumerator
15 | {
16 | public SHAREVOXEnumerator()
17 | {
18 | // https://github.com/SHAREVOX/sharevox_engine
19 | Initialize("SHAREVOX", "http://127.0.0.1:50025");
20 | }
21 | public override ISpeechController GetControllerInstance(SpeechEngineInfo info)
22 | {
23 | return EngineName == info.EngineName ? new SHAREVOXController(info) : null;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Speech/Controller/VOICEPEAKController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Diagnostics;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Runtime.InteropServices;
8 | using System.Text;
9 | using System.Threading;
10 | using System.Threading.Tasks;
11 |
12 |
13 | namespace Speech
14 | {
15 | public class VOICEPEAKController : IDisposable, ISpeechController
16 | {
17 |
18 | public SpeechEngineInfo Info { get; private set; }
19 |
20 | ///
21 | /// Voiceroid のフルパス
22 | ///
23 | public string VoiceroidPath { get; private set; }
24 |
25 | private string[] ExecuteVoicepeak(string args)
26 | {
27 | ProcessStartInfo psInfo = new ProcessStartInfo();
28 |
29 | psInfo.FileName = Info.EnginePath;
30 | psInfo.CreateNoWindow = true;
31 | psInfo.UseShellExecute = false;
32 | psInfo.RedirectStandardOutput = true;
33 | psInfo.Arguments = args;
34 |
35 | using (Process p = Process.Start(psInfo))
36 | {
37 | // Voicepeakは非同期実行されるのでプロセス終了後に標準出力を取り出す
38 | p.WaitForExit(10);
39 |
40 | // 行の整形
41 | string[] stdout = p.StandardOutput.ReadToEnd().Split('\n');
42 | string[] output = stdout.Where(x => x.Trim().Length > 0).Select(x => x.Trim()).ToArray();
43 | return output;
44 | }
45 | }
46 |
47 | public VOICEPEAKController(SpeechEngineInfo info)
48 | {
49 | Info = info;
50 | //emotions = ExecuteVoicepeak($"--list-emotion \"{info.LibraryName}\"");
51 | }
52 |
53 |
54 | ///
55 | /// 音声再生が完了したときに発生するイベント
56 | ///
57 | public event EventHandler Finished;
58 | protected virtual void OnFinished()
59 | {
60 | EventArgs se = new EventArgs();
61 | Finished?.Invoke(this, se);
62 | }
63 |
64 | ///
65 | /// 起動中かどうかを確認
66 | ///
67 | /// 起動中であれば true
68 | public bool IsActive()
69 | {
70 | // VOICEPEAK は起動しているかどうかは問わない設計となっているので常にtrue
71 | return true;
72 | }
73 |
74 | ///
75 | /// 音声合成エンジンを起動する。すでに起動している場合には起動しているものを操作対象とする。
76 | ///
77 | public void Activate()
78 | {
79 | // 明示的に起動する必要はないので何もしない
80 | }
81 |
82 | ///
83 | /// 指定した文字列を再生します
84 | ///
85 | /// 再生する文字列
86 | public void Play(string text)
87 | {
88 | ExecuteVoicepeak($"-n \"{Info.LibraryName}\" -s \"{text}\"");
89 | using (SoundPlayer soundPlayer = new SoundPlayer())
90 | {
91 | soundPlayer.Play("output.wav");
92 | }
93 | }
94 |
95 |
96 | ///
97 | /// VOICEROID2 に入力された文字列を再生します
98 | ///
99 | public void Play()
100 | {
101 |
102 | }
103 | ///
104 | /// 再生を停止します
105 | ///
106 | public void Stop()
107 | {
108 |
109 | }
110 |
111 | enum EffectType { Volume = 0, Speed = 1, Pitch = 2, PitchRange = 3 }
112 | ///
113 | /// 音量を設定します
114 | ///
115 | /// 0.0~2.0
116 | public void SetVolume(float value)
117 | {
118 | SetEffect(EffectType.Volume, value);
119 | }
120 | ///
121 | /// 音量を取得します
122 | ///
123 | /// 音量
124 | public float GetVolume()
125 | {
126 | return GetEffect(EffectType.Volume);
127 | }
128 | ///
129 | /// 話速を設定します
130 | ///
131 | /// 0.5~4.0
132 | public void SetSpeed(float value)
133 | {
134 | SetEffect(EffectType.Speed, value);
135 | }
136 | ///
137 | /// 話速を取得します
138 | ///
139 | /// 話速
140 | public float GetSpeed()
141 | {
142 | return GetEffect(EffectType.Speed);
143 | }
144 |
145 | ///
146 | /// 高さを設定します
147 | ///
148 | /// 0.5~2.0
149 | public void SetPitch(float value)
150 | {
151 | SetEffect(EffectType.Pitch, value);
152 | }
153 | ///
154 | /// 高さを取得します
155 | ///
156 | /// 高さ
157 | public float GetPitch()
158 | {
159 | return GetEffect(EffectType.Pitch);
160 | }
161 | ///
162 | /// 抑揚を設定します
163 | ///
164 | /// 0.0~2.0
165 | public void SetPitchRange(float value)
166 | {
167 | SetEffect(EffectType.PitchRange, value);
168 | }
169 | ///
170 | /// 抑揚を取得します
171 | ///
172 | /// 抑揚
173 | public float GetPitchRange()
174 | {
175 | return GetEffect(EffectType.PitchRange);
176 | }
177 |
178 | private void SetEffect(EffectType t, float value)
179 | {
180 | }
181 | private float GetEffect(EffectType t)
182 | {
183 | return 0;
184 | }
185 |
186 | #region IDisposable Support
187 | private bool disposedValue = false;
188 |
189 | protected virtual void Dispose(bool disposing)
190 | {
191 | if (!disposedValue)
192 | {
193 | // Disposable ではあるが、実際にリソースを握っているわけではないのでそのまま返す
194 | disposedValue = true;
195 | }
196 | }
197 |
198 | public void Dispose()
199 | {
200 | Dispose(true);
201 | }
202 | #endregion
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/src/Speech/Controller/VOICEPEAKEnumerator.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Win32;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace Speech
11 | {
12 | class VOICEPEAKEnumerator : ISpeechEnumerator
13 | {
14 | string path = "";
15 | const string EngineName = "VOICEPEAK";
16 |
17 | public VOICEPEAKEnumerator()
18 | {
19 | string[] files = GetInstalledPath();
20 | if (files.Length > 0)
21 | {
22 | path = files[0];
23 | }
24 | }
25 |
26 | private string[] ExecuteVoicepeak(string args)
27 | {
28 | ProcessStartInfo psInfo = new ProcessStartInfo();
29 |
30 | psInfo.FileName = path;
31 | psInfo.CreateNoWindow = true;
32 | psInfo.UseShellExecute = false;
33 | psInfo.RedirectStandardOutput = true;
34 | psInfo.Arguments = args;
35 |
36 | using (Process p = Process.Start(psInfo))
37 | {
38 | // Voicepeakは非同期実行されるのでプロセス終了後に標準出力を取り出す
39 | p.WaitForExit(10);
40 |
41 | // 行の整形
42 | string[] stdout = p.StandardOutput.ReadToEnd().Split('\n');
43 | string[] output = stdout.Where(x => x.Trim().Length > 0).Select(x => x.Trim()).ToArray();
44 | return output;
45 | }
46 | }
47 |
48 | private string[] GetInstalledPath()
49 | {
50 | List appPath = new List();
51 |
52 | // インストーラでインストールされた64bitアプリを列挙
53 | string regKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
54 | using (RegistryKey localMachine64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
55 | {
56 | RegistryKey subKey = localMachine64.OpenSubKey(regKey);
57 |
58 | // "voicepeak" を含むアプリケーションのみを抽出(6ナレーターとその他は別扱い)
59 | string[] names = subKey.GetSubKeyNames().Where(x => x.ToLower().IndexOf("voicepeak") >= 0).ToArray();
60 |
61 | foreach (string name in names)
62 | {
63 | using (RegistryKey appkey = subKey.OpenSubKey(name))
64 | {
65 | string path = appkey.GetValue("Inno Setup: App Path")?.ToString();
66 | // バージョンが 1.2.1以上のものを抽出
67 | if (path != null)
68 | {
69 | string version = appkey.GetValue("DisplayVersion")?.ToString();
70 | string[] vs = version.Split('.');
71 | if (vs.Length >= 3)
72 | {
73 | try
74 | {
75 | int v = int.Parse(vs[0]) * 100 * 100 + int.Parse(vs[1]) * 100 + int.Parse(vs[2]);
76 | if (v >= 1 * 100 * 100 + 2 * 100 + 1)
77 | {
78 | appPath.Add(Path.Combine(path, "voicepeak.exe"));
79 | }
80 | }
81 | catch (Exception)
82 | {
83 | // バージョン判定できないので追加しない
84 | }
85 | }
86 | }
87 |
88 | }
89 | }
90 |
91 | }
92 | return appPath.ToArray();
93 | }
94 |
95 | public SpeechEngineInfo[] GetSpeechEngineInfo()
96 | {
97 | List infoList = new List();
98 |
99 | string[] narrators = ExecuteVoicepeak("--list-narrator");
100 | foreach(var s in narrators)
101 | {
102 | SpeechEngineInfo info = new SpeechEngineInfo();
103 | info.EngineName = EngineName;
104 | info.EnginePath = path;
105 | info.LibraryName = s;
106 | info.Is64BitProcess = true;
107 | infoList.Add(info);
108 | }
109 | return infoList.ToArray();
110 | }
111 |
112 | public ISpeechController GetControllerInstance(SpeechEngineInfo info)
113 | {
114 | return EngineName == info.EngineName ? new VOICEPEAKController(info) : null;
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/Speech/Controller/VOICEVOXController.cs:
--------------------------------------------------------------------------------
1 | using Codeer.Friendly;
2 | using Codeer.Friendly.Windows;
3 | using Codeer.Friendly.Windows.Grasp;
4 | using RM.Friendly.WPFStandardControls;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.ComponentModel;
8 | using System.Diagnostics;
9 | using System.IO;
10 | using System.Linq;
11 | using System.Net;
12 | using System.Net.Http;
13 | using System.Reflection;
14 | using System.Runtime.InteropServices;
15 | using System.Text;
16 | using System.Text.RegularExpressions;
17 | using System.Threading;
18 | using System.Threading.Tasks;
19 | using System.Windows.Forms;
20 | using System.Windows.Media.Animation;
21 | using System.Windows.Threading;
22 |
23 | namespace Speech
24 | {
25 | ///
26 | /// VOICEVOX 操作クラス
27 | ///
28 | public class VOICEVOXController : IDisposable, ISpeechController
29 | {
30 | public SpeechEngineInfo Info { get; internal set; }
31 |
32 | internal string _libraryName;
33 | internal string _baseUrl;
34 |
35 | internal VOICEVOXEnumerator _enumerator;
36 | internal float Volume { get; set; } = 1.0f;
37 | internal float Speed { get; set; } = 1.0f;
38 | internal float Pitch { get; set; } = 0.0f;
39 | internal float Intonation { get; set; } = 1.0f;
40 |
41 | public VOICEVOXController(SpeechEngineInfo info)
42 | {
43 | Info = info;
44 | _enumerator = new VOICEVOXEnumerator();
45 | _baseUrl = _enumerator.BaseUrl;
46 | _libraryName = info.LibraryName;
47 | }
48 |
49 | ///
50 | /// 音声再生が完了したときに発生するイベント
51 | ///
52 | public event EventHandler Finished;
53 | protected virtual void OnFinished()
54 | {
55 | EventArgs se = new EventArgs();
56 | Finished?.Invoke(this, se);
57 | }
58 |
59 | ///
60 | /// VOICEVOX が起動中かどうかを確認
61 | ///
62 | /// 起動中であれば true
63 | public bool IsActive()
64 | {
65 | using (var client = new HttpClient())
66 | {
67 | var response = client.GetAsync($"{_baseUrl}/docs").GetAwaiter().GetResult();
68 | return (response.StatusCode == HttpStatusCode.OK);
69 | }
70 | }
71 |
72 | ///
73 | /// VOICEVOX を起動する。すでに起動している場合には起動しているものを操作対象とする。
74 | ///
75 | public void Activate()
76 | {
77 |
78 | }
79 |
80 | private string UpdateParam(string str)
81 | {
82 | str = ReplaceParam(str, "volumeScale", Volume);
83 | str = ReplaceParam(str,"speedScale", Speed);
84 | str = ReplaceParam(str, "intonationScale", Intonation);
85 | str = ReplaceParam(str, "pitchScale", Pitch);
86 | return str;
87 | }
88 |
89 | private string ReplaceParam(string str, string key, float value)
90 | {
91 | // "pitchScale":0.0,
92 | string result = Regex.Replace(str, $"{key}\"\\s*?:\\s*?[\\d\\.]+", $"{key}\":{value:F2}");
93 | return result;
94 | }
95 |
96 | ///
97 | /// 指定した文字列を再生します
98 | ///
99 | /// 再生する文字列
100 | public void Play(string text)
101 | {
102 | string tempFile = Path.GetTempFileName();
103 |
104 | var content = new StringContent("", Encoding.UTF8, @"application/json");
105 | var encodeText = Uri.EscapeDataString(text);
106 |
107 | int talkerNo = _enumerator.Names[_libraryName];
108 |
109 | string queryData = "";
110 | using (var client = new HttpClient())
111 | {
112 | try
113 | {
114 | var response = client.PostAsync($"{_baseUrl}/audio_query?text={encodeText}&speaker={talkerNo}", content).GetAwaiter().GetResult();
115 | if (response.StatusCode != HttpStatusCode.OK) { return; }
116 | queryData = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
117 |
118 | // 音量等のパラメータを反映させる
119 | queryData = UpdateParam(queryData);
120 |
121 | content = new StringContent(queryData, Encoding.UTF8, @"application/json");
122 | response = client.PostAsync($"{_baseUrl}/synthesis?speaker={talkerNo}", content).GetAwaiter().GetResult();
123 | if (response.StatusCode != HttpStatusCode.OK) { return; }
124 |
125 | var soundData = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult();
126 |
127 | using (var fileStream = new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.None))
128 | {
129 | soundData.CopyTo(fileStream);
130 |
131 | }
132 |
133 | SoundPlayer sp = new SoundPlayer();
134 | sp.Play(tempFile);
135 | }
136 | finally
137 | {
138 | OnFinished();
139 | }
140 | }
141 |
142 | }
143 |
144 | ///
145 | /// このメソッドは無効です。発話する文字列を指定してください。
146 | ///
147 | public void Play()
148 | {
149 | }
150 | ///
151 | /// 再生を停止します
152 | ///
153 | public void Stop()
154 | {
155 | }
156 |
157 | ///
158 | /// 音量を設定します
159 | ///
160 | /// 0.0~2.0
161 | public void SetVolume(float value)
162 | {
163 | Volume = value;
164 | }
165 | ///
166 | /// 音量を取得します
167 | ///
168 | /// 音量
169 | public float GetVolume()
170 | {
171 | return Volume;
172 | }
173 | ///
174 | /// 話速を設定します
175 | ///
176 | /// 0.5~4.0
177 | public void SetSpeed(float value)
178 | {
179 | Speed = value;
180 | }
181 | ///
182 | /// 話速を取得します
183 | ///
184 | /// 話速
185 | public float GetSpeed()
186 | {
187 | return Speed;
188 | }
189 |
190 | ///
191 | /// 高さを設定します
192 | ///
193 | /// 0.5~2.0
194 | public void SetPitch(float value)
195 | {
196 | Pitch = value;
197 | }
198 | ///
199 | /// 高さを取得します
200 | ///
201 | /// 高さ
202 | public float GetPitch()
203 | {
204 | return Pitch;
205 | }
206 | ///
207 | /// 抑揚を設定します
208 | ///
209 | /// 0.0~2.0
210 | public void SetPitchRange(float value)
211 | {
212 | Intonation = value;
213 | }
214 | ///
215 | /// 抑揚を取得します:この関数は無効です
216 | ///
217 | /// 抑揚
218 | public float GetPitchRange()
219 | {
220 | return Intonation;
221 | }
222 |
223 |
224 | #region IDisposable Support
225 | private bool disposedValue = false;
226 |
227 | protected virtual void Dispose(bool disposing)
228 | {
229 | if (!disposedValue)
230 | {
231 | if (disposing)
232 | {
233 |
234 | }
235 | disposedValue = true;
236 | }
237 | }
238 |
239 | public void Dispose()
240 | {
241 | Dispose(true);
242 | }
243 | #endregion
244 | }
245 | }
--------------------------------------------------------------------------------
/src/Speech/Controller/VOICEVOXEnumerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Net.Http;
7 | using System.Runtime.Serialization.Json;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 | using System.Xml.Linq;
11 |
12 | namespace Speech
13 | {
14 | [System.Runtime.Serialization.DataContract]
15 | class Speaker
16 | {
17 | [System.Runtime.Serialization.DataMember]
18 | public string name { get; set; }
19 | [System.Runtime.Serialization.DataMember]
20 | public string speaker_uuid { get; set; }
21 | [System.Runtime.Serialization.DataMember]
22 | public Style[] styles { get; set; }
23 | [System.Runtime.Serialization.DataMember]
24 | public string version { get; set; }
25 |
26 | }
27 | [System.Runtime.Serialization.DataContract]
28 | class Style
29 | {
30 | [System.Runtime.Serialization.DataMember]
31 | public int id { get; set; }
32 | [System.Runtime.Serialization.DataMember]
33 | public string name { get; set; }
34 | }
35 | public class VOICEVOXEnumerator : ISpeechEnumerator
36 | {
37 | string[] _name = new string[0];
38 | internal string BaseUrl;
39 | public string EngineName;
40 |
41 | public Dictionary Names = new Dictionary();
42 | public VOICEVOXEnumerator()
43 | {
44 | Initialize("VOICEVOX","http://127.0.0.1:50021");
45 | }
46 |
47 | public string AssemblyPath { get; private set; }
48 | internal void Initialize(string engineName,string baseUrl)
49 | {
50 | EngineName = engineName;
51 | BaseUrl = baseUrl;
52 | List presetName = new List();
53 | try
54 | {
55 | using (var client = new HttpClient())
56 | {
57 | client.Timeout = TimeSpan.FromSeconds(2);
58 | var response = client.GetAsync($"{baseUrl}/speakers").GetAwaiter().GetResult();
59 | if (response.StatusCode == HttpStatusCode.OK)
60 | {
61 | var json = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
62 | using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
63 | {
64 | var sr = new DataContractJsonSerializer(typeof(Speaker[]));
65 | var data = sr.ReadObject(ms) as Speaker[];
66 | foreach (var d in data)
67 | {
68 | presetName.Add(d.name);
69 | Names.Add(d.name, d.styles[0].id); // スタイル省略時は各話者の最初のIDを利用する
70 | for (int i = 1; i < d.styles.Length; i++) // スタイルは(スタイル名)とする
71 | {
72 | string styleName = $"{d.name}({d.styles[i].name})";
73 | presetName.Add(styleName);
74 | Names.Add(styleName, d.styles[i].id);
75 | }
76 | }
77 | }
78 | }
79 | }
80 | }
81 | catch
82 | {
83 | // 何らかの例外が出た場合は無視
84 | }
85 | _name = presetName.ToArray();
86 | }
87 | public SpeechEngineInfo[] GetSpeechEngineInfo()
88 | {
89 | List info = new List();
90 | foreach (var v in _name)
91 | {
92 | info.Add(new SpeechEngineInfo
93 | {
94 | EngineName = EngineName,
95 | LibraryName = v,
96 | Is64BitProcess = Environment.Is64BitProcess
97 | }) ;
98 | }
99 | return info.ToArray();
100 | }
101 |
102 | public virtual ISpeechController GetControllerInstance(SpeechEngineInfo info)
103 | {
104 | return EngineName == info.EngineName ? new VOICEVOXController(info) : null;
105 | }
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/src/Speech/Controller/Voiceroid2Controller.cs:
--------------------------------------------------------------------------------
1 | using Codeer.Friendly;
2 | using Codeer.Friendly.Windows;
3 | using Codeer.Friendly.Windows.Grasp;
4 | using RM.Friendly.WPFStandardControls;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.ComponentModel;
8 | using System.Diagnostics;
9 | using System.IO;
10 | using System.Linq;
11 | using System.Runtime.InteropServices;
12 | using System.Text;
13 | using System.Threading;
14 | using System.Threading.Tasks;
15 |
16 | using System.Windows.Forms;
17 | using System.Windows.Threading;
18 |
19 | namespace Speech
20 | {
21 | ///
22 | /// VOICEROID2 操作クラス
23 | ///
24 | public class Voiceroid2Controller : IDisposable, ISpeechController
25 | {
26 | WindowsAppFriend _app;
27 | Process _process;
28 | WindowControl _root;
29 | System.Timers.Timer _timer; // 状態監視のためのタイマー
30 | Queue _queue = new Queue();
31 |
32 | public delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lparam);
33 | static int _pid = 0;
34 |
35 | [DllImport("User32.dll")]
36 | static extern int SetForegroundWindow(IntPtr hWnd);
37 | [DllImport("user32.dll", CharSet = CharSet.Auto)]
38 | private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
39 |
40 | public SpeechEngineInfo Info { get; private set; }
41 |
42 | ///
43 | /// Voiceroid のフルパス
44 | ///
45 | public string VoiceroidPath { get; private set; }
46 |
47 | string _libraryName;
48 | string _promptString;
49 | bool _isPlaying = false;
50 | bool _isRunning = false;
51 | double _tickCount = 0;
52 | public Voiceroid2Controller(SpeechEngineInfo info)
53 | {
54 | Info = info;
55 |
56 | var voiceroid2 = new Voiceroid2Enumerator();
57 | _promptString = voiceroid2.PromptString;
58 |
59 | VoiceroidPath = info.EnginePath;
60 | _libraryName = info.LibraryName;
61 | _timer = new System.Timers.Timer(100);
62 | _timer.Elapsed += timer_Elapsed;
63 | }
64 |
65 | object _lockObject = new object();
66 |
67 | private void timer_Elapsed(object sender, EventArgs e)
68 | {
69 | _timer.Stop(); // 途中の処理が重いため、タイマーをいったん止める
70 | lock (_lockObject)
71 | {
72 | _tickCount += _timer.Interval;
73 |
74 | // ここからプロセス間通信&UI操作(重い)
75 | WPFButtonBase playButton = new WPFButtonBase(_root.IdentifyFromLogicalTreeIndex(0, 4, 3, 5, 3, 0, 3, 0));
76 | var d = playButton.LogicalTree();
77 | System.Windows.Visibility v = (System.Windows.Visibility)(d[2])["Visibility"]().Core; // [再生]の画像の表示状態
78 | // ここまで
79 |
80 | if (v != System.Windows.Visibility.Visible && !_isRunning)
81 | {
82 | _isRunning = true;
83 | }
84 | else
85 | // 再生開始から 500 ミリ秒程度経過しても再生ボタンがうまく確認できなかった場合にも完了とみなす
86 | if (v == System.Windows.Visibility.Visible && (_isRunning || (!_isRunning && _tickCount > 500)))
87 | {
88 | if(_queue.Count == 0)
89 | {
90 | StopSpeech();
91 | return; // タイマーが止まったまま終了
92 | }else
93 | {
94 | // 喋るべき内容が残っているときは再開
95 | string t = _queue.Dequeue();
96 | WPFTextBox textbox = new WPFTextBox(_root.IdentifyFromLogicalTreeIndex(0, 4, 3, 5, 3, 0, 2));
97 | textbox.EmulateChangeText(t);
98 |
99 | playButton.EmulateClick();
100 | _isPlaying = true;
101 | _isRunning = false;
102 | _tickCount = 0;
103 | }
104 | }
105 |
106 | _timer.Start();
107 | }
108 | }
109 |
110 | private void StopSpeech()
111 | {
112 | _timer.Stop();
113 | lock (_lockObject)
114 | {
115 | _tickCount = 0;
116 | if (_isPlaying)
117 | {
118 | _isRunning = false;
119 | _isPlaying = false;
120 | OnFinished();
121 | }
122 | }
123 | }
124 |
125 | ///
126 | /// 音声再生が完了したときに発生するイベント
127 | ///
128 | public event EventHandler Finished;
129 | protected virtual void OnFinished()
130 | {
131 | EventArgs se = new EventArgs();
132 | Finished?.Invoke(this, se);
133 | }
134 |
135 | ///
136 | /// Voiceroid が起動中かどうかを確認
137 | ///
138 | /// 起動中であれば true
139 | public bool IsActive()
140 | {
141 | string name = Path.GetFileNameWithoutExtension(VoiceroidPath);
142 | Process[] localByName = Process.GetProcessesByName(name);
143 |
144 | if (localByName.Length > 0)
145 | {
146 | // VOICEROID2 は2重起動しないはずなので 0番目を参照する
147 | _process = localByName[0];
148 | _pid = _process.Id;
149 | return true;
150 | }
151 | return false;
152 | }
153 |
154 | ///
155 | /// Voiceroidを起動する。すでに起動している場合には起動しているものを操作対象とする。
156 | ///
157 | public void Activate()
158 | {
159 | if (!IsActive())
160 | {
161 | _app = new WindowsAppFriend(Process.Start(this.VoiceroidPath));
162 | while (_root == null || (_root != null && _root.TypeFullName != "AI.Talk.Editor.MainWindow"))
163 | {
164 | _process = Process.GetProcessById(_app.ProcessId);
165 | _root = WindowControl.GetTopLevelWindows(_app)[0];
166 | Thread.Sleep(2000);
167 | }
168 | }else
169 | {
170 | _app = new WindowsAppFriend(_process);
171 | _process = Process.GetProcessById(_app.ProcessId);
172 | _root = WindowControl.GetTopLevelWindows(_app)[0];
173 | }
174 | }
175 |
176 | ///
177 | /// 指定した文字列を再生します
178 | ///
179 | /// 再生する文字列
180 | public void Play(string text)
181 | {
182 | SetText(text);
183 | }
184 | internal virtual void SetText(string text)
185 | {
186 | text = text.Trim() == "" ? "." : text;
187 | string t = _libraryName + _promptString + text;
188 | if (_queue.Count == 0)
189 | {
190 | WPFTextBox textbox = new WPFTextBox(_root.IdentifyFromLogicalTreeIndex(0, 4, 3, 5, 3, 0, 2));
191 | textbox.EmulateChangeText(t);
192 | Play();
193 | }
194 | else
195 | {
196 | _queue.Enqueue(t);
197 | }
198 | }
199 |
200 | ///
201 | /// VOICEROID2 に入力された文字列を再生します
202 | ///
203 | public void Play()
204 | {
205 | WPFButtonBase playButton = new WPFButtonBase(_root.IdentifyFromLogicalTreeIndex(0, 4, 3, 5, 3, 0, 3, 0));
206 | playButton.EmulateClick();
207 | Application.DoEvents();
208 | _isPlaying = true;
209 | _isRunning = false;
210 | _timer.Start();
211 | }
212 | ///
213 | /// VOICEROID2 の再生を停止します(停止ボタンを押す)
214 | ///
215 | public void Stop()
216 | {
217 | StopSpeech();
218 | WPFButtonBase stopButton = new WPFButtonBase(_root.IdentifyFromLogicalTreeIndex(0, 4, 3, 5, 3, 0, 3, 1));
219 | stopButton.EmulateClick();
220 | }
221 |
222 | enum EffectType { Volume = 0, Speed = 1, Pitch = 2, PitchRange = 3}
223 | ///
224 | /// 音量を設定します
225 | ///
226 | /// 0.0~2.0
227 | public void SetVolume(float value)
228 | {
229 | SetEffect(EffectType.Volume, value);
230 | }
231 | ///
232 | /// 音量を取得します
233 | ///
234 | /// 音量
235 | public float GetVolume()
236 | {
237 | return GetEffect(EffectType.Volume);
238 | }
239 | ///
240 | /// 話速を設定します
241 | ///
242 | /// 0.5~4.0
243 | public void SetSpeed(float value)
244 | {
245 | SetEffect(EffectType.Speed, value);
246 | }
247 | ///
248 | /// 話速を取得します
249 | ///
250 | /// 話速
251 | public float GetSpeed()
252 | {
253 | return GetEffect(EffectType.Speed);
254 | }
255 |
256 | ///
257 | /// 高さを設定します
258 | ///
259 | /// 0.5~2.0
260 | public void SetPitch(float value)
261 | {
262 | SetEffect(EffectType.Pitch, value);
263 | }
264 | ///
265 | /// 高さを取得します
266 | ///
267 | /// 高さ
268 | public float GetPitch()
269 | {
270 | return GetEffect(EffectType.Pitch);
271 | }
272 | ///
273 | /// 抑揚を設定します
274 | ///
275 | /// 0.0~2.0
276 | public void SetPitchRange(float value)
277 | {
278 | SetEffect(EffectType.PitchRange, value);
279 | }
280 | ///
281 | /// 抑揚を取得します
282 | ///
283 | /// 抑揚
284 | public float GetPitchRange()
285 | {
286 | return GetEffect(EffectType.PitchRange);
287 | }
288 |
289 | private void SetEffect(EffectType t, float value)
290 | {
291 | WPFTextBox textbox = new WPFTextBox(_root.IdentifyFromLogicalTreeIndex(0, 4, 5, 0, 1, 0, 3, 0, 6, (int)t, 0, 7));
292 | textbox.EmulateChangeText($"{value:0.00}");
293 | }
294 | private float GetEffect(EffectType t)
295 | {
296 | WPFTextBox textbox = new WPFTextBox(_root.IdentifyFromLogicalTreeIndex(0, 4, 5, 0, 1, 0, 3, 0, 6, (int)t, 0, 7));
297 | return Convert.ToSingle(textbox.Text);
298 | }
299 |
300 | #region IDisposable Support
301 | private bool disposedValue = false;
302 |
303 | protected virtual void Dispose(bool disposing)
304 | {
305 | if (!disposedValue)
306 | {
307 | if (disposing)
308 | {
309 | if(_app != null)
310 | {
311 | _app.Dispose();
312 | }
313 | }
314 | disposedValue = true;
315 | }
316 | }
317 |
318 | public void Dispose()
319 | {
320 | Dispose(true);
321 | }
322 | #endregion
323 | }
324 | }
--------------------------------------------------------------------------------
/src/Speech/Controller/Voiceroid2Enumerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Xml.Linq;
8 |
9 | namespace Speech
10 | {
11 | public class Voiceroid2Enumerator : ISpeechEnumerator
12 | {
13 | protected string[] _name = new string[0];
14 | public string PromptString { get; internal set; }
15 |
16 | public string EngineName { get; internal set; }
17 | public Voiceroid2Enumerator()
18 | {
19 | // VOICEROID2 の一覧は下記で取得できる
20 | // 下記ファイルは VOICEROID2 終了時に生成されるため、一度 VOICEROID2 を起動・終了
21 | // しておくこと
22 | string path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
23 | + @"\AHS\VOICEROID\2.0\Standard.settings";
24 | Initialize(path, "VOICEROID2");
25 | }
26 |
27 | internal void Initialize(string path,string engineName)
28 | {
29 | EngineName = engineName;
30 |
31 | if (File.Exists(path))
32 | {
33 | List presetName = new List();
34 | try
35 | {
36 | var xml = XElement.Load(path);
37 |
38 | // 話者を識別するための記号。デフォルトは「>」。「紲星あかり>」などと指定する。
39 | PromptString = (from c in xml.Elements("VoicePreset").Elements("PromptString")
40 | select c.Value).ToArray()[0];
41 |
42 | // インストール済み話者一覧
43 | presetName.AddRange(from c in xml.Elements("VoicePreset").Elements("VoicePresets").Elements("VoicePreset").Elements("PresetName")
44 | select c.Value);
45 |
46 | // ユーザが追加・変更した話者一覧
47 | string isSpecialFolderEnabled = (from c in xml.Elements("VoicePreset").Elements("VoicePresetFilePath").Elements("IsSpecialFolderEnabled")
48 | select c.Value).ToArray()[0];
49 | string partialPath = (from c in xml.Elements("VoicePreset").Elements("VoicePresetFilePath").Elements("PartialPath")
50 | select c.Value).ToArray()[0];
51 | string userPresetPath = Path.Combine(
52 | Environment.GetFolderPath(Environment.SpecialFolder.Personal)
53 | , partialPath);
54 | if (isSpecialFolderEnabled == "false")
55 | {
56 | userPresetPath = partialPath;
57 | }
58 | var userXml = XElement.Load(userPresetPath);
59 | presetName.AddRange(from c in userXml.Elements("VoicePreset").Elements("PresetName")
60 | select c.Value);
61 |
62 | _name = presetName.ToArray();
63 | }
64 | catch
65 | {
66 | PromptString = "";
67 | }
68 | }
69 | else
70 | {
71 | _name = new string[0];
72 | }
73 | }
74 | public virtual SpeechEngineInfo[] GetSpeechEngineInfo()
75 | {
76 | List info = new List();
77 | string path = GetInstalledPath();
78 |
79 | if (string.IsNullOrEmpty(path))
80 | {
81 | return new SpeechEngineInfo[0];
82 | }
83 | foreach (var v in _name)
84 | {
85 | info.Add(new SpeechEngineInfo { EngineName = EngineName, EnginePath = path, LibraryName = v });
86 | }
87 | return info.ToArray();
88 | }
89 | internal virtual string GetInstalledPath()
90 | {
91 | string uninstall_path = @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\";
92 | // 32bit の場合 SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
93 |
94 | string result = "";
95 | Microsoft.Win32.RegistryKey uninstall = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(uninstall_path, false);
96 | if (uninstall != null)
97 | {
98 | foreach (string subKey in uninstall.GetSubKeyNames())
99 | {
100 | Microsoft.Win32.RegistryKey appkey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(uninstall_path + "\\" + subKey, false);
101 | var key = appkey.GetValue("DisplayName");
102 | if (key != null && key.ToString() == "VOICEROID2 Editor")
103 | {
104 | var location = appkey.GetValue("InstallLocation").ToString();
105 | result = Path.Combine(location , @"VoiceroidEditor.exe");
106 | break;
107 | }
108 | }
109 | }
110 | return result;
111 | }
112 |
113 |
114 | public virtual ISpeechController GetControllerInstance(SpeechEngineInfo info)
115 | {
116 | return EngineName == info.EngineName ? new Voiceroid2Controller(info) : null;
117 | }
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/src/Speech/Controller/Voiceroid64Enumerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Speech
9 | {
10 | class Voiceroid64Enumerator : Voiceroid2Enumerator
11 | {
12 | public Voiceroid64Enumerator()
13 | {
14 | string path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
15 | + @"\AHS\VOICEROID\2.0\Standard.settings";
16 | Initialize(path, "VOICEROID64");
17 | }
18 |
19 | internal override string GetInstalledPath()
20 | {
21 | string uninstall_path = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\";
22 |
23 | string result = "";
24 | Microsoft.Win32.RegistryKey uninstall = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(uninstall_path, false);
25 | if (uninstall != null)
26 | {
27 | foreach (string subKey in uninstall.GetSubKeyNames())
28 | {
29 | Microsoft.Win32.RegistryKey appkey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(uninstall_path + "\\" + subKey, false);
30 | var key = appkey.GetValue("DisplayName");
31 | if (key != null && key.ToString() == "VOICEROID2 Editor 64bit")
32 | {
33 | var location = appkey.GetValue("InstallLocation").ToString();
34 | result = Path.Combine(location, @"VoiceroidEditor.exe");
35 | break;
36 | }
37 | }
38 | }
39 |
40 | return result;
41 | }
42 |
43 | public override SpeechEngineInfo[] GetSpeechEngineInfo()
44 | {
45 | var info = base.GetSpeechEngineInfo();
46 | foreach(var i in info)
47 | {
48 | i.Is64BitProcess = true;
49 | }
50 | return info;
51 | }
52 |
53 | public override ISpeechController GetControllerInstance(SpeechEngineInfo info)
54 | {
55 | return EngineName == info.EngineName ? new Voiceroid64Controller(info) : null;
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Speech/Controller/VoiceroidPlusController.cs:
--------------------------------------------------------------------------------
1 | using Codeer.Friendly;
2 | using Codeer.Friendly.Windows;
3 | using Codeer.Friendly.Windows.Grasp;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.ComponentModel;
7 | using System.Diagnostics;
8 | using System.IO;
9 | using System.Linq;
10 | using System.Runtime.InteropServices;
11 | using System.Text;
12 | using System.Threading.Tasks;
13 | using System.Windows.Forms;
14 | using System.Windows.Threading;
15 |
16 | namespace Speech
17 | {
18 | ///
19 | /// VOICEROID+ 操作クラス
20 | ///
21 | public class VoiceroidPlusController : IDisposable, ISpeechController
22 | {
23 | WindowsAppFriend _app;
24 | Process _process;
25 | protected WindowControl _root;
26 |
27 | System.Timers.Timer _timer; // 状態監視のためのタイマー
28 | bool _playStarting = false;
29 |
30 | [DllImport("User32.dll")]
31 | static extern int SetForegroundWindow(IntPtr hWnd);
32 | [DllImport("user32.dll", CharSet = CharSet.Auto)]
33 | protected static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
34 |
35 |
36 | ///
37 | /// Voiceroid のフルパス
38 | ///
39 | public string VoiceroidPath { get; protected set; }
40 |
41 | public SpeechEngineInfo Info { get; protected set; }
42 |
43 | public VoiceroidPlusController(SpeechEngineInfo info)
44 | {
45 | Info = info;
46 | VoiceroidPath = info.EnginePath;
47 | _timer = new System.Timers.Timer(100);
48 | _timer.Elapsed += timer_Elapsed;
49 | }
50 |
51 | private void timer_Elapsed(object sender, EventArgs e)
52 | {
53 | try
54 | {
55 | WindowControl playButton = _root.IdentifyFromZIndex(2, 0, 0, 1, 0, 1, 0, 3);
56 | AppVar button = playButton.AppVar;
57 | string text = (string)button["Text"]().Core;
58 | if (!_playStarting && text.Trim() == "再生")
59 | {
60 | _timer.Stop();
61 | OnFinished();
62 | }
63 | _playStarting = false;
64 | }
65 | catch
66 | {
67 | // VOICEROID+ との通信が失敗することがあるが無視
68 | }
69 | }
70 |
71 | ///
72 | /// 音声再生が完了したときに発生するイベント
73 | ///
74 | public event EventHandler Finished;
75 | protected virtual void OnFinished()
76 | {
77 | EventArgs se = new EventArgs();
78 | Finished?.Invoke(this, se);
79 | }
80 |
81 | ///
82 | /// Voiceroid が起動中かどうかを確認
83 | ///
84 | /// 起動中であれば true
85 | public bool IsActive()
86 | {
87 | string name = Path.GetFileNameWithoutExtension(VoiceroidPath);
88 | Process[] localByName = Process.GetProcessesByName(name);
89 | foreach(var p in localByName)
90 | {
91 | if(p.MainModule.FileName == VoiceroidPath)
92 | {
93 | _process = p;
94 | return true;
95 | }
96 | }
97 | return false;
98 | }
99 |
100 | ///
101 | /// Voiceroidを起動する。すでに起動している場合には起動しているものを操作対象とする。
102 | ///
103 | public void Activate()
104 | {
105 | if (IsActive())
106 | {
107 | _app = new WindowsAppFriend(_process);
108 | }
109 | else
110 | {
111 | _process = Process.Start(VoiceroidPath);
112 | _app = new WindowsAppFriend(_process);
113 | }
114 | _root = WindowControl.GetTopLevelWindows(_app)[0];
115 | }
116 |
117 | ///
118 | /// 指定した文字列を再生します
119 | ///
120 | /// 再生する文字列
121 | public void Play(string text)
122 | {
123 | WindowControl speechTextBox = _root.IdentifyFromZIndex(2, 0, 0, 1, 0, 1, 1);
124 | AppVar textbox = speechTextBox.AppVar;
125 | textbox["Text"](text);
126 | Play();
127 | }
128 | ///
129 | /// VOICEROID+ に入力された文字列を再生します
130 | ///
131 | public virtual void Play()
132 | {
133 | WindowControl playButton = _root.IdentifyFromZIndex(2, 0, 0, 1, 0, 1, 0, 3);
134 | AppVar button = playButton.AppVar;
135 | string text = (string)button["Text"]().Core;
136 | if(text.Trim() == "再生")
137 | {
138 | button["PerformClick"]();
139 | _playStarting = true;
140 | _timer.Start();
141 | }
142 | }
143 | ///
144 | /// VOICEROID+ の再生を停止します(停止ボタンを押す)
145 | ///
146 | public virtual void Stop()
147 | {
148 | WindowControl stopButton = _root.IdentifyFromZIndex(2, 0, 0, 1, 0, 1, 0, 2);
149 | AppVar button = stopButton.AppVar;
150 | button["PerformClick"]();
151 | }
152 |
153 | protected enum EffectType { Volume = 8, Speed = 9, Pitch = 10, PitchRange = 11}
154 | ///
155 | /// 音量を設定します
156 | ///
157 | /// 0.0~2.0
158 | public void SetVolume(float value)
159 | {
160 | SetEffect(EffectType.Volume, value);
161 | }
162 | ///
163 | /// 音量を取得します
164 | ///
165 | /// 音量
166 | public float GetVolume()
167 | {
168 | return GetEffect(EffectType.Volume);
169 | }
170 | ///
171 | /// 話速を設定します
172 | ///
173 | /// 0.5~4.0
174 | public void SetSpeed(float value)
175 | {
176 | SetEffect(EffectType.Speed,value);
177 | }
178 | ///
179 | /// 話速を取得します
180 | ///
181 | /// 話速
182 | public float GetSpeed()
183 | {
184 | return GetEffect(EffectType.Speed);
185 | }
186 |
187 | ///
188 | /// 高さを設定します
189 | ///
190 | /// 0.5~2.0
191 | public void SetPitch(float value)
192 | {
193 | SetEffect(EffectType.Pitch, value);
194 | ChangeToVoiceEffect();
195 | }
196 | ///
197 | /// 高さを取得します
198 | ///
199 | /// 高さ
200 | public float GetPitch()
201 | {
202 | return GetEffect(EffectType.Pitch);
203 | }
204 | ///
205 | /// 抑揚を設定します
206 | ///
207 | /// 0.0~2.0
208 | public void SetPitchRange(float value)
209 | {
210 | SetEffect(EffectType.PitchRange, value);
211 | }
212 | ///
213 | /// 抑揚を取得します
214 | ///
215 | /// 抑揚
216 | public float GetPitchRange()
217 | {
218 | return GetEffect(EffectType.PitchRange);
219 | }
220 |
221 | protected virtual void SetEffect(EffectType t, float value)
222 | {
223 | ChangeToVoiceEffect();
224 | int index = (int)t;
225 | WindowControl control = _root.IdentifyFromZIndex(2, 0, 0, 0, 0, 0, 0, index);
226 | AppVar v = control.AppVar;
227 | v["Focus"]();
228 | v["Text"](string.Format("{0:0.00}", value));
229 |
230 | // TODO: VOICEROID+では数値を変更するだけでは変更が行われないため何らかの方法が必要
231 |
232 | }
233 | protected virtual float GetEffect(EffectType t)
234 | {
235 | ChangeToVoiceEffect();
236 | int index = (int)t;
237 | WindowControl control = _root.IdentifyFromZIndex(2, 0, 0, 0, 0, 0, 0, index);
238 | AppVar v = control.AppVar;
239 | return Convert.ToSingle((string)v["Text"]().Core);
240 | }
241 |
242 |
243 | ///
244 | /// 音声効果タブを選択します
245 | ///
246 | protected virtual void ChangeToVoiceEffect()
247 | {
248 | RestoreMinimizedWindow();
249 | WindowControl tabControl = _root.IdentifyFromZIndex(2, 0, 0, 0, 0);
250 | AppVar tab = tabControl.AppVar;
251 | tab["SelectedIndex"](2);
252 | }
253 | protected void RestoreMinimizedWindow()
254 | {
255 | const uint WM_SYSCOMMAND = 0x0112;
256 | const int SC_RESTORE = 0xF120;
257 | FormWindowState state = (FormWindowState)_root["WindowState"]().Core;
258 | if (state == FormWindowState.Minimized)
259 | {
260 | SendMessage(_root.Handle, WM_SYSCOMMAND,
261 | new IntPtr(SC_RESTORE), IntPtr.Zero);
262 | }
263 | }
264 |
265 | #region IDisposable Support
266 | private bool disposedValue = false;
267 |
268 | protected virtual void Dispose(bool disposing)
269 | {
270 | if (!disposedValue)
271 | {
272 | if (disposing)
273 | {
274 | if(_app != null)
275 | {
276 | _app.Dispose();
277 | }
278 | }
279 | disposedValue = true;
280 | }
281 | }
282 |
283 | public void Dispose()
284 | {
285 | Dispose(true);
286 | }
287 | #endregion
288 | }
289 | }
--------------------------------------------------------------------------------
/src/Speech/Controller/VoiceroidPlusEnumerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Xml.Linq;
8 |
9 | namespace Speech
10 | {
11 |
12 | public class VoiceroidPlusEnumerator : ISpeechEnumerator
13 | {
14 |
15 | protected class Data
16 | {
17 | public string Name { get; internal set; }
18 | public string Path { get; internal set; }
19 | }
20 | protected Data[] _info;
21 | public string EngineName { get; internal set; }
22 |
23 | public VoiceroidPlusEnumerator()
24 | {
25 | Initialize();
26 | }
27 |
28 | private void Initialize()
29 | {
30 | EngineName = "VOICEROID+";
31 | // VOICEROID の一覧は下記で取得できる
32 | string path = System.Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
33 | + @"\AHS\";
34 | List data = new List();
35 | try
36 | {
37 | string[] files = Directory.GetDirectories(path);
38 |
39 | for (int i = 0; i < files.Length; i++)
40 | {
41 | string folder = files[i].Substring(files[i].LastIndexOf(@"\"));
42 | if (folder.StartsWith(@"\VOICEROID+"))
43 | {
44 | Data d = new Data();
45 | d.Name = folder.Substring(12); // 「東北きりたん」など
46 |
47 | string[] sub = Directory.GetDirectories(files[i]);
48 | var xml = XElement.Load(Path.Combine(sub[0], "VOICEROID.dat"));
49 | var dbsPath = (from c in xml.Elements("DbsPath")
50 | select c.Value).ToArray()[0];
51 | d.Path = Path.Combine(dbsPath.Substring(0, dbsPath.LastIndexOf(@"\")), "VOICEROID.exe");
52 |
53 | data.Add(d);
54 | }
55 | }
56 | }
57 | catch
58 | {
59 | // 初期化に途中で失敗した場合はうまく処理できたところまで返す
60 | }
61 | _info = data.ToArray();
62 | }
63 |
64 | public SpeechEngineInfo[] GetSpeechEngineInfo()
65 | {
66 |
67 | List info = new List();
68 | foreach (var v in _info)
69 | {
70 | info.Add(new SpeechEngineInfo { EngineName = EngineName, EnginePath = v.Path, LibraryName = v.Name });
71 | }
72 | return info.ToArray();
73 | }
74 | public virtual ISpeechController GetControllerInstance(SpeechEngineInfo info)
75 | {
76 | return EngineName == info.EngineName ? new VoiceroidPlusController(info) : null;
77 | }
78 |
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Speech/Effect/Wave.cs:
--------------------------------------------------------------------------------
1 | using NAudio.Wave;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace Speech.Effect
9 | {
10 | ///
11 | /// Waveファイル操作
12 | ///
13 | public class Wave
14 | {
15 | ///
16 | /// 波形データ
17 | ///
18 | public double[] Data { get; set; }
19 | public double[] EData { get; set; }
20 | public WaveFormat Format { get; private set; }
21 | ///
22 | /// 音声データを読み込みます
23 | ///
24 | /// ファイル名
25 | /// 読み込んだデータをdouble[](-1.0~1.0)に変換したもの
26 | public double[] Read(string filename)
27 | {
28 | Data = null;
29 |
30 | Format = new WaveFormat(48000, 16, 1);
31 | string tmpFile = "resampled.wav";
32 | using (WaveFileReader reader = new WaveFileReader(filename))
33 | {
34 | using (var resampler = new MediaFoundationResampler(reader, Format))
35 | {
36 | WaveFileWriter.CreateWaveFile(tmpFile, resampler);
37 | }
38 | }
39 | using (WaveFileReader reader = new WaveFileReader(tmpFile))
40 | {
41 | byte[] src = new byte[reader.Length];
42 | reader.Read(src, 0, src.Length);
43 | Data = ConvertToDouble(src);
44 | }
45 |
46 | return Data;
47 | }
48 | ///
49 | /// 音声データをファイルに出力します
50 | ///
51 | /// 出力ファイル名
52 | /// 音声データ
53 | public void Write(string filename, double[] data)
54 | {
55 | using (WaveFileWriter writer = new WaveFileWriter(filename, Format))
56 | {
57 | float scale = 5f;
58 | writer.Write(ConvertToByte(data, scale), 0, data.Length * 2);
59 | }
60 | }
61 |
62 | short nonzero = 1;
63 |
64 | private double[] ConvertToDouble(byte[] data)
65 | {
66 | double[] result = new double[data.Length / 2];
67 | for (int i = 0; i < data.Length; i += 2)
68 | {
69 | short d = (short)(data[i] | (data[i + 1] << 8));
70 | if (d == 0)
71 | {
72 | d = nonzero; // 信号レベルが0の状態が続くと以降が無音になってしまうための対応
73 | }
74 | result[i / 2] = d / 32767.0;
75 | }
76 | return result;
77 | }
78 | private byte[] ConvertToByte(double[] data, float scale)
79 | {
80 | byte[] result = new byte[data.Length * 2];
81 | for (int i = 0; i < data.Length; i++)
82 | {
83 | short d = (short)(data[i] * 32767.0 * scale);
84 | if (d == nonzero)
85 | {
86 | d = 0;
87 | }
88 | result[i * 2] = (byte)(d & 255);
89 | result[i * 2 + 1] = (byte)((d >> 8) & 255);
90 | }
91 | return result;
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/Speech/Effect/Whisper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Speech.Effect
8 | {
9 | ///
10 | /// toWhisper: (c) zeta, 2017 (修正BSDライセンス)
11 | /// https://github.com/zeta-chicken/toWhisper
12 | /// を元に C# に移植
13 | /// https://github.com/ksasao/toWhisper
14 | ///
15 | public class Whisper
16 | {
17 | //LPC次数
18 | public int Order { get; set; } = 0;
19 | //有声音割合(0.0~1.0)
20 | public double Rate { get; set; } = 0.02;
21 | //プリエンファシスフィルタの係数(0.0~1.0)
22 | public double Hpf { get; set; } = 0.97;
23 | //デエンファシスフィルタの係数(0.0~1.0)
24 | public double Lpf { get; set; } = 0.2;
25 |
26 | //フレーム幅をサンプル数に
27 | public double FrameT { get; set; } = 20.0;
28 | int frame = 0;
29 |
30 | // ホワイトノイズ生成用
31 | Random random = new Random();
32 |
33 |
34 | public void Convert(Wave wave)
35 | {
36 | AdjustSize(wave);
37 | WhisperFilter(wave);
38 | }
39 |
40 | private void WhisperFilter(Wave wave)
41 | {
42 | Func windowFunction = Hamming;
43 | int length = wave.Data.Length;
44 | double[] x = new double[frame];
45 | double[] y = new double[frame];
46 | double[] E = new double[frame]; //残差信号
47 | double[] a = new double[Order + 1]; //LPC係数
48 |
49 | double[] v = wave.Data;
50 | double[] v1 = new double[v.Length];
51 | double[] v2 = new double[v.Length];
52 |
53 | for (int i = 0; i < length / frame * 2 - 1; i++)
54 | {
55 | double max = 0.0;
56 | for (int j = 0; j < frame; j++)
57 | {
58 | x[j] = v[j + i * frame / 2] * windowFunction(j, frame);
59 | }
60 |
61 | //LPC係数の導出
62 | LevinsonDurbin(x, frame, a, Order);
63 |
64 | //残差信号(声帯音源)の導出
65 | for (int j = 0; j < frame; j++)
66 | {
67 | double e = 0.0;
68 | for (int n = 0; n < Order + 1; n++)
69 | {
70 | if (j >= n)
71 | {
72 | e += a[n] * x[j - n];
73 | }
74 | }
75 | E[j] = e;
76 | max += e * e;
77 | }
78 |
79 | //声帯振動
80 | for (int j = 0; j < frame; j++)
81 | {
82 | v2[j + i * frame / 2] += E[j] * 10.0;
83 | }
84 | //ホワイトノイズの生成
85 | for (int j = 0; j < frame; j++)
86 | {
87 | y[j] = GenerateWhiteNoise();
88 | }
89 |
90 | for (int j = 1; j < frame; j++)
91 | {
92 | E[j] = ((1.0 - Rate) * E[j - 1] + E[j]) / (2.0 - Rate);
93 | }
94 | //残差信号とノイズの二乗平均レベルをそろえる
95 | max = Math.Sqrt(3.0 * max / frame);
96 | for (int j = 0; j < frame; j++)
97 | {
98 | y[j] = Rate * E[j] + (1.0 - Rate) * max * y[j];
99 | }
100 |
101 | //for (int j=1; j= n) y[j] -= a[n] * y[j - n];
109 | }
110 | }
111 |
112 | for (int j = 0; j < frame; j++)
113 | {
114 | v1[j + i * frame / 2] += y[j];
115 | }
116 | }
117 |
118 | //デエンファシス
119 | for (int i = 1; i < length; i++) v1[i] = Lpf * v1[i - 1] + v1[i];
120 | for (int i = 1; i < length; i++) v2[i] = Lpf * v2[i - 1] + v2[i];
121 |
122 | wave.Data = v1;
123 | wave.EData = v2;
124 | }
125 |
126 | ///
127 | /// フィルタ処理に適したサイズに調整する
128 | ///
129 | /// 読み込んだWaveデータ
130 | private void AdjustSize(Wave wave)
131 | {
132 | //LPC次数の計算
133 | if (Order == 0)
134 | {
135 | Order = wave.Format.SampleRate * 40 / 44100;
136 | }
137 |
138 | //20msのフレーム幅
139 | frame = (int)(wave.Format.SampleRate * FrameT / 1000);
140 | if (frame % 2 != 0) frame++;
141 |
142 | //フレーム幅でちょうど割り切れるようにする
143 | int last = wave.Data.Length;
144 | int length = wave.Data.Length - (wave.Data.Length % frame) + frame;
145 |
146 | double[] v1 = wave.Data;
147 | double[] v = new double[length];
148 |
149 | //足りない分はゼロづめ
150 | for (int i = 1; i < length; i++)
151 | {
152 | if (i < last)
153 | {
154 | v[i] = v1[i] - Hpf * v1[i - 1];
155 | }
156 | else
157 | {
158 | v[i] = 0.0;
159 | }
160 | }
161 |
162 | wave.Data = v;
163 | }
164 |
165 | double Hanning(int i, int frame)
166 | {
167 | return 0.5 - 0.5 * Math.Cos(2.0 * Math.PI / (double)frame * (double)i);
168 | }
169 |
170 | double Hamming(int i, int frame)
171 | {
172 | return 0.54 - 0.46 * Math.Cos(2.0 * Math.PI / (double)frame * (double)i);
173 | }
174 |
175 | double Blackman(int i, int frame)
176 | {
177 | return 0.42 - 0.5 * Math.Cos(2.0 * Math.PI / (double)frame * (double)i) + 0.08 * Math.Cos(4.0 * Math.PI / (double)frame * (double)i);
178 | }
179 |
180 | // 自己相関関数
181 | double AutoCorrelation(double[] x, int l, int N)
182 | {
183 | double res = 0.0;
184 | double r = 0.0, t = 0.0;
185 | for (int i = 0; i < N - l; i++)
186 | {
187 | t = res + (x[i] * x[i + l] + r);
188 | r = (x[i] * x[i + l] + r) - (t - res);
189 | res = t;
190 | }
191 | return res;
192 | }
193 | void LevinsonDurbin(double[] x, int length, double[] a, int lpcOrder)
194 | {
195 | //lpcOrder = k の場合
196 | //フィルタ係数は
197 | //a[0], a[1] , ......, a[k]
198 | //となるため,k+1のメモリ領域を確保して置く必要がある.
199 | double lambda = 0.0, E = 0.0;
200 | double[] r = new double[lpcOrder + 1];
201 | double[] V = new double[lpcOrder + 1];
202 | double[] U = new double[lpcOrder + 1];
203 | for (int i = 0; i < lpcOrder + 1; i++)
204 | {
205 | r[i] = AutoCorrelation(x, i, length);
206 | }
207 | for (int i = 0; i <= lpcOrder; i++)
208 | {
209 | a[i] = 0.0;
210 | }
211 | a[0] = 1.0;
212 | a[1] = -r[1] / r[0];
213 | E = r[0] + r[1] * a[1];
214 | for (int k = 1; k < lpcOrder; k++)
215 | {
216 | lambda = 0.0;
217 | for (int j = 0; j <= k; j++)
218 | {
219 | lambda += a[j] * r[k + 1 - j];
220 | }
221 | lambda /= -E;
222 | for (int j = 0; j <= k + 1; j++)
223 | {
224 | U[j] = a[j];
225 | V[j] = a[k + 1 - j];
226 | }
227 | for (int j = 0; j <= k + 1; j++)
228 | {
229 | a[j] = U[j] + lambda * V[j];
230 | }
231 | E = (1.0 - lambda * lambda) * E;
232 | }
233 | return;
234 | }
235 | double GenerateWhiteNoise()
236 | {
237 | return random.NextDouble() * 2.0 - 1.0;
238 | }
239 |
240 | void LtiFilter(double[] x, int len, double[] a, int al, double[] b, int bl)
241 | {
242 | //線形時不変フィルタ
243 | //a[0]は出力信号のフィルタ係数であるため,常に1にすること
244 | if (x == null) return;
245 | if (a == null) return;
246 | if (b == null) bl = 0;
247 | double[] y = new double[len];
248 |
249 | y[0] = x[0];
250 | for (int i = 1; i < len; i++)
251 | {
252 | y[i] = 0.0;
253 | for (int j = 0; j < bl; j++)
254 | {
255 | if (i >= j) y[i] += b[j] * x[i - j];
256 | }
257 | for (int j = 1; j < al; j++)
258 | {
259 | if (i >= j) y[i] -= a[j] * y[i - j];
260 | }
261 | }
262 | Array.Copy(y, x, len);
263 | return;
264 | }
265 | }
266 | }
267 |
--------------------------------------------------------------------------------
/src/Speech/EngineParameters.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Speech
8 | {
9 | public class EngineParameters
10 | {
11 | public float Pitch { get; set; } = -1;
12 | public float PitchRange { get; set; } = -1;
13 | public float Volume { get; set; } = -1;
14 | public float Speed { get; set; } = -1;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Speech/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // アセンブリに関する一般情報は以下の属性セットをとおして制御されます。
6 | // アセンブリに関連付けられている情報を変更するには、
7 | // これらの属性値を変更してください。
8 | [assembly: AssemblyTitle("Voice")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Voice")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから
18 | // 参照不可能になります。COM からこのアセンブリ内の型にアクセスする場合は、
19 | // その型の ComVisible 属性を true に設定してください。
20 | [assembly: ComVisible(false)]
21 |
22 | // このプロジェクトが COM に公開される場合、次の GUID が typelib の ID になります
23 | [assembly: Guid("7787a468-a1bb-4077-85bc-543258610060")]
24 |
25 | // アセンブリのバージョン情報は次の 4 つの値で構成されています:
26 | //
27 | // メジャー バージョン
28 | // マイナー バージョン
29 | // ビルド番号
30 | // Revision
31 | //
32 | // すべての値を指定するか、下のように '*' を使ってビルドおよびリビジョン番号を
33 | // 既定値にすることができます:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/src/Speech/SoundPlayer.cs:
--------------------------------------------------------------------------------
1 | using NAudio.Wave;
2 | using NAudio.Wave.SampleProviders;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 |
11 | namespace Speech
12 | {
13 | public class SoundPlayer : IDisposable
14 | {
15 | private bool disposedValue;
16 | private WaveOutEvent waveOut;
17 | public SoundPlayer()
18 | {
19 | waveOut = new WaveOutEvent();
20 | waveOut.PlaybackStopped += WaveOut_PlaybackStopped;
21 | }
22 |
23 | private void WaveOut_PlaybackStopped(object sender, StoppedEventArgs e)
24 | {
25 | // throw new NotImplementedException();
26 | }
27 |
28 |
29 | ///
30 | /// 無音をスピーカーに出力します。Bluetoothスピーカーなど停止状態から
31 | /// 音声が正常に再生されるようになるまで一定時間音声出力が必要なもの
32 | /// のために利用します。
33 | ///
34 | /// 無音出力時間(ミリ秒)
35 | public void PlaySilenceMs(int millisec)
36 | {
37 | byte[] data = new byte[millisec*10*2];
38 | IWaveProvider provider = new RawSourceWaveStream(
39 | new MemoryStream(data), new WaveFormat(10000,16,1));
40 |
41 | waveOut.Init(provider);
42 | waveOut.Play();
43 | while (waveOut.PlaybackState == PlaybackState.Playing)
44 | {
45 | Thread.Sleep(1);
46 | }
47 | waveOut.Stop();
48 | }
49 |
50 | ///
51 | /// 音声ファイルを再生します。
52 | ///
53 | /// ファイル名
54 | public void Play(string filename)
55 | {
56 | using (var soundReader = new MediaFoundationReader(filename))
57 | {
58 | waveOut.Init(soundReader);
59 | waveOut.Play();
60 | while (waveOut.PlaybackState == PlaybackState.Playing)
61 | {
62 | Thread.Sleep(1);
63 | }
64 | }
65 | waveOut.Stop();
66 | }
67 |
68 | protected virtual void Dispose(bool disposing)
69 | {
70 | if (!disposedValue)
71 | {
72 | if (disposing)
73 | {
74 | waveOut.Dispose();
75 | }
76 |
77 | // TODO: アンマネージド リソース (アンマネージド オブジェクト) を解放し、ファイナライザーをオーバーライドします
78 | // TODO: 大きなフィールドを null に設定します
79 | disposedValue = true;
80 | }
81 | }
82 |
83 |
84 | public void Dispose()
85 | {
86 | // このコードを変更しないでください。クリーンアップ コードを 'Dispose(bool disposing)' メソッドに記述します
87 | Dispose(disposing: true);
88 | GC.SuppressFinalize(this);
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/Speech/SoundRecorder.cs:
--------------------------------------------------------------------------------
1 | using NAudio.CoreAudioApi;
2 | using NAudio.Wave;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Diagnostics;
6 | using System.Linq;
7 | using System.Runtime.InteropServices;
8 | using System.Text;
9 | using System.Threading;
10 | using System.Threading.Tasks;
11 |
12 | namespace Speech
13 | {
14 | public class SoundRecorder : IDisposable
15 | {
16 | private const int APPCOMMAND_VOLUME_MUTE = 0x80000;
17 | private const int APPCOMMAND_VOLUME_UP = 0xA0000;
18 | private const int APPCOMMAND_VOLUME_DOWN = 0x90000;
19 | private const int WM_APPCOMMAND = 0x319;
20 |
21 | bool _finished = false;
22 |
23 | [DllImport("kernel32.dll")]
24 | static extern IntPtr GetConsoleWindow();
25 | [DllImport("user32.dll")]
26 | public static extern IntPtr SendMessageW(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
27 |
28 | public IntPtr GetHandle()
29 | {
30 | var handle = Process.GetCurrentProcess().MainWindowHandle;
31 | if(handle == IntPtr.Zero)
32 | {
33 | handle = GetConsoleWindow();
34 | }
35 | return handle;
36 | }
37 |
38 | private void Mute()
39 | {
40 | var handle = GetHandle();
41 | SendMessageW(handle, WM_APPCOMMAND, handle, (IntPtr)APPCOMMAND_VOLUME_MUTE);
42 | }
43 | private void Unmute()
44 | {
45 | var handle = GetHandle();
46 | SendMessageW(handle, WM_APPCOMMAND, handle, (IntPtr)APPCOMMAND_VOLUME_UP);
47 | SendMessageW(handle, WM_APPCOMMAND, handle, (IntPtr)APPCOMMAND_VOLUME_DOWN);
48 | }
49 |
50 | private WaveFileWriter _writer = null;
51 | private IWaveIn _capture = null;
52 | private bool disposedValue;
53 |
54 | ///
55 | /// 音声合成の完了後に何ミリ秒待ってから録音を終了するか
56 | ///
57 | public UInt32 PostWait { get; set; } = 0;
58 |
59 | ///
60 | /// Start()が呼び出されてから何ミリ秒待ってから録音を開始するか
61 | ///
62 | public UInt32 PreWait { get; set; } = 0;
63 |
64 | ///
65 | /// 出力先のファイル名を取得または設定します
66 | ///
67 | public string OutputPath { get; set; }
68 |
69 | public SoundRecorder(string filename)
70 | {
71 | OutputPath = filename;
72 | _capture = new WasapiLoopbackCapture();
73 | }
74 | public async Task Start()
75 | {
76 | _finished = false;
77 | _writer = new WaveFileWriter(OutputPath, _capture.WaveFormat);
78 | _capture.DataAvailable += (s, a) =>
79 | {
80 | _writer.Write(a.Buffer, 0, a.BytesRecorded);
81 | };
82 | _capture.RecordingStopped += (s, a) =>
83 | {
84 | _writer.Flush();
85 | _writer.Close();
86 | _writer.Dispose();
87 | _finished = true;
88 | };
89 | await Task.Delay((int)PreWait);
90 | Mute();
91 | _capture.StartRecording();
92 | }
93 | public async Task Stop()
94 | {
95 | if(_capture != null)
96 | {
97 | await Task.Delay((int)PostWait);
98 | _capture.StopRecording();
99 | _capture.Dispose();
100 | while (!_finished)
101 | {
102 | Thread.Sleep(100);
103 | }
104 | Unmute();
105 | }
106 | }
107 |
108 | protected virtual void Dispose(bool disposing)
109 | {
110 | if (!disposedValue)
111 | {
112 | if (disposing)
113 | {
114 | Task t = Stop();
115 | t.Wait();
116 | }
117 |
118 | // TODO: アンマネージド リソース (アンマネージド オブジェクト) を解放し、ファイナライザーをオーバーライドします
119 | // TODO: 大きなフィールドを null に設定します
120 | disposedValue = true;
121 | }
122 | }
123 |
124 | // // TODO: 'Dispose(bool disposing)' にアンマネージド リソースを解放するコードが含まれる場合にのみ、ファイナライザーをオーバーライドします
125 | // ~SoundRecorder()
126 | // {
127 | // // このコードを変更しないでください。クリーンアップ コードを 'Dispose(bool disposing)' メソッドに記述します
128 | // Dispose(disposing: false);
129 | // }
130 |
131 | public void Dispose()
132 | {
133 | // このコードを変更しないでください。クリーンアップ コードを 'Dispose(bool disposing)' メソッドに記述します
134 | Dispose(disposing: true);
135 | GC.SuppressFinalize(this);
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/Speech/Speech.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {7787A468-A1BB-4077-85BC-543258610060}
8 | Library
9 | Properties
10 | Speech
11 | Speech
12 | v4.8
13 | 512
14 |
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 | AnyCPU
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 | true
36 | bin\x86\Debug\
37 | DEBUG;TRACE
38 | full
39 | AnyCPU
40 | prompt
41 | MinimumRecommendedRules.ruleset
42 |
43 |
44 | bin\x86\Release\
45 | TRACE
46 | true
47 | pdbonly
48 | x86
49 | prompt
50 | MinimumRecommendedRules.ruleset
51 |
52 |
53 |
54 | ..\packages\Codeer.Friendly.2.6.1\lib\net40\Codeer.Friendly.dll
55 |
56 |
57 | ..\packages\Codeer.Friendly.2.6.1\lib\net40\Codeer.Friendly.Dynamic.dll
58 |
59 |
60 | ..\packages\Codeer.Friendly.Windows.2.15.0\lib\net20\Codeer.Friendly.Windows.dll
61 |
62 |
63 | ..\packages\Codeer.Friendly.Windows.Grasp.2.12.0\lib\net35\Codeer.Friendly.Windows.Grasp.2.0.dll
64 |
65 |
66 | ..\packages\Codeer.Friendly.Windows.Grasp.2.12.0\lib\net35\Codeer.Friendly.Windows.Grasp.3.5.dll
67 |
68 |
69 | ..\packages\Codeer.TestAssistant.GeneratorToolKit.3.10.0\lib\net20\Codeer.TestAssistant.GeneratorToolKit.dll
70 |
71 |
72 | ..\packages\NAudio.1.8.4\lib\net35\NAudio.dll
73 |
74 |
75 |
76 |
77 | ..\packages\RM.Friendly.WPFStandardControls.1.46.1\lib\net40\RM.Friendly.WPFStandardControls.3.0.dll
78 |
79 |
80 | ..\packages\RM.Friendly.WPFStandardControls.1.46.1\lib\net40\RM.Friendly.WPFStandardControls.3.0.Generator.dll
81 |
82 |
83 | ..\packages\RM.Friendly.WPFStandardControls.1.46.1\lib\net40\RM.Friendly.WPFStandardControls.3.5.dll
84 |
85 |
86 | ..\packages\RM.Friendly.WPFStandardControls.1.46.1\lib\net40\RM.Friendly.WPFStandardControls.4.0.dll
87 |
88 |
89 | ..\packages\RM.Friendly.WPFStandardControls.1.46.1\lib\net40\RM.Friendly.WPFStandardControls.4.0.Generator.dll
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 | {C866CA3A-32F7-11D2-9602-00C04F8EE628}
155 | 5
156 | 4
157 | 0
158 | tlbimp
159 | False
160 | True
161 |
162 |
163 |
164 |
171 |
--------------------------------------------------------------------------------
/src/Speech/SpeechController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Speech
10 | {
11 | public class SpeechController
12 | {
13 | string[] enumerators =
14 | {
15 | "AIVOICEEnumerator",
16 | "AITalk3Enumerator",
17 | "VoiceroidPlusEnumerator",
18 | "Voiceroid2Enumerator",
19 | "Voiceroid64Enumerator",
20 | "GynoidTalkEnumerator",
21 | "OtomachiUnaTalkEnumerator",
22 | "CeVIOEnumerator",
23 | "CeVIO64Enumerator",
24 | "CeVIOAIEnumerator",
25 | "SAPI5Enumerator",
26 | "VOICEVOXEnumerator",
27 | "COEIROINKEnumerator",
28 | "SHAREVOXEnumerator",
29 | "VOICEPEAKEnumerator"
30 | };
31 | ISpeechEnumerator[] speechEnumerator;
32 |
33 | private static SpeechController instance = null;
34 | BlockingCollection bc = new BlockingCollection();
35 |
36 | private ISpeechEnumerator CreateInstance(string typeName)
37 | {
38 | Type type = Type.GetType("Speech." + typeName + ",Speech"); // Speechはアセンブリ名
39 | var instance = Activator.CreateInstance(type) as ISpeechEnumerator;
40 | return instance;
41 | }
42 |
43 | private SpeechController()
44 | {
45 | Parallel.ForEach(enumerators, e =>
46 | {
47 | ISpeechEnumerator instance = CreateInstance(e);
48 | if(instance != null)
49 | {
50 | bc.Add(instance);
51 | }
52 | });
53 | bc.CompleteAdding();
54 | speechEnumerator = bc.ToArray();
55 | bc.Dispose();
56 | }
57 |
58 | public static SpeechEngineInfo[] GetAllSpeechEngine()
59 | {
60 | if(instance == null)
61 | {
62 | instance = new SpeechController();
63 | }
64 | List info = new List();
65 |
66 | foreach(var se in instance.speechEnumerator)
67 | {
68 | var e = se.GetSpeechEngineInfo();
69 | if(e.Length > 0 && e[0].Is64BitProcess == Environment.Is64BitProcess)
70 | {
71 | info.AddRange(se.GetSpeechEngineInfo());
72 | }
73 | }
74 | return info.ToArray();
75 | }
76 |
77 | public static ISpeechController GetInstance(string libraryName)
78 | {
79 | var info = GetAllSpeechEngine();
80 | foreach(var e in info)
81 | {
82 | if(e.LibraryName == libraryName && Environment.Is64BitProcess == e.Is64BitProcess)
83 | {
84 | return GetInstance(e);
85 | }
86 | }
87 | return null;
88 | }
89 | public static ISpeechController GetInstance(string libraryName, string engineName)
90 | {
91 | var info = GetAllSpeechEngine();
92 | foreach (var e in info)
93 | {
94 | if (e.LibraryName == libraryName && e.EngineName == engineName && Environment.Is64BitProcess == e.Is64BitProcess)
95 | {
96 | return GetInstance(e);
97 | }
98 | }
99 | return null;
100 | }
101 |
102 | public static ISpeechController GetInstance(SpeechEngineInfo info)
103 | {
104 | if (instance == null)
105 | {
106 | instance = new SpeechController();
107 | }
108 | foreach (var i in instance.speechEnumerator)
109 | {
110 | var controller = i.GetControllerInstance(info);
111 | if(controller != null)
112 | {
113 | return controller;
114 | }
115 | }
116 | return null;
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/Speech/SpeechEngineInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Speech
8 | {
9 | public class SpeechEngineInfo
10 | {
11 | ///
12 | /// 音声合成エンジンの名称
13 | ///
14 | public string EngineName { get; internal set; }
15 | ///
16 | /// 音声合成ライブラリの名称
17 | ///
18 | public string LibraryName { get; internal set; }
19 | ///
20 | /// 音声合成エンジンのパス(SAPIの場合は空文字)
21 | ///
22 | public string EnginePath { get; internal set; }
23 | ///
24 | /// 音声合成エンジンが64bitプロセスの場合はtrue
25 | ///
26 | public bool Is64BitProcess { get; internal set; } = false;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Speech/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/Speech/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/SpeechSample/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/SpeechSample/Options.cs:
--------------------------------------------------------------------------------
1 | using CommandLine;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace SpeechSample
9 | {
10 | class Options
11 | {
12 | [Option('t', "text", Required = false, HelpText = "発話するテキスト")]
13 | public string Text { get; set; }
14 | [Option('n', "Name", Required = false, HelpText = "音声合成エンジン名")]
15 | public string Name { get; set; }
16 | [Option('s', "speaker", Required = false, HelpText = "再生するスピーカー")]
17 | public string Speaker { get; set; }
18 | [Option('o', "output", Required = false, HelpText = "出力ファイル名(.wav)")]
19 | public string Output { get; set; }
20 | [Option('v', "verbose", Required = false, HelpText = "音声合成エンジン、スピーカーの列挙")]
21 | public bool Verbose { get; set; } = false;
22 | [Option('w', "whisper", Required = false, HelpText = "ささやき声で出力")]
23 | public bool Whisper { get; set; } = false;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/SpeechSample/Program.cs:
--------------------------------------------------------------------------------
1 | using CommandLine;
2 | using AudioSwitcher.AudioApi.CoreAudio;
3 | using Speech;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using System.Net;
10 | using System.Security.Cryptography;
11 | using Speech.Effect;
12 | using System.Threading;
13 |
14 | namespace SpeechSample
15 | {
16 | class Program
17 | {
18 | static IEnumerable devices;
19 |
20 | static string name;
21 | static bool finished = false;
22 | [MTAThread]
23 | static void Main(string[] args)
24 | {
25 | try
26 | {
27 | Parser.Default.ParseArguments(args)
28 | .WithParsed(opt =>
29 | {
30 | string[] voices = GetLibraryName();
31 | DateTime now = DateTime.Now;
32 | string date = now.ToString("yyyy年 MM月 dd日");
33 | string time = now.ToString("HH時 mm分 ss秒");
34 | string text = time + "です。";
35 |
36 | string name = voices[0];
37 | string speaker = "";
38 | string output = "";
39 |
40 | bool interactiveMode = true;
41 | if (opt.Verbose)
42 | {
43 | ShowVerbose();
44 | return;
45 | }
46 | if (opt.Text != null)
47 | {
48 | interactiveMode = false;
49 | text = opt.Text.Replace("{date}", date).Replace("{time}",time);
50 | }
51 | if (opt.Name != null)
52 | {
53 | interactiveMode = false;
54 | name = opt.Name;
55 | }
56 | if (opt.Speaker != null)
57 | {
58 | devices = new CoreAudioController().GetPlaybackDevices();
59 | speaker = opt.Speaker;
60 | ChangeSpeaker(speaker);
61 | }
62 | if (opt.Output != null)
63 | {
64 | interactiveMode = false;
65 | output = opt.Output;
66 | }
67 | if (interactiveMode)
68 | {
69 | InteractiveMode();
70 | return;
71 | }
72 | if (output == "")
73 | {
74 | if (opt.Whisper)
75 | {
76 | WhisperMode(name, text);
77 | }
78 | else
79 | {
80 | OneShotPlayMode(name, text);
81 | }
82 | }
83 | else
84 | {
85 | RecordMode(name, text, output);
86 | }
87 | while (!finished)
88 | {
89 | Task.Delay(100);
90 | }
91 | });
92 | }catch (Exception ex)
93 | {
94 | Console.WriteLine(ex.Message);
95 | return;
96 | }
97 | }
98 |
99 | private static string[] GetLibraryName()
100 | {
101 | var engines = SpeechController.GetAllSpeechEngine();
102 | var names = from c in engines
103 | select c.LibraryName;
104 | return names.ToArray();
105 | }
106 |
107 | private static void OneShotPlayMode(string libraryName, string text)
108 | {
109 |
110 | var engines = SpeechController.GetAllSpeechEngine();
111 | var engine = SpeechController.GetInstance(libraryName);
112 | if (engine == null)
113 | {
114 | Console.WriteLine($"{libraryName} を起動できませんでした。");
115 | return;
116 | }
117 | engine.Activate();
118 | engine.Finished += (s, a) =>
119 | {
120 | finished = true;
121 | engine.Dispose();
122 | };
123 | engine.Play(text);
124 |
125 | }
126 | private static void WhisperMode(string libraryName, string text)
127 | {
128 | string tempFile = "normal.wav";
129 | string whisperFile = "whisper.wav";
130 |
131 | var engines = SpeechController.GetAllSpeechEngine();
132 | var engine = SpeechController.GetInstance(libraryName);
133 | if (engine == null)
134 | {
135 | Console.WriteLine($"{libraryName} を起動できませんでした。");
136 | return;
137 | }
138 | engine.Activate();
139 |
140 | SoundRecorder recorder = new SoundRecorder(tempFile);
141 | {
142 | recorder.PostWait = 300;
143 |
144 | engine.Finished += (s, a) =>
145 | {
146 | finished = true;
147 | };
148 |
149 | recorder.Start();
150 | engine.Play(text);
151 | }
152 |
153 | while (!finished)
154 | {
155 | Thread.Sleep(100);
156 | }
157 | engine.Dispose();
158 | Task t = recorder.Stop();
159 | t.Wait();
160 | // ささやき声に変換
161 | Whisper whisper = new Whisper();
162 | Wave wave = new Wave();
163 | wave.Read(tempFile);
164 | whisper.Convert(wave);
165 | wave.Write(whisperFile, wave.Data);
166 |
167 | //// 変換した音声を再生
168 | SoundPlayer sp = new SoundPlayer();
169 | sp.Play(whisperFile);
170 |
171 |
172 | }
173 | public static void RecordMode(string libraryName, string text, string outputFilename)
174 | {
175 | SoundRecorder recorder = new SoundRecorder(outputFilename);
176 | recorder.PostWait = 300;
177 |
178 | var engines = SpeechController.GetAllSpeechEngine();
179 | var engine = SpeechController.GetInstance(libraryName);
180 | if (engine == null)
181 | {
182 | Console.WriteLine($"{libraryName} を起動できませんでした。");
183 | return;
184 | }
185 |
186 | engine.Activate();
187 | engine.Finished += (s, a) =>
188 | {
189 | Task t = recorder.Stop();
190 | t.Wait();
191 | finished = true;
192 | engine.Dispose();
193 | };
194 | recorder.Start();
195 | engine.Play(text);
196 | }
197 | private static void InteractiveMode()
198 | {
199 | ShowVerbose();
200 |
201 | // ライブラリ名を入力(c.LibraryName列)
202 | Console.Write("\r\nLibraryName> ");
203 | name = Console.ReadLine().Trim();
204 |
205 | // 対象となるライブラリを実行
206 | var engine = SpeechController.GetInstance(name);
207 | if (engine == null)
208 | {
209 | Console.WriteLine($"{name} を起動できませんでした。");
210 | Console.ReadKey();
211 | return;
212 | }
213 | // 設定した音声の再生が終了したときに呼び出される処理を設定
214 | engine.Finished += Engine_Finished;
215 |
216 | // 音声合成エンジンを起動
217 | engine.Activate();
218 | engine.SetVolume(1.0f);
219 | engine.SetPitch(1.0f);
220 | engine.SetSpeed(1.0f);
221 | engine.SetPitchRange(1.0f);
222 | string message = $"音声合成エンジン {engine.Info.EngineName}、{engine.Info.LibraryName}を起動しました。";
223 | engine.Play(message); // 音声再生は非同期実行される
224 | Console.WriteLine(message);
225 |
226 | string line = "";
227 | while(true)
228 | {
229 | line = Console.ReadLine();
230 | if (line.Trim() == "")
231 | {
232 | engine.Dispose();
233 | return;
234 | }
235 | try
236 | {
237 | engine.Stop(); // 喋っている途中に文字が入力されたら再生をストップ
238 | engine.Play(line); // 音声再生は非同期実行される
239 | Console.WriteLine($"Volume: {engine.GetVolume()}, Speed: {engine.GetSpeed()}, Pitch: {engine.GetPitch()}, PitchRange: {engine.GetPitchRange()}");
240 | }catch(Exception ex)
241 | {
242 | Console.WriteLine(ex.Message);
243 | }
244 | }
245 | }
246 |
247 | private static void ChangeSpeaker(string name)
248 | {
249 | var speakers = (from c in devices
250 | where c.FullName.IndexOf(name) >= 0
251 | select c).ToArray();
252 | if (speakers.Length > 0)
253 | {
254 | speakers[0].SetAsDefault();
255 | }
256 | else
257 | {
258 | Console.WriteLine("Speaker not found.");
259 | }
260 | }
261 | private static void Engine_Finished(object sender, EventArgs e)
262 | {
263 | Console.WriteLine("* 再生完了 *");
264 | Console.Write($"{name}> ");
265 | }
266 |
267 | private static void ShowVerbose()
268 | {
269 | // インストール済み音声合成ライブラリの列挙
270 | var names = GetLibraryName();
271 | Console.WriteLine("インストール済み音声合成ライブラリ");
272 | string bit = Environment.Is64BitProcess ? "64 bit" : "32 bit";
273 | Console.WriteLine($"※ このアプリケーションは {bit}プロセスのため、{bit}のライブラリのみが列挙されます。");
274 | Console.WriteLine("-----");
275 | foreach (var s in names)
276 | {
277 | Console.WriteLine(s);
278 | }
279 | Console.WriteLine("-----");
280 |
281 | // 接続先スピーカーの列挙
282 | Console.WriteLine("接続先スピーカー");
283 | Console.WriteLine("-----");
284 | devices = new CoreAudioController().GetPlaybackDevices();
285 | string speaker = (devices.ToArray())[0].FullName;
286 | foreach (var d in devices)
287 | {
288 | Console.WriteLine($"{d.FullName}");
289 | }
290 | Console.WriteLine("-----");
291 | }
292 | }
293 | }
294 |
--------------------------------------------------------------------------------
/src/SpeechSample/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // アセンブリに関する一般情報は以下の属性セットをとおして制御されます。
6 | // アセンブリに関連付けられている情報を変更するには、
7 | // これらの属性値を変更してください。
8 | [assembly: AssemblyTitle("SpeechSample")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("SpeechSample")]
13 | [assembly: AssemblyCopyright("Copyright © 2020 ksasao")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから
18 | // 参照不可能になります。COM からこのアセンブリ内の型にアクセスする場合は、
19 | // その型の ComVisible 属性を true に設定してください。
20 | [assembly: ComVisible(false)]
21 |
22 | // このプロジェクトが COM に公開される場合、次の GUID が typelib の ID になります
23 | [assembly: Guid("bb36cbbb-d522-409b-a6bf-7ac39c77a9ff")]
24 |
25 | // アセンブリのバージョン情報は次の 4 つの値で構成されています:
26 | //
27 | // メジャー バージョン
28 | // マイナー バージョン
29 | // ビルド番号
30 | // Revision
31 | //
32 | // すべての値を指定するか、下のように '*' を使ってビルドおよびリビジョン番号を
33 | // 既定値にすることができます:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/src/SpeechSample/SpeechSample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}
8 | Exe
9 | Properties
10 | SpeechSample
11 | SpeechSample
12 | v4.8
13 | 512
14 | true
15 |
16 |
17 |
18 | x86
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 | false
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | {7787a468-a1bb-4077-85bc-543258610060}
58 | Speech
59 |
60 |
61 |
62 |
63 | 3.0.0.1
64 |
65 |
66 | 2.8.0
67 |
68 |
69 |
70 |
77 |
--------------------------------------------------------------------------------
/src/SpeechWebServer/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/SpeechWebServer/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // アセンブリに関する一般情報は以下の属性セットをとおして制御されます。
6 | // アセンブリに関連付けられている情報を変更するには、
7 | // これらの属性値を変更してください。
8 | [assembly: AssemblyTitle("SpeechWebServer")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("SpeechWebServer")]
13 | [assembly: AssemblyCopyright("Copyright © 2020 ksasao")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから
18 | // 参照不可能になります。COM からこのアセンブリ内の型にアクセスする場合は、
19 | // その型の ComVisible 属性を true に設定してください。
20 | [assembly: ComVisible(false)]
21 |
22 | // このプロジェクトが COM に公開される場合、次の GUID が typelib の ID になります
23 | [assembly: Guid("bb36cbbb-d522-409b-a6bf-7ac39c77a9ff")]
24 |
25 | // アセンブリのバージョン情報は次の 4 つの値で構成されています:
26 | //
27 | // メジャー バージョン
28 | // マイナー バージョン
29 | // ビルド番号
30 | // Revision
31 | //
32 | // すべての値を指定するか、下のように '*' を使ってビルドおよびリビジョン番号を
33 | // 既定値にすることができます:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("0.0.4.0")]
36 | [assembly: AssemblyFileVersion("0.0.4.0")]
37 |
--------------------------------------------------------------------------------
/src/SpeechWebServer/SpeechWebServer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {471C9269-34CC-45BC-996E-2610DA84C4C8}
8 | Exe
9 | Properties
10 | SpeechWebServer
11 | SpeechWebServer
12 | v4.8
13 | 512
14 | true
15 |
16 |
17 |
18 | AnyCPU
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 | true
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 | false
37 |
38 |
39 | app.manifest
40 |
41 |
42 | true
43 | bin\x64\Debug\
44 | DEBUG;TRACE
45 | full
46 | AnyCPU
47 | 7.3
48 | prompt
49 | MinimumRecommendedRules.ruleset
50 | false
51 |
52 |
53 | bin\x64\Release\
54 | TRACE
55 | true
56 | pdbonly
57 | x64
58 | 7.3
59 | prompt
60 | MinimumRecommendedRules.ruleset
61 | true
62 |
63 |
64 |
65 | ..\packages\AudioSwitcher.AudioApi.3.0.0\lib\net40\AudioSwitcher.AudioApi.dll
66 |
67 |
68 | ..\packages\AudioSwitcher.AudioApi.CoreAudio.3.0.0.1\lib\net40\AudioSwitcher.AudioApi.CoreAudio.dll
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | {7787a468-a1bb-4077-85bc-543258610060}
92 | Speech
93 |
94 |
95 |
96 |
97 | PreserveNewest
98 |
99 |
100 |
101 |
108 |
--------------------------------------------------------------------------------
/src/SpeechWebServer/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
52 |
59 |
60 |
61 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/src/SpeechWebServer/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | GitHubに移動します
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/SpeechWebServer/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/TTSController.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30611.23
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpeechSample", "SpeechSample\SpeechSample.csproj", "{BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speech", "Speech\Speech.csproj", "{7787A468-A1BB-4077-85BC-543258610060}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpeechWebServer", "SpeechWebServer\SpeechWebServer.csproj", "{471C9269-34CC-45BC-996E-2610DA84C4C8}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Debug|x64 = Debug|x64
16 | Debug|x86 = Debug|x86
17 | Release|Any CPU = Release|Any CPU
18 | Release|x64 = Release|x64
19 | Release|x86 = Release|x86
20 | EndGlobalSection
21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Debug|x64.ActiveCfg = Debug|Any CPU
25 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Debug|x64.Build.0 = Debug|Any CPU
26 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Debug|x86.ActiveCfg = Debug|Any CPU
27 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Debug|x86.Build.0 = Debug|Any CPU
28 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Release|x64.ActiveCfg = Release|Any CPU
31 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Release|x64.Build.0 = Release|Any CPU
32 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Release|x86.ActiveCfg = Release|Any CPU
33 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Release|x86.Build.0 = Release|Any CPU
34 | {7787A468-A1BB-4077-85BC-543258610060}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {7787A468-A1BB-4077-85BC-543258610060}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {7787A468-A1BB-4077-85BC-543258610060}.Debug|x64.ActiveCfg = Debug|Any CPU
37 | {7787A468-A1BB-4077-85BC-543258610060}.Debug|x64.Build.0 = Debug|Any CPU
38 | {7787A468-A1BB-4077-85BC-543258610060}.Debug|x86.ActiveCfg = Debug|x86
39 | {7787A468-A1BB-4077-85BC-543258610060}.Debug|x86.Build.0 = Debug|x86
40 | {7787A468-A1BB-4077-85BC-543258610060}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {7787A468-A1BB-4077-85BC-543258610060}.Release|Any CPU.Build.0 = Release|Any CPU
42 | {7787A468-A1BB-4077-85BC-543258610060}.Release|x64.ActiveCfg = Release|Any CPU
43 | {7787A468-A1BB-4077-85BC-543258610060}.Release|x64.Build.0 = Release|Any CPU
44 | {7787A468-A1BB-4077-85BC-543258610060}.Release|x86.ActiveCfg = Release|x86
45 | {7787A468-A1BB-4077-85BC-543258610060}.Release|x86.Build.0 = Release|x86
46 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
48 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Debug|x64.ActiveCfg = Debug|x64
49 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Debug|x64.Build.0 = Debug|x64
50 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Debug|x86.ActiveCfg = Debug|Any CPU
51 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Debug|x86.Build.0 = Debug|Any CPU
52 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
53 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Release|Any CPU.Build.0 = Release|Any CPU
54 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Release|x64.ActiveCfg = Release|x64
55 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Release|x64.Build.0 = Release|x64
56 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Release|x86.ActiveCfg = Release|Any CPU
57 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Release|x86.Build.0 = Release|Any CPU
58 | EndGlobalSection
59 | GlobalSection(SolutionProperties) = preSolution
60 | HideSolutionNode = FALSE
61 | EndGlobalSection
62 | GlobalSection(ExtensibilityGlobals) = postSolution
63 | SolutionGuid = {3450397B-1546-4FA1-8B59-1653317CE540}
64 | EndGlobalSection
65 | EndGlobal
66 |
--------------------------------------------------------------------------------
/src/TTSController_vs2019.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30611.23
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpeechSample", "SpeechSample\SpeechSample.csproj", "{BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speech", "Speech\Speech.csproj", "{7787A468-A1BB-4077-85BC-543258610060}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpeechWebServer", "SpeechWebServer\SpeechWebServer.csproj", "{471C9269-34CC-45BC-996E-2610DA84C4C8}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Debug|x64 = Debug|x64
16 | Debug|x86 = Debug|x86
17 | Release|Any CPU = Release|Any CPU
18 | Release|x64 = Release|x64
19 | Release|x86 = Release|x86
20 | EndGlobalSection
21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Debug|x64.ActiveCfg = Debug|Any CPU
25 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Debug|x64.Build.0 = Debug|Any CPU
26 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Debug|x86.ActiveCfg = Debug|Any CPU
27 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Debug|x86.Build.0 = Debug|Any CPU
28 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Release|x64.ActiveCfg = Release|Any CPU
31 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Release|x64.Build.0 = Release|Any CPU
32 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Release|x86.ActiveCfg = Release|Any CPU
33 | {BB36CBBB-D522-409B-A6BF-7AC39C77A9FF}.Release|x86.Build.0 = Release|Any CPU
34 | {7787A468-A1BB-4077-85BC-543258610060}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {7787A468-A1BB-4077-85BC-543258610060}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {7787A468-A1BB-4077-85BC-543258610060}.Debug|x64.ActiveCfg = Debug|Any CPU
37 | {7787A468-A1BB-4077-85BC-543258610060}.Debug|x64.Build.0 = Debug|Any CPU
38 | {7787A468-A1BB-4077-85BC-543258610060}.Debug|x86.ActiveCfg = Debug|x86
39 | {7787A468-A1BB-4077-85BC-543258610060}.Debug|x86.Build.0 = Debug|x86
40 | {7787A468-A1BB-4077-85BC-543258610060}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {7787A468-A1BB-4077-85BC-543258610060}.Release|Any CPU.Build.0 = Release|Any CPU
42 | {7787A468-A1BB-4077-85BC-543258610060}.Release|x64.ActiveCfg = Release|Any CPU
43 | {7787A468-A1BB-4077-85BC-543258610060}.Release|x64.Build.0 = Release|Any CPU
44 | {7787A468-A1BB-4077-85BC-543258610060}.Release|x86.ActiveCfg = Release|x86
45 | {7787A468-A1BB-4077-85BC-543258610060}.Release|x86.Build.0 = Release|x86
46 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
48 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Debug|x64.ActiveCfg = Debug|x64
49 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Debug|x64.Build.0 = Debug|x64
50 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Debug|x86.ActiveCfg = Debug|Any CPU
51 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Debug|x86.Build.0 = Debug|Any CPU
52 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
53 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Release|Any CPU.Build.0 = Release|Any CPU
54 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Release|x64.ActiveCfg = Release|x64
55 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Release|x64.Build.0 = Release|x64
56 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Release|x86.ActiveCfg = Release|Any CPU
57 | {471C9269-34CC-45BC-996E-2610DA84C4C8}.Release|x86.Build.0 = Release|Any CPU
58 | EndGlobalSection
59 | GlobalSection(SolutionProperties) = preSolution
60 | HideSolutionNode = FALSE
61 | EndGlobalSection
62 | GlobalSection(ExtensibilityGlobals) = postSolution
63 | SolutionGuid = {3450397B-1546-4FA1-8B59-1653317CE540}
64 | EndGlobalSection
65 | EndGlobal
66 |
--------------------------------------------------------------------------------