- 投稿日:2020-04-30T23:42:01+09:00
文字列の中に変数を入れる記法
自分はもともとRubyでプログラミングに触れて、eRubyでCGIを書いたりしてたから文字列の中に
{}でくくった変数を入れるのを多用していたのですが。HelloWorld.rbstr1 = "Hello" str2 = "World!" print "#{str1}, #{str2}\n"これをC#で書こうとしたらこうなります。
HelloWorld.csvar str1 = "Hello"; var str2 = "World!"; Console.Write($"{str1}, {str2}\n") ;別解でこういう風にも書けるようです。
HelloWorldAlt.csvar str1 = "Hello"; var str2 = "World!"; Console.Write("{0}, {1}\n", str1, str2);ところで、最近Javaをはじめたのですが、Javaだとこういう風に書くみたいです。
HelloWorld.javavar str1 = "Hello"; var str2 = "World!"; System.out.printf("%s, %s\n", str1, str2);後で書いた
HelloWorldAlt.csのC#のパターンに近いですね。
ぶっちゃけJavaが一番わかりづらいので、今後C#で書く時もJava表記に近い後者の記法を書いていこうかなと思いました。
- 投稿日:2020-04-30T21:58:27+09:00
Yahoo天気をスクレイピングしてみた(C#編)
初めに
前回Pythonでyahoo天気をスクレイピングしましたが、自分が得意なC#でもやってみたいと思い挑戦してみました。Python版が2月中旬だったので2か月越しになりますが、実装自体はPython版作った次の日には終わっていました。忙しく存在自体を忘れてた...。C#は一部界隈では統一論もあるくらい色々使える言語なので天気のスクレイピングは必ず需要があり自分も色々なところに実装できるようメモ代わりに記事を残します。
Python編は
https://qiita.com/MonoShobel/items/c36bb0e8eecf538248b4
にあります。開発環境
Windows10
VisualStudio2019スクレイビング用のライブラリ
スクレイビングを簡単にできるライブラリがあります。
「AngleSharp」というライブラリがあるのでこれをNugetからインストールします。
インストール方法は後述します。
AngleSharpのホームページ
https://anglesharp.github.io/Python編で書いたけどもう一度
Yahoo天気ですが、たぶんスクレイピングは禁止されていません。
Yahooファイナンスは明確に禁止されていましたが、天気に関しては禁止しているような記述は見つかりませんでした。
ただ節度は守りましょう。定期的にスクレイピングするとしても天気は1時間または30分ごとくらいのアクセスで十分だと思います。やること
色々取りたい情報はありますがとりあえず東京の今日の天気を取得しました。
下記の画像の今日の天気を取得します。
コードを書く前に
C#といえばVisualStudio。(Visual Codeでもできますが)
まずはプロジェクトの作成を行っていきます。
次の画面でコンソールアプリ(.NET Core)を選択します。右下の次へを選びます。コンソールアプリ(.NET Core)をダブルクリックでも大丈夫です。コンソールアプリ(.NET Core)が見つからない場合は、右上でC#、Windowsを選択すると上に出てきます。
AngleSharpをダウンロードします。VisualStduinoのコードを記述する最初の画面が開いたら下記の画像の通り、画面上部の「プロジェクト」のタブを選んで、その中から「NuGetパッケージの管理」をクリックしてください。
画像の画面が開いたら、「参照」を押下し、検索ボックスに「AngleSharp」を入力すると、画像の通り目的のライブラリが出てくるので、
選択して、右の画面のインストールを押下してください。
7.元の画面に戻ったら下記の画像の赤枠の部分を同じ場所に入力します。
コード
実際にコードを実行すれば今日の東京の天気を取得できます。
using AngleSharp;// 取得対象の設定 var htmlUrl = $"https://weather.yahoo.co.jp/weather/jp/13/4410.html"; var querySelector = $"#main > div.forecastCity > table > tbody > tr > td:nth-child(2) > div > p.pict"; // HTMLドキュメントの取得 var document = BrowsingContext.New(Configuration.Default.WithDefaultLoader()).OpenAsync(htmlUrl).Result; // クエリセレクタでデータの取得 var element = document.QuerySelector(querySelector); /// 天気の文字列を種痘 string tenki = element.TextContent; Console.WriteLine(tenki); Console.ReadKey();終わりに
C#でもライブラリを使えば簡単にスクレイビング出来て驚きました。
このプログラムを元に色々作ってみたいと思います。
- 投稿日:2020-04-30T19:27:35+09:00
Google Cloud Text-To-Speechを利用してUnityでキャラクターをフルボイスに!
タイトルの通りです.
UnityでもGoogle CloudのAPIを利用することで(簡単に)音声合成を行うことができます.余談
Google CloudのText-To-Speech APIを利用することで誰でも手軽に音声合成を行うことができます.
しかし,これを利用した記事は現状あまり公開されていません.特にC#(.NET)環境での動作については,Google Cloudのドキュメンテーションページでも
と非常に残念な感じです.今回これを触る機会があり,動くようにするために結構苦労しました.
実装の際特に詰まった部分について解説しながら動くサンプルを示したいと思います.準備
Unityプロジェクトに導入するためにあらかじめ行う作業があります.
Google Cloud Platformの利用登録
まず,Google Cloudに開発者申請を行い,APIリクエストを送る際の認証情報を取得する必要があります.
Google Cloud Platformのページにアクセスし,利用者登録を行います.
住所,名前,電話番号,クレジットカード番号を入力する欄がありますが,個人開発者であれば基本的に無料で利用できます.料金ページに利用料の記載があります.
Text-To-Speechの場合,クラウドに送信した文字数に応じて料金が変動します.
通常の音声合成とWaveNet音声(ちょっとリアルな声)で値段が変わり,以下のように無料枠と有料の場合の値段が定められています.
利用者登録を行った後は分かりやすい名前を付けてプロジェクトを作成します.
その後プロジェクトでCloud Text-To-Speech APIを有効にします.
左上のHamburgerタブからメニューを開き,「APIとサービス」から「ライブラリ」を選びます.
そこでCloud Text-To-Speechを選択し,APIを有効化します
次に実際に利用するAPIキーを作成します.
左上のHamburgerタブからメニューを開き,「IAMの管理」から「サービスアカウント」を選択し,サービスアカウントの作成を行います.
「作成」を選択し,次に出る組織の選択はスルーして大丈夫です.
次の「キーの作成」は必須です.
Json形式でキーを作成し,保存します.(このキーは厳重に管理してください)
これでGoogle Could Platform側での作業は終わりです.Unityにライブラリをインポートする
現在Google Cloud Text-To-SpeechをUnityで利用できるようなライブラリは存在しません.
なので自分で利用するライブラリ(dll)をインポートする必要があります.NuGetからライブラリをインポートする
Google Cloud APIはNuGetで.Net用ライブラリが公開されており,それを利用します.
UnityにNuGetライブラリをインポートするための便利なツールに「NuGetForUnity」というものがあるので,これを利用しUnityに導入していきます.
これを利用することで依存するライブラリをまとめてインポートできるので便利です.検索窓に「Cloud TextToSpeech」と入力することで目的のAPIが見つかるのでこれをインストールします.
これでAssetフォルダに利用するdllがインポートできましたが,このままでは
unload broken assemblyというエラーが出ます.
これはdll間で依存するライブラリを見つけられていないためで,これを回避するためにはいろいろ方法がありますが,今回は一番単純な「すべてのdllを同じディレクトリに置く」という手段を取ります.結構めんどくさいです.インポートを行った後
NuGetForUnityはもう不要なので,Assetのディレクトリから該当するフォルダを削除します.(dllの入っているPackagesフォルダは消さない!)
そしてpackages以下に存在するdllを全てpackagesの先頭フォルダに移し,纏めます.
これでエラーは出なくなったはずです.ランタイムで利用するライブラリをインポートする
NuGetからライブラリをインポートすることでエディタ上ではエラーが出なくなりますが,このままでは実行時に
DllNotFoundException: grpc_csharp_extやEntryPointNotFoundException: grpcsharp_native_callback_dispatcher_initといったエラーが出てしまいます.ランタイム(実行時)で利用するライブラリはhttps://github.com/jsmouret/grpc-unity-package からお借りします.
https://github.com/jsmouret/grpc-unity-package/releases から最新のgrpc-unity-package.zipをダウンロードし,Plugins/Grpc.Core/runtimes/win(利用環境によって変えてください)/x64/grpc_csharp_ex.dllをコピーし,先ほどNuGetからインポートしたフォルダに貼り付けます.これで実行時のエラーもなくなります.
スクリプトの用意
TextToSpeechSampleというC#スクリプトを新たに作成し,以下のスクリプトを貼り付けます.
using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Google.Apis.Auth.OAuth2; using Google.Cloud.TextToSpeech.V1; using Grpc.Auth; using Grpc.Core; using UnityEngine; using UnityEngine.UI; using WWUtils.Audio; using Debug = UnityEngine.Debug; public class TextToSpeechSample : MonoBehaviour { [SerializeField] private InputField inputField; [SerializeField] private Button button; [SerializeField] private AudioSource audioSource; [SerializeField] private string credential; private const string GcpUrl = "https://www.googleapis.com/auth/cloud-platform"; private const string ChannelTarget = "texttospeech.googleapis.com:443"; private TextToSpeechClient _client; private AudioConfig _audioConfig; private VoiceSelectionParams _voiceSelectionParams; private ChannelCredentials _credentials; private SynchronizationContext _context; private Stopwatch _stopwatch = new Stopwatch(); private void Start() { // ボタンを押したときのイベントを追加 button.onClick.AddListener(() => { var str = inputField.text; if (string.IsNullOrEmpty(str)) return; inputField.text = ""; CreateRequest(str); Debug.Log($"Send Request: {str}"); }); // 認証情報をResourceから読み込む var credentialStr = Resources.Load<TextAsset>(credential).text; var googleCredential = GoogleCredential.FromJson(credentialStr); _credentials = googleCredential.CreateScoped(GcpUrl).ToChannelCredentials(); var channel = new Channel(ChannelTarget, _credentials); _client = new TextToSpeechClientImpl(new TextToSpeech.TextToSpeechClient(channel), new TextToSpeechSettings()); // オプションを記述 _audioConfig = new AudioConfig() { AudioEncoding = AudioEncoding.Linear16, SampleRateHertz = 44100 }; // 声のパラメータを指定 // https://cloud.google.com/text-to-speech/docs/voices?hl=jaに記載されているものから選択できます _voiceSelectionParams = new VoiceSelectionParams() { SsmlGender = SsmlVoiceGender.Female, LanguageCode = "ja-JP" }; _context = SynchronizationContext.Current; } /// <summary> /// リクエストを送信する /// </summary> /// <param name="text">音声合成を行う対象の文</param> public void CreateRequest(string text) { var request = new SynthesizeSpeechRequest { Input = new SynthesisInput {Text = text}, AudioConfig = _audioConfig, Voice = _voiceSelectionParams }; _stopwatch.Restart(); // リクエストを非同期で送信し,返ってきた後に再生するメソッドに投げる Task.Run(async () => { SetAudioClip(await _client.SynthesizeSpeechAsync(request)); }); } /// <summary> /// Google CloudからのレスポンスをAudioClipに書き出し,再生する /// </summary> /// <param name="response">Google Cloudからのレスポンス</param> private void SetAudioClip(SynthesizeSpeechResponse response) { var bytes = response.AudioContent.ToByteArray(); // byte[]をAudioClipで利用できる形に変換する var wav = new WAV(bytes); Debug.Log("Get Response: Elapsed time " + _stopwatch.ElapsedMilliseconds + "ms.\nData Length: " + (wav.SampleCount * (1f / wav.Frequency) * 1000f).ToString("F0") + "ms."); _context.Post(_ => { // AudioSourceに新しいAudioClipを貼り付ける audioSource.clip = AudioClip.Create("TextToSpeech", wav.SampleCount, 1, wav.Frequency, false); audioSource.clip.SetData(wav.LeftChannel, 0); // AudioClipを再生 audioSource.Play(); }, null); } }このスクリプトでは以下の処理を記述しています
- ButtonとInputFieldのイベントを追加
- 認証キー(Json)を読みこむ
- Google Cloudとの接続を行うクライアントの作成
- リクエストを送信する
- リクエストの返信からAudioClipを作成し,再生する
Google Cloudからのレスポンスはbyte[]で送られてくるので,それをAudioClipで利用できるようfloat[]に変換する必要があります.
そのためにここで示されているWAVクラスを導入します.新しいスクリプトを作成し,記述されているスクリプトを新しいC#スクリプトに貼り付けます.
Sceneの用意
これで完成です.
以下のようにInputFieldとButtonを持つ新しいシーンを作成します.
空のGameObjectを作成し,AudioSourceと先ほど作成したTextToSpeechSampleをアタッチします.先のGoogle Cloud Platformの利用登録で取得したJsonの認証情報をResourcesのフォルダ内に置きます.
TextToSpeechSampleにInputFieldとButton,AudioSourceを登録し,"Credential"にResource以下のJsonファイルのディレクトリを指定します.(例:Assets/Resources/Credentials/credential.jsonに格納した場合,Credentials/credentialと記述)これで完成です.
実行する
実行し,InputFieldに発言させたい文を記述し,"Send"ボタンを押すことでリクエストが送られ,少し待つと合成された音声が再生されます.
細かくは調べていませんが,リクエスト最初の一回は少し時間がかかり,二回目以降は「こんにちは」といった短い文であれば0.3秒ほどで再生されます.今回のスクリプトでは応答にかかった時間,文を読み上げるのにかかる再生のログ表示も併せて行われます.
終わりに
Google Cloud Text-To-Speechを利用することで,簡単に
(記事を書いてて思いましたが,結構大変でした)Unityで音声合成を行うことができます.【Unity】自分の声をテキスト化する方法【Google Cloud Speech Recognition 】など,Google Cloud Speech Recognitionを利用した音声認識に関する記事は結構存在するので,少し調べればUnity上で音声認識もできます.
同じGoogle Cloudを利用することで今回冒頭のGoogle Cloud Platform利用者登録の大変な部分を次回以降はなくせるので比較的楽に実装できるかと思います.これを利用したチャットボットなど作成するのも面白そうです.
- 投稿日:2020-04-30T18:50:44+09:00
nullを取り除くLINQ Where [null許容参照型]
課題
nullable reference type (null許容参照型) の配列からnullを取り除きたい
後述するように警告が出てしまうことが課題です。nullable生活を始めたら瞬時にこの問題に当たったのですが、巷になかなか情報が無いですね。
コード例
nullを除外しているにもかかわらずWhereの結果は
IEnumerable<string?>なので、Selectのところで nullかも (Dereference of a possibly null reference) と警告が出ます。#nullable enable string?[] array = { "hoge", null, "fuga", null, "piyo" }; var uppers = array .Where(s => s != null) .Select(s => s.ToUpper()) // 警告 .OrderByDescending(s => s);結論
今のところ、ここで示されている
WhereNotNullという拡張メソッドを用意するのが一番確実に思いました。
https://github.com/dotnet/roslyn/issues/39586#issuecomment-547909968static class MyExtensions { public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> source) where T : class { if (source == null) { return Enumerable.Empty<T>(); } return source.Where(x => x != null)!; } }var uppers = array .WhereNotNull() .Select(s => s.ToUpper()) .OrderByDescending(s => s);ほかの案
1.OfType
コメントで指摘頂きました。
var uppers = array .OfType<string>() .Select(s => s.ToUpper()) .OrderByDescending(s => s);ReSharperを利用している場合、
Redundant 'IEnumerable.OfType<T>' call. Consider comparing with 'null' instead.との指摘が出ます。stringとstring?は属性の有無だけの差ですから型変換とはみなされず、そうなるのだと考えます。毎度の指摘抑止も大変ですから、拡張メソッドにするのが良さそうですね。nullをフィルタする意図が即座には伝わりにくい点も緩和できます。static class MyExtensions { public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> source) where T : class // ReSharper disable once RedundantEnumerableCastCall => source.OfType<T>(); }2.びっくりSelect
個人的にこう呼んでいます。
var uppers = array .Where(s => s != null) .Select(s => s!) .Select(s => s.ToUpper()) .OrderByDescending(s => s);この例はあまりよくないので、この程度であればびっくりをいきなり付けていいですね。
var uppers = array .Where(s => s != null) .Select(s => s!.ToUpper()) .OrderByDescending(s => s);せっかくnullableしているのにびっくり演算子付けたら負けな気がするし、かといって拡張メソッドはあまり作りたくないし、悩ましいところはあります。
3. SelectMany
あまりやりたくない案
var uppers = array.SelectMany(s => s == null ? new string[0] : new[] { s }) .Select(s => s.ToUpper()) .OrderByDescending(s => s);この方法は、ほかには2回Parseしたくないときに使えたりします。
var array = new[] {"123", "abc", "一二三"}; // 素朴な方法 var numbers1 = array .Where(s => int.TryParse(s, out _)) .Select(s => int.Parse(s)); var numbers2 = array .SelectMany(s => int.TryParse(s, out var i) ? new[] { i } : new int[0]);
- 投稿日:2020-04-30T16:29:02+09:00
動画資料のための音声データ(ナレーション)を Google の Text-to-Speech に作成させる(C#とPythonのサンプル付き)
概要
Google が提供する音声合成サービス「Cloud Text-to-Speech」の導入メモです。APIサービスの有効化から、認証ファイルの取得、自作プログラム(C#またはPython)から呼び出して利用するまでの流れをできるだけ詳しく説明しました。
基本的には公式の「クイックスタート:クライアント ライブラリの使用」の内容をスクリーンショット付きで解説したものです(なお、不要と思われるステップは飛ばしています)。
Cloud Text-to-Speech は、テキストデータ(日本語OK)から、読みあげ音声データ (.mp3) を生成してくるクラウドサービスです。かなり人間に近い自然な音声出力が可能です。品質については こちら から、任意のテキスト(日本語もOK)を与えて確認することができます。
Google Cloud Platform に登録
Google Cloud ( https://cloud.google.com/?hl=ja ) からユーザー登録します。
お手持ちの Googleカウント を使って無料トライアル利用が可能です。ただし、登録時にクレジットカードが必要です。とはいえ、期間終了後に、有料アカウントに自動移行されることもないので、また、有料カウントに移行しても料金は(個人的には)非常にリーズナブルだと思いますので、あまり構えずに登録しましょう。
利用料金の目安
以下、Google Cloud に登録済みとして説明を進めていきます。
Text-to-Speech のサービス有効化と認証ファイルの取得
Google Cloud Platform にアクセして、ログインします。
ダイアログが表示されるので「新しいプロジェクト」を選択します。
適当なプロジェクト名(ここでは
Text To Speech 20xxxとします)を入力して「作成」をクリックします。ダッシュボードに戻ってくるので、いま作成したプロジェクトに切り替えます。
左上のメニューをクリックして「APIとサービス」、「ダッシュボード」に進みます。
テキストボックスに「Text to Speech」と入力します。
「Cloud Text-to-Speech API」を選択します。
自作のプログラムからサービスを利用するために必要となる「認証情報を作成」を選択します。
「プロジェクトへの認証情報の追加」という画面に移動するので、下記のドロップダウンリストから「Cloud Text-to-Speech API」を選択してから「必要な認証情報」をクリックします。
※ この画面は「APIとサービス」、「認証情報」、「認証情報を作成」、「+認証情報を作成」、「ウィザードで選択」と選択していっても呼び出すことができます。
表示が切り替わるので「いいえ、使用していません」を選択して、再度「必要な認証情報」をクリックします。
適当なサービスアカウント名を入力します(ここでは
testとしました)。ロールは特に選択しません。また「サービスアカウントID」は自動で生成されます。「次へ」を選択します。次のようなダイアログが表示されますが「ロールなしで作成」を選択します。
次のようなダイアログが表示されて、認証情報が入ったJSONファイルが、PCにダウンロードされます。
このファイルの名前を「
credentials.json」に変更して「C:\Users\xxx\Desktop」に配置するものとします。なお、公式のクイックスタート では、このファイルのパスを 環境変数
GOOGLE_APPLICATION_CREDENTIALSとして登録し、プログラムでは環境変数経由で情報を参照するという方法を説明しています。一方、ここでは環境変数には登録せず、プログラムから直接パスを指定して情報を参照する方法をとります。C#(.NET Core)プログラムから呼び出す
VisualStudio を起動し[ファイル]-[新規作成]-[プロジェクト]で、「Visual C#」の「コンソールアプリ(.NET Core)」を選択します。
メニューの[ツール]-[NuGetパッケージマネージャ]-[パッケージ マネージャー コンソール]を選択します。コンソールに
Install-Package Google.Cloud.TextToSpeech.V1 -Preを入力して実行します。PM> Install-Package Google.Cloud.TextToSpeech.V1 -Pre
Program.csの内容を以下に書き換えます。Program.csusing System; using System.IO; using Google.Cloud.TextToSpeech.V1; using System.Diagnostics; public class QuickStart { public static void Main(string[] args) { var credentialsFilePath = @"C:\Users\xxx\Desktop\credentials.json"; var textToSpeechClientBuilder = new TextToSpeechClientBuilder() { CredentialsPath = credentialsFilePath }; var client = textToSpeechClientBuilder.Build(); // 読み上げテキストの設定 SynthesisInput input = new SynthesisInput { Text = "目的地は、日本橋です。" }; // 音声タイプの設定 VoiceSelectionParams voice = new VoiceSelectionParams { Name = "ja-JP-Wavenet-D", LanguageCode = "ja-JP", SsmlGender = SsmlVoiceGender.Neutral }; // オーディオ出力の設定 AudioConfig config = new AudioConfig { AudioEncoding = AudioEncoding.Mp3, Pitch = -2.0 }; // Text-to-Speech リクエストの生成 var response = client.SynthesizeSpeech(new SynthesizeSpeechRequest { Input = input, Voice = voice, AudioConfig = config }); // Text-to-Speech レスポンス(音声ファイル)の保存 var fileName = DateTime.Now.ToString("yyyy-MM-dd_HHmmss") + ".mp3"; using (Stream output = File.Create(fileName)) { response.AudioContent.WriteTo(output); Console.WriteLine($"音声コンテンツを '{fileName}' として保存しました。"); } Console.WriteLine("ファイルを出力したフォルダを開きますか [Y]/n"); var k = Console.ReadKey(); if (k.Key != ConsoleKey.N && k.Key != ConsoleKey.Escape) { Process.Start("explorer.exe", Directory.GetCurrentDirectory()); } } }実行すると「もくてきちは、にほんばしです。」という MP3 ファイルが生成されます。
なお、音声合成マークアップ言語(SSML)にも対応しており、以下のように変更すると
「もくてきちは、にほんばしではなく、にっぽんばしです。」と読み上げてくれます。また、<break time="200ms"/>などにより 間 を挿入することもできます。SSML形式SynthesisInput input = new SynthesisInput { Ssml = "<speak>目的地は、日本橋、ではなく、<sub alias='にっぽんばし'>日本橋</sub>です。</speak>".Replace("'", "\"") };Python プログラムから呼び出す
pip install --upgrade google-cloud-texttospeechfrom datetime import datetime from pytz import timezone from google.cloud import texttospeech from google.oauth2 import service_account credentials = service_account.Credentials.from_service_account_file('credentials.json') client = texttospeech.TextToSpeechClient(credentials=credentials) synthesis_input = texttospeech.types.SynthesisInput( text='目的地は、秋葉原です。') voice = texttospeech.types.VoiceSelectionParams( language_code='ja-JP', name='ja-JP-Wavenet-D', ssml_gender=texttospeech.enums.SsmlVoiceGender.NEUTRAL) audio_config = texttospeech.types.AudioConfig( audio_encoding=texttospeech.enums.AudioEncoding.MP3, pitch = -2.0 ) response = client.synthesize_speech(synthesis_input, voice, audio_config) now = datetime.now(timezone('Asia/Tokyo')) filename = now.strftime('%Y-%m-%d_%H%M%S.mp3') with open(filename, 'wb') as out: out.write(response.audio_content) print(f'Audio content written to file {filename}')
- 投稿日:2020-04-30T16:29:02+09:00
動画資料ための音声データ(ナレーション)を Google の Text-to-Speech に作成させる(C#とPythonのサンプル付き)
概要
Google が提供する音声合成サービス「Cloud Text-to-Speech」の導入メモです。APIサービスの有効化から、認証ファイルの取得、自作プログラム(C#またはPython)から呼び出して利用するまでの流れをできるだけ詳しく説明しました。
基本的には公式の「クイックスタート:クライアント ライブラリの使用」の内容をスクリーンショット付きで解説したものです(なお、不要と思われるステップは飛ばしています)。
Cloud Text-to-Speech は、テキストデータ(日本語OK)から、読みあげ音声データ (.mp3) を生成してくるクラウドサービスです。かなり人間に近い自然な音声出力が可能です。品質については こちら から、任意のテキスト(日本語もOK)を与えて確認することができます。
Google Cloud Platform に登録
Google Cloud ( https://cloud.google.com/?hl=ja ) からユーザー登録します。
お手持ちの Googleカウント を使って無料トライアル利用が可能です。ただし、登録時にクレジットカードが必要です。とはいえ、期間終了後に、有料アカウントに自動移行されることもないので、また、有料カウントに移行しても料金は(個人的には)非常にリーズナブルだと思いますので、あまり構えずに登録しましょう。
利用料金の目安
以下、Google Cloud に登録済みとして説明を進めていきます。
Text-to-Speech のサービス有効化と認証ファイルの取得
Google Cloud Platform にアクセして、ログインします。
ダイアログが表示されるので「新しいプロジェクト」を選択します。
適当なプロジェクト名(ここでは
Text To Speech 20xxxとします)を入力して「作成」をクリックします。ダッシュボードに戻ってくるので、いま作成したプロジェクトに切り替えます。
左上のメニューをクリックして「APIとサービス」、「ダッシュボード」に進みます。
テキストボックスに「Text to Speech」と入力します。
「Cloud Text-to-Speech API」を選択します。
自作のプログラムからサービスを利用するために必要となる「認証情報を作成」を選択します。
「プロジェクトへの認証情報の追加」という画面に移動するので、下記のドロップダウンリストから「Cloud Text-to-Speech API」を選択してから「必要な認証情報」をクリックします。
※ この画面は「APIとサービス」、「認証情報」、「認証情報を作成」、「+認証情報を作成」、「ウィザードで選択」と選択していっても呼び出すことができます。
表示が切り替わるので「いいえ、使用していません」を選択して、再度「必要な認証情報」をクリックします。
適当なサービスアカウント名を入力します(ここでは
testとしました)。ロールは特に選択しません。また「サービスアカウントID」は自動で生成されます。「次へ」を選択します。次のようなダイアログが表示されますが「ロールなしで作成」を選択します。
次のようなダイアログが表示されて、認証情報が入ったJSONファイルが、PCにダウンロードされます。
このファイルの名前を「
credentials.json」に変更して「C:\Users\xxx\Desktop」に配置するものとします。なお、公式のクイックスタート では、このファイルのパスを 環境変数
GOOGLE_APPLICATION_CREDENTIALSとして登録し、プログラムでは環境変数経由で情報を参照するという方法を説明しています。一方、ここでは環境変数には登録せず、プログラムから直接パスを指定して情報を参照する方法をとります。C#(.NET Core)プログラムから呼び出す
VisualStudio を起動し[ファイル]-[新規作成]-[プロジェクト]で、「Visual C#」の「コンソールアプリ(.NET Core)」を選択します。
メニューの[ツール]-[NuGetパッケージマネージャ]-[パッケージ マネージャー コンソール]を選択します。コンソールに
Install-Package Google.Cloud.TextToSpeech.V1 -Preを入力して実行します。PM> Install-Package Google.Cloud.TextToSpeech.V1 -Pre
Program.csの内容を以下に書き換えます。Program.csusing System; using System.IO; using Google.Cloud.TextToSpeech.V1; using System.Diagnostics; public class QuickStart { public static void Main(string[] args) { var credentialsFilePath = @"C:\Users\xxx\Desktop\credentials.json"; var textToSpeechClientBuilder = new TextToSpeechClientBuilder() { CredentialsPath = credentialsFilePath }; var client = textToSpeechClientBuilder.Build(); // 読み上げテキストの設定 SynthesisInput input = new SynthesisInput { Text = "目的地は、日本橋です。" }; // 音声タイプの設定 VoiceSelectionParams voice = new VoiceSelectionParams { Name = "ja-JP-Wavenet-D", LanguageCode = "ja-JP", SsmlGender = SsmlVoiceGender.Neutral }; // オーディオ出力の設定 AudioConfig config = new AudioConfig { AudioEncoding = AudioEncoding.Mp3, Pitch = -2.0 }; // Text-to-Speech リクエストの生成 var response = client.SynthesizeSpeech(new SynthesizeSpeechRequest { Input = input, Voice = voice, AudioConfig = config }); // Text-to-Speech レスポンス(音声ファイル)の保存 var fileName = DateTime.Now.ToString("yyyy-MM-dd_HHmmss") + ".mp3"; using (Stream output = File.Create(fileName)) { response.AudioContent.WriteTo(output); Console.WriteLine($"音声コンテンツを '{fileName}' として保存しました。"); } Console.WriteLine("ファイルを出力したフォルダを開きますか [Y]/n"); var k = Console.ReadKey(); if (k.Key != ConsoleKey.N && k.Key != ConsoleKey.Escape) { Process.Start("explorer.exe", Directory.GetCurrentDirectory()); } } }実行すると「もくてきちは、にほんばしです。」という MP3 ファイルが生成されます。
なお、音声合成マークアップ言語(SSML)にも対応しており、以下のように変更すると
「もくてきちは、にほんばしではなく、にっぽんばしです。」と読み上げてくれます。また、<break time="200ms"/>などにより 間 を挿入することもできます。SSML形式SynthesisInput input = new SynthesisInput { Ssml = "<speak>目的地は、日本橋、ではなく、<sub alias='にっぽんばし'>日本橋</sub>です。</speak>".Replace("'", "\"") };Python プログラムから呼び出す
pip install --upgrade google-cloud-texttospeechfrom datetime import datetime from pytz import timezone from google.cloud import texttospeech from google.oauth2 import service_account credentials = service_account.Credentials.from_service_account_file('credentials.json') client = texttospeech.TextToSpeechClient(credentials=credentials) synthesis_input = texttospeech.types.SynthesisInput( text='目的地は、秋葉原です。') voice = texttospeech.types.VoiceSelectionParams( language_code='ja-JP', name='ja-JP-Wavenet-D', ssml_gender=texttospeech.enums.SsmlVoiceGender.NEUTRAL) audio_config = texttospeech.types.AudioConfig( audio_encoding=texttospeech.enums.AudioEncoding.MP3, pitch = -2.0 ) response = client.synthesize_speech(synthesis_input, voice, audio_config) now = datetime.now(timezone('Asia/Tokyo')) filename = now.strftime('%Y-%m-%d_%H%M%S.mp3') with open(filename, 'wb') as out: out.write(response.audio_content) print(f'Audio content written to file {filename}')
- 投稿日:2020-04-30T15:27:33+09:00
新作講座 Unity3Dゲーム Standard講座を発売したって話
この度、 Unity3Dゲーム Standard講座を発売しましたー。
こちらは3年ぶりの新作講座なので かなり久しぶりですね。
名前のとおり 3Dゲームに特化した内容で
Unityの基礎をマスターできる内容となっています。動画で約6時間のボリュームです。
更には 作ったゲームを アプリとしてリリースするまでの過程も全部
解説しています。
【Androidでの解説だけです。iosはありませんので】今年に入って Unityを教えることなども多くなってきたので
更に 第3弾の講座も予定しています。
本当は、 勉強会などもやる予定だったんですけど。
コロナの影響で 会場とかカラオケ店なども 使えなくなりましたからね。なので、勉強会はしばらくあおずけです。
今は皆さん 引きこもって ゲームとかしている人も多いと思いますけど
これを機に、 ゲーム作りなどをしてみてはどうでしょうか?
PS
講座はNoteにて 販売しています。
https://note.com/zazizuzezo22334/n/n90340339a00c?magazine_key=m3f4ee59f0352只今 緊急事態セールということで 70%OFFで 購入できます。
あと、 C#プログラミングマスター講座も 好評発売中です。
こちらも70%OFFにしています。この2つをセットで 購入するお客さんが多いですね。
やはりC#が分からないと Unityも理解できないので
C#の勉強もちゃんとしたほうがいいかと思います。NOTEでも ちょいちょいUnityネタを投稿しています。
よかったら、フォローしてください。
- 投稿日:2020-04-30T14:46:42+09:00
【Unity:C#】Attributeの定義をまとめる
VSで自動フォーマット適用した時にAttributeの定義が改行されて行が増えるのが地味に嫌だった
sample.before.cs[SerializeField] [Range(0, 1)] private int sample;カンマ区切りでいけたんだね
sample.after.cs[SerializeField, Range(0, 1)] private int sample;
- 投稿日:2020-04-30T11:06:28+09:00
[スキーマチェックに関する質問]xsdによってxmlを検証し、複数エラーメッセージを返却値として受け取りたい
はじめに
初めまして、エンジニア歴1年目で最近C#を触り始めたものです。
早速ですが、1週間ほど悩んで解決できないことがあるので質問させていただきます。使用環境及び言語
Microsoft Visual Studio 2019でC#を使用しております。
実現したいこと
実現したいこととしましては下記になります。
1.xsdによるxmlの検証(これはおそらくできている)
2.エラーが出た場合、エラーメッセージをArrayList型に設定し、返却値として渡す(すべてのエラーを設定したい)現在の状況
xsdによってxmlを検証し、コンソールにエラーメッセージを出すところまでは調べれば出てきたのですが、
・複数のエラーが起きた時に最初のエラーのみが表示される点
・実際にはコンソールに表示するのではなく、エラーメッセージをArrayList型に設定し返却値とする点
以上の二点が現在実装できていない状況です。以下、現在のソースコードです。(xmlファイル、xsdファイルについては適当なものをローカルに保存し、ファイルパスを渡しています)
private ArrayList xmlCheck(string xmlFilePath, string xsdFilePath) { //xsdファイルパスから型変換 XmlTextReader reader = new XmlTextReader(xsdFilePath); XmlSchema xmlschema = XmlSchema.Read(reader, ValidationCallback); XmlSchemaSet schemaSet = new XmlSchemaSet(); schemaSet.Add(xmlschema); // XMLファイルとXSDファイルを設定する。 XmlDocument xmlDocument = new XmlDocument(); xmlDocument.Schemas.Add(schemaSet); xmlDocument.Load(xmlFilePath); try{ //スキーマチェック xmlDocument.Validate(ValidationEventHandler); //仮の返却値 return null; //xsd等の規約に対する例外?(そもそも必要?) }catch(XmlSchemaValidationException ex) { //エラーあり(true)を返却 return ex; } } private void ValidationCallback(object sender, ValidationEventArgs e) { switch (e.Severity) { case XmlSeverityType.Error: Console.WriteLine("Error:" + e.Message); break; case XmlSeverityType.Warning: Console.WriteLine("Warning:" + e.Message); break; } } private void ValidationEventHandler(object sender, ValidationEventArgs e) { switch(e.Severity) { case XmlSeverityType.Error: //errorInfoList.Add(e.Message); Console.WriteLine("Error:" + e.Message); break; case XmlSeverityType.Warning: Console.WriteLine("Warning:" + e.Message); break; } }終わりに
以上が私の質問になります。解答だけでなく分かりにくい点や、もっと情報が欲しい場合等も対応したいと思うので、そういった指摘でもございましたら是非よろしくお願いします。
- 投稿日:2020-04-30T03:59:31+09:00
DotNetCoreCLI@2でprojectsに指定した値と違うプロジェクトがpublishされる
Azure DevOpsのPipelinesでDotNetCoreCLI@2で指定したつもりのソリューションがpublishされず、かなり時間を浪費したため、状況をまとめます。
全く違うプロジェクトがビルドされていた
こんな風にGitリポジトリには複数プロジェクトが存在し、共通のクラスライブラリを見ています。
AのslnファイルにはCoreとAだけ、BのslnファイルにはCoreとBだけが含まれている状態です。フォルダ構成├ HogeHoge.Core (.Net Coreクラスライブラリ) │ └ HogeHoge.Core.csproj ├ HogeHoge.A.Functions (Azure FunctionsプロジェクトA) │ └ HogeHoge.A.Functions.csproj ├ HogeHoge.B.Functions (Azure FunctionsプロジェクトB) │ └ HogeHoge.B.Functions.csproj ├ HogeHoge.A.Functions.sln ├ HogeHoge.B.Functions.slnPipelineのYMLでは最初はこんな風に指定していました。(変数の指定については割愛)
最初に作ったYML- task: DotNetCoreCLI@2 inputs: command: 'publish' projects: '**/HogeHoge.B.Functions.sln' arguments: '--configuration $(buildConfiguration) -r $(buildPlatform) --output $(Build.ArtifactStagingDirectory)' zipAfterPublish: truePublishは成功しているのですが、何故かAのFunctionsがPublishされています。
Pipelineのログを見ると、何故かAプロジェクトが直接参照されています。
何故projectsの指定が無視されるのか・・・============================================================================== Task : .NET Core Description : Build, test, package, or publish a dotnet application, or run a custom dotnet command Version : 2.167.1 Author : Microsoft Corporation Help : https://docs.microsoft.com/azure/devops/pipelines/tasks/build/dotnet-core-cli ============================================================================== C:\windows\system32\chcp.com 65001 Active code page: 65001 C:\hostedtoolcache\windows\dotnet\dotnet.exe publish d:\a\1\s\HogeHoge.A.Functions\HogeHoge.A.Functions.csproj --configuration Release -r win-x64 --output d:\a\1\a\HogeHoge.A.Functionsその1:Projectsの記法が異なる
まず、projectsの指定方法が間違っていました。こんな風に書かないと認識されません。
projects: | **/HogeHoge.B.Functions.slnその2: publishWebProjectsが必要
publishWebProjectsをfalseであっても指定しないとprojectsの指定は無視されるようです。
projectsの指定がない場合、勝手に適当なプロジェクトを探しに行ってしまうらしい。だったらエラーを吐いてほしい・・・publishWebProjects: false # このパラメータが無いとprojectsの指定が無視される結果
こんなYMLに変更することで、ちゃんとpublishされるようになりました。
- task: DotNetCoreCLI@2 inputs: command: 'publish' publishWebProjects: false projects: | **/HogeHoge.B.Functions.sln arguments: '--configuration $(buildConfiguration) -r $(buildPlatform) --output $(Build.ArtifactStagingDirectory)' zipAfterPublish: true参考
DotNetCoreCLI@2 ignores projects on publish
https://developercommunity.visualstudio.com/content/problem/826169/dotnetcorecli-3.html
Excluding projects when executing the DotNetCoreCLI@2 task
https://stackoverflow.com/questions/58975756/excluding-projects-when-executing-the-dotnetcorecli2-task
- 投稿日:2020-04-30T00:46:44+09:00
Roslynが作るコードツリーを覗く
前書き
.net coreでコンパイル時の自動コード生成をする上で、Roslyn APIに触る必要がある。
その手始めとして、Roslynが吐くASTを除いて見たメモ。環境
今回の実験環境
% dotnet --info .NET Core SDK (global.json を反映): Version: 3.1.201 Commit: b1768b4ae7 ランタイム環境: OS Name: Mac OS X OS Version: 10.15 OS Platform: Darwin RID: osx.10.15-x64 Base Path: /usr/local/share/dotnet/sdk/3.1.201/ Host (useful for support): Version: 3.1.3 Commit: 4a9f85e9f8 .NET Core SDKs installed: 3.1.201 [/usr/local/share/dotnet/sdk] .NET Core runtimes installed: Microsoft.AspNetCore.App 3.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.NETCore.App 3.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]プロジェクト準備
コンソールアプリとして作成
% dotnet new console -o CodeGenDemo
Roslyn APIのパッケージを加える% dotnet add CodeGenDemo package Microsoft.CodeAnalysis.CSharp% dotnet list CodeGenDemo package プロジェクト 'CodeGenDemo' に次のパッケージ参照が含まれています [netcoreapp3.1]: 最上位レベル パッケージ 要求済み 解決済み > Microsoft.CodeAnalysis.CSharp 3.5.0 3.5.0作成
例として、以下のインターフェースのASTを覗く
using System; namespace DemoGenDao { public interface IColorDao { ColorData? FindById(int id); } }
ASTの作成は、Microsoft.CodeAnalysis.CSharp名前空間のCSharpSyntaxTreeクラスのParseTextメソッドを使用する。see: CSharpSyntaxTree.ParseText Method
ASTのルートツリーは、CSharpSyntaxTreeのGetCompilationUnitRootメソッドで取得可能。see: CSharpSyntaxTree.GetCompilationUnitRoot(CancellationToken) Method
using System; using Microsoft.CodeAnalysis.CSharp; // (snip) static void Main(string[] args) { var programText = @" using System; namespace DemoGenDao { public interface IColorDao { ColorData? FindById(int id); } } "; var tree = CSharpSyntaxTree.ParseText(programText); var root = tree.GetCompilationUnitRoot(); }
usingで取り込んだ名前空間の情報は、Usingsプロパティから辿る。
その他クラスユニット等は、Membersプロパティから辿る。static void Main(string[] args) { // 続き System.Console.WriteLine($"The tree has {root.Usings.Count} using statements. They are:"); foreach (var e in root.Usings) { System.Console.WriteLine($"\t{e.Name}"); } System.Console.WriteLine($"The tree is {root.Kind()} node."); System.Console.WriteLine($"The tree has {root.Members.Count} elements in it."); }
ASTのノードは、Microsoft.CodeAnalysis.CSharp.Syntax名前空間で定義されている。
適宜キャストして扱う。see: Microsoft.CodeAnalysis.CSharp.Syntax
今回お世話になったのは、
- NamespaceDeclarationSyntax
- InterfaceDeclarationSyntax
- MethodDeclarationSyntax
- ParameterSyntax
- TypeSyntax
using Microsoft.CodeAnalysis.CSharp.Syntax; // 追加 // (snip) static void Main(string[] args) { // 続き foreach (var m in root.Members) { System.Console.WriteLine($"\tThe member in root is {m.Kind()}."); var ns = (NamespaceDeclarationSyntax)m; // Microsoft.CodeAnalysis.CSharp.Syntax System.Console.WriteLine($"\tThere are {ns.Members.Count} members declared in this namespace."); foreach (var t in ns.Members) { System.Console.WriteLine($"\t\tThe member in this namespace is {t.Kind()}."); if (t is InterfaceDeclarationSyntax) { var intf = (InterfaceDeclarationSyntax)t; System.Console.WriteLine($"\t\tThis type is {intf.Identifier} interface."); System.Console.WriteLine($"\t\tThis type has {intf.Members.Count} members."); foreach (var mm in intf.Members) { System.Console.WriteLine($"\t\t\tThe member in this type is {mm.Kind()}."); if (mm is MethodDeclarationSyntax) { var method = (MethodDeclarationSyntax)mm; System.Console.WriteLine($"\t\t\tThis method name is {method.Identifier}."); System.Console.WriteLine($"\t\t\tThis method has {method.Modifiers.Count} modifiers."); System.Console.WriteLine($"\t\t\tThis method returns {method.ReturnType}."); System.Console.WriteLine($"\t\t\t\tThe return type nullable in method is {method.ReturnType.IsNotNull}."); System.Console.WriteLine($"\t\t\tThis method receives {method.ParameterList.Parameters.Count} args."); foreach (ParameterSyntax arg in method.ParameterList.Parameters) { System.Console.WriteLine($"\t\t\t\tThe arg in this method is {arg.Kind()}."); System.Console.WriteLine($"\t\t\t\tThe arg name is {arg.Identifier}."); System.Console.WriteLine($"\t\t\t\tThe arg type is {arg.Type}."); System.Console.WriteLine($"\t\t\t\t\tThe arg nullable is {arg.Type.IsNotNull}."); } } else { System.Console.WriteLine($"\t\t???? This member syntax is {t.GetType().FullName}."); } } } else { System.Console.WriteLine($"\t\t???? This type syntax is {t.GetType().FullName}."); } } } }実行結果
実行結果は以下の通り
% dotnet run -p CodeGenDemo The tree is CompilationUnit node. The tree has 1 using statements. They are: System The tree has 1 elements in it. The member in root is NamespaceDeclaration. There are 1 members declared in this namespace. The member in this namespace is InterfaceDeclaration. This type is IColorDao interface. This type has 1 members. The member in this type is MethodDeclaration. This method name is FindById. This method has 0 modifiers. This method returns ColorData?. The return type nullable in method is False. This method receives 1 args. The arg in this method is Parameter. The arg name is id. The arg type is int. The arg nullable is False.





































