//////////////////
// Text2Speech.cs
//////////////////
using System;
using System.Windows.Forms;
using System.Drawing;		//Size等を使う為
using System.IO;			//StreamReaderを使う為
using System.Text;			//Encodingを使う為
using System.Speech.Synthesis;
using System.Speech.AudioFormat;
//コンパイル時に"/reference:C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Speech\v4.0_4.0.0.0__31bf3856ad364e35\System.Speech.dll"オプションが必要

//昔はSpeechLib.dllがあったようだ
//https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ms720177(v=vs.85)
//https://www.valuestar.work/news/archives/44
//https://petile.com/mahoroba/e1590.html
//using SpeechLib;
//SpVoice sp = new SpeechLib.SpVoice();
//sp.Speak("テスト");

namespace Text2Speech
{
	public partial class AppForm : Form
	{
		//フォームのコントロール
		TextBox txtBox;
		Button clrBtn, rdBtn, spBtn, sltBtn, swBtn, extBtn;
		//スピーチシンセサイザー
		SpeechSynthesizer synth;

		[STAThread]
		public static void Main()
		{
			AppForm ap = new AppForm();
			Application.Run(ap);
		}

		public AppForm()
		{
			this.Size = new Size(640, 480);
			this.MinimumSize = new Size(320, 260);
			this.Text = "System.Speech Test";
			this.Load += AppForm_Load;
		}

		private void AppForm_Load(object sender, EventArgs e)
		{
			//Clearボタン
			clrBtn = new Button();
			clrBtn.Location = new Point(ClientSize.Width - clrBtn.Width - 10, 10);
			clrBtn.Text = "文章消去";
			clrBtn.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
			clrBtn.Click += Button1_Click;
			this.Controls.Add(clrBtn);

			//Readボタン
			rdBtn = new Button();
			rdBtn.Location = new Point(ClientSize.Width - rdBtn.Width - 10, clrBtn.Height + 20);
			rdBtn.Text = "読み込み";
			rdBtn.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
			rdBtn.Click += Button2_Click;
			this.Controls.Add(rdBtn);

			//Speechボタン
			spBtn = new Button();
			spBtn.Location = new Point(ClientSize.Width - spBtn.Width - 10, clrBtn.Height + rdBtn.Height + 30);
			spBtn.Text = "読み上げ";
			spBtn.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
			spBtn.Click += Button3_Click;
			this.Controls.Add(spBtn);

			//Selectボタン
			sltBtn = new Button();
			sltBtn.Location = new Point(ClientSize.Width - sltBtn.Width - 10, clrBtn.Height + rdBtn.Height + spBtn.Height  + 40);
			sltBtn.Text = "音声選択";
			sltBtn.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
			sltBtn.Click += Button4_Click;
			this.Controls.Add(sltBtn);

			//Showボタン
			swBtn = new Button();
			swBtn.Location = new Point(ClientSize.Width - swBtn.Width - 10, clrBtn.Height + rdBtn.Height + spBtn.Height + sltBtn.Height + 50);
			swBtn.Text = "詳細";
			swBtn.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
			swBtn.Click += Button5_Click;
			this.Controls.Add(swBtn);

			//Exitボタン
			extBtn = new Button();
			extBtn.Location = new Point(ClientSize.Width - extBtn.Width - 10, ClientSize.Height - extBtn.Height - 20);
			extBtn.Text = "終了";
			extBtn.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right);
			extBtn.Click += Button6_Click;
			this.Controls.Add(extBtn);

			//テキストボックス
			txtBox = new TextBox();
			//位置
			txtBox.Location = new Point(10, 10);
			txtBox.Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right);
			//サイズ
			txtBox.Width = ClientSize.Width - clrBtn.Width - 30;
			txtBox.Height = ClientSize.Height - 20;
			//その他プロパティ
			txtBox.Multiline = true;
			txtBox.ScrollBars = ScrollBars.Both;
			txtBox.WordWrap= true;
			txtBox.Font = new Font(txtBox.Font.OriginalFontName, 12f);	//フォントサイズを変更する
			//FormにTextBoxを追加
			this.Controls.Add(txtBox);

			//スピーチシンセサイザーの初期化
			try
			{
				//音声合成インスタンスの作成
				synth = new SpeechSynthesizer();
				//音声出力をデフォルトデバイスにする
				synth.SetOutputToDefaultAudioDevice();
			}
			catch
			{
				MessageBox.Show("スピーチシンセサイザーが使えません。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
				//終了処理
				Close();
			}
		}

		//終了処理
		protected override void OnFormClosing(FormClosingEventArgs e)
		{
			base.OnFormClosing(e);
			DialogResult dr = MessageBox.Show("終了しますか？", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
			if(dr == DialogResult.No)
			{
				e.Cancel = true;
			}
			else
			{
				//現在ペンディングの音声キューをキャンセルする
				synth.SpeakAsyncCancelAll();
				//リソースの開放
				synth.Dispose();
			}
		}

		private void Button1_Click(object sender, EventArgs e)
		{
			//txtBoxをクリアする
			txtBox.Clear();
		}

		private void Button2_Click(object sender, EventArgs e)
		{
			//txtBoxにファイルを読む
			OpenFileDialog ofDlg = new OpenFileDialog();
			ofDlg.FileName = "*.*";						//初期ファイル名の指定
			ofDlg.Filter = "すべてのファイル(*.*)|*.*";	//ファイルフィルターの指定
			ofDlg.FilterIndex = 1;						//ファイルフィルターインデックスの指定
			ofDlg.RestoreDirectory = true;				//ダイアログボックスを閉じる前に現在のディレクトリを復元するようにする
			ofDlg.CheckFileExists = true;				//存在しないファイルの名前が指定されたとき警告を表示する
			ofDlg.CheckPathExists = true;				//存在しないパスが指定されたとき警告を表示する
			ofDlg.Multiselect = true;					//複数ファイルの選択を行う
			ofDlg.InitialDirectory = @".\";				// デフォルトのフォルダを指定する
			ofDlg.Title = "ファイルを開く";				//ダイアログのタイトルを指定する
			if(ofDlg.ShowDialog() == DialogResult.OK)	//ダイアログを表示する
			{
				//StreamReaderでtxtBoxに"Shift_JIS"ファイルを読み込む
				StreamReader sr = new StreamReader(ofDlg.FileName, Encoding.GetEncoding("Shift_JIS"));
 				txtBox.Text = sr.ReadToEnd();
				sr.Close();
			}
			else
			{
				MessageBox.Show("キャンセルされました。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
			// オブジェクトを破棄する
			ofDlg.Dispose();
		}

		private void Button3_Click(object sender, EventArgs e)
		{
			DialogResult dr;	//MessageBoxの回答
			//音声の状況を確認する
			if(synth.State != SynthesizerState.Ready)	//0 - SynthesizerState.Ready、1 - SynthesizerState.Speaking、2 - SynthesizerState.Paused
			{
				string str = (synth.State == SynthesizerState.Speaking) ? "読み上げ中です。中断しますか？" : "読み上げ停止中です。中断しますか？";
				dr = MessageBox.Show(str, "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
				if(dr == DialogResult.Yes)
				{
					//すべてのペンディング読み上げを中断
					synth.SpeakAsyncCancelAll();
					//Readyになるまで〜ループを回す
					while(synth.State != SynthesizerState.Ready);
				}
				else
					return;		//「読み上げ」をキャンセル
			}
			//音量設定
			synth.Volume = 50;	//0 - 100で設定
			//音速設定
			synth.Rate = 0;	//-10 - 10で設定
			//同期、非同期の選択
			dr = MessageBox.Show("同期で読みますか？（はい）、\r\n非同期にしますか？（いいえ）", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
			if(dr == DialogResult.Yes)
			{
				dr = MessageBox.Show("SSMLで読みますか？（はい）、\r\nTextにしますか？（いいえ）", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
				if(dr == DialogResult.Yes)
					//SSMLテキストによる読み上げ
					synth.SpeakSsml(txtBox.Text);	//SSML同期読み上げ−中断できない
				else
					//テキスト読み上げ
					synth.Speak(txtBox.Text);		//同期読み上げ−中断できない
			}
			else
			{
				dr = MessageBox.Show("SSMLで読みますか？（はい）、\r\nTextにしますか？（いいえ）", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
				if(dr == DialogResult.Yes)
				{
					//SSMLテキストによる読み上げ
					synth.SpeakSsmlAsync(txtBox.Text);	//非同期読み上げ−中断できる
					//SSMLテキストのPrompt読み上げ
					//Prompt pmt= new Prompt(txtBox.Text);
					//synth.SpeakAsync(pmt);			//非同期読み上げ−中断できる
				}
				else
					//テキストによる読み上げ
					synth.SpeakAsync(txtBox.Text);	//非同期読み上げ−中断できる
			}
		}

		private void Button4_Click(object sender, EventArgs e)
		{
			//インストールされた音声の列挙
			foreach (InstalledVoice voice in synth.GetInstalledVoices())
			{
				string Voice = "";
				VoiceInfo vinfo = voice.VoiceInfo;
				//音声様式情報をテキストボックスに表示
				Voice += "Name:	" + vinfo.Name + "\r\n";
				Voice += "Culture:	" + vinfo.Culture + "\r\n";
				Voice += "Age:	" + vinfo.Age + "\r\n";
				Voice += "Gender:	" + vinfo.Gender + "\r\n";
				DialogResult dr = MessageBox.Show(Voice + "この音声でよろしいですか？", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
				if(dr == DialogResult.Yes)
				{
					synth.SelectVoice(vinfo.Name);		//音声の設定
					break;;
				}
			}
		}

		private void Button5_Click(object sender, EventArgs e)
		{
			string buff = txtBox.Text;	//現在のテキストを退避させる
			txtBox.Clear();				//現在のテキストを消去
			//インストールされた音声の列挙（現在の音声は synth.Voice プロパティで取得できる）
			foreach (InstalledVoice voice in synth.GetInstalledVoices())
			{
				string Voice = "";						//音声源情報(SelectVoice(String) が使える）
				VoiceInfo vinfo = voice.VoiceInfo;
				//音声様式情報をテキストボックスに表示
				Voice += "Name:		" + vinfo.Name + "\r\n";
				Voice += "Culture:		" + vinfo.Culture + "\r\n";
				Voice += "Age:		" + vinfo.Age + "\r\n";
				Voice += "Gender:		" + vinfo.Gender + "\r\n";
				Voice += "Description:	" + vinfo.Description + "\r\n";
				Voice += "ID:		" + vinfo.Id + "\r\n";
				Voice += "Enabled:		" + voice.Enabled + "\r\n";
				txtBox.Text += "＜音声＞\r\n" + Voice;
				//音声フォーマット（Audio Format）を調べる
				string AudioFormats = "＜音声フォーマット＞\r\n";	//Voice.SupportedAudioFormats−音声がサポートするオーディオ形式のコレクション
				foreach (SpeechAudioFormatInfo fmt in vinfo.SupportedAudioFormats)
				{
					AudioFormats += String.Format("{0}\r\n", fmt.EncodingFormat.ToString());
				}
				if(vinfo.SupportedAudioFormats.Count != 0)
				{
					txtBox.Text += AudioFormats + "\r\n";
				}
				else
				{
					txtBox.Text += "＜音声フォーマット＞\r\n利用できる音声フォーマットがありません。" + "\r\n";
				}
				//その他情報を得る
				string AdditionalInfo = "＜その他情報＞\r\n";
				foreach (string key in vinfo.AdditionalInfo.Keys)
				{
					AdditionalInfo += String.Format("{0}: {1}\n", key, vinfo.AdditionalInfo[key]);
					}
				txtBox.Text += AdditionalInfo + "\r\n\r\n";
			}
			DialogResult dr = MessageBox.Show("テキストを復元しますか？", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
			if(dr == DialogResult.Yes)
			{
				txtBox.Text = buff;
			}
		}

		private void Button6_Click(object sender, EventArgs e)
		{
			//終了処理
			Close();
		}
	}
}
