まずは[コンソール アプリ (.NET Core)]のプロジェクトを「SampleVoiceApp」という名前(もちろん好きな名前でもOK)で作成する(手順は割愛)。
次に、音声合成するテキストを作成する。
このテキストはREST APIに対してHTTP POSTのリクエストボディとして送信するが、エーアイ提供サービスの場合は、そのデータフォーマットがSSML(Speech Synthesis Markup Language)というXML形式の一種になるので注意してほしい。SSMLでどのような内容が記述できるのかについては、下記のリンク先を参照されたい。ちなみに前掲のリスト1が、実際にSSMLで記載されていた。
これをC#で書いたのが、リスト2である。基本的なコンソールアプリのひな型コードと、単なる文字列変数の宣言でしかないので、コーディング上の説明は不要だろう。
using System;
using System.Text;
class Program
{
// サービス: 音声合成
// エンドポイント: SSML 【Powerd by エーアイ】
// HTTPリクエストボディ:
static string textBySSML = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
<speak version=""1.1"">
<voice name=""nozomi"">
<prosody pitch=""1.6"" volume=""1.1"">
のぞみです。おやすみなさい。
</prosody>
</voice>
<break time=""500ms"" />
<voice name=""seiji"">
せいじです。おやすみなさい。
</voice>
<break time=""500ms"" />
<voice name=""akari"">
あきらです。おやすみなさい。
</voice>
<break time=""500ms"" />
<voice name=""anzu"">
anzuです。おやすみなさい。
</voice>
<break time=""500ms"" />
<voice name=""hiroshi"">
hiroshiです。おやすみなさい。
</voice>
<break time=""500ms"" />
<voice name=""kaho"">
kahoです。おやすみなさい。
</voice>
<break time=""500ms"" />
<voice name=""maki"">
makiです。おやすみなさい。
</voice>
<break time=""500ms"" />
<voice name=""nanako"">
nanakoです。おやすみなさい。
</voice>
<break time=""500ms"" />
<voice name=""osamu"">
osamuです。おやすみなさい。
</voice>
<break time=""500ms"" />
<voice name=""sumire"">
sumireです。おやすみなさい。
</voice>
</speak>";
static void Main(string[] args)
{
// .NET Coreのコンソールアプリでは日本語の文字列出力が文字化けしてしまう問題がある。
// この問題を回避するために、Encoding〜で始まる以下の1行を追加している。
// 事前に、NuGetで「System.Text.Encoding.CodePages」をインストールしておく必要がある
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
// REST APIリクエストボディ送信用にバイト配列データを作成
byte[] bytesBySSML = Encoding.UTF8.GetBytes(textBySSML);
// ……実装はこれから……
}
}
SSML文書における<voice>要素のname属性に指定している値(例:「nozomi」)はスピーカー名である。この例では、(執筆時点で)利用可能なスピーカー全員を順番に呼び出して、「<名前>です。おやすみなさい。」としゃべらせている。<prosody pitch=""1.6"" volume=""1.1"">要素は音声のピッチやボリュームを変更する例である。<break time=""500ms"" />要素は500ミリ秒、間を置くための指定だ。
次に、今回の音声合成REST API呼び出しの仕様(URLとHTTPヘッダ)を変数としてまとめて宣言しておこう。これに関する情報は、前述の「API実行サンプルを試してみる」節の[APIコンソール]ページのところで確認済みだ。具体的にはリスト3のようなコードになる。
using System.Net.Http;
……前略……
// HTTPメソッド: POST
static HttpMethod apiMethod = HttpMethod.Post;
// エンドポイントURL:
static string apiEndPointURL = "https://api.apigw.smt.docomo.ne.jp/aiTalk/v1/textToSpeech?APIKEY=";
// [APIKEY]: (※取得方法は前述)
static string apiKey = "xxxxxxxxxxxxxxxxx<ここは書き換える>";
// HTTPリクエストヘッダ:
// [Content-Type]:
static string apiContentType = "application/ssml+xml";
// [Accept]:
static string apiAccept = "audio/L16";
// [Content-Length]: (※ HTTPリクエストボディのバイトサイズで自動計算)
static void Main(string[] args)
{
……中略……
}
……後略……
以上で準備は整ったので、実際にREST APIを呼び出してみよう。
HTTPリクエストを作成してREST APIを呼び出す部分は、リスト4に示すようなコードになる。コード内容はHttpClientクラスの使い方そのもので、本稿が伝えたい趣旨ではないので説明を割愛する。
using System.Net;
using System.Net.Http.Headers;
……前略……
static void Main(string[] args)
{
……中略……
using (HttpClient client = new HttpClient())
{
// HTTPメソッド/エンドポイントURL/[APIKEY]
var request = new HttpRequestMessage(apiMethod, apiEndPointURL + apiKey);
// HTTPリクエストボディ
request.Content = new ByteArrayContent(bytesBySSML);
// HTTPリクエストヘッダ: [Content-Type]/[Accept]
request.Content.Headers.ContentType = new MediaTypeHeaderValue(apiContentType);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(apiAccept));
// [Content-Length]: (※ HTTPリクエストボディのバイトサイズは自動計算)
try
{
// REST APIの呼び出し
var asyncSendTask = client.SendAsync(request);
asyncSendTask.Wait();
//……HTTPレスポンスの取得処理はリスト5で説明……
}
catch (Exception ex)
{
//……エラー処理はリスト5で説明……
}
finally
{
//……最終結果の出力処理はリスト5で説明……
}
Console.ReadKey();
}
}
……後略……
最後に、HTTPレスポンスを取得して、音声ファイルを適当な場所に保存する。これもコードに記載されている通りの処理で難しくはないので、説明を割愛する。
using System.IO;
……前略……
static void Main(string[] args)
{
……中略……
// 音声ファイルの保存場所を決定(※事前にフォルダを作成する)
string lpcmFilePath = $@"C:\VoiceData\AITalkR{DateTime.Now.ToString("yyyyMMddHHmmss")}.lpcm";
// HTTPレスポンスヘッダの結果情報
var sbResponseHeaders = new StringBuilder();
// 最後にまとめて情報出力するためのフラグ
var isSuccessResponse = false;
using (HttpClient client = new HttpClient())
{
//……HTTPリクエストの作成はリスト4……
try
{
//……REST APIの呼び出しはリスト4……
// HTTPレスポンスヘッダ情報を全て取得する
sbResponseHeaders.AppendLine($"HTTP-Status-Code: {asyncSendTask.Result.StatusCode}");
foreach (var headerItem in asyncSendTask.Result.Headers)
{
var headkey = headerItem.Key;
var headValue = String.Join(",", headerItem.Value);
sbResponseHeaders.AppendLine($"{headkey}: {headValue}");
}
if (asyncSendTask.Result.StatusCode == HttpStatusCode.OK)
{
isSuccessResponse = true;
// HTTPレスポンスボディの内容を.lpcmファイルとして書き出す
var msgResponseBody = asyncSendTask.Result.Content;
var stmResponseBody = msgResponseBody.ReadAsStreamAsync().GetAwaiter().GetResult();
using (BinaryReader reader = new BinaryReader(stmResponseBody))
{
Byte[] lnByte = reader.ReadBytes(1 * 1024 * 1024 * 10);
using (FileStream lxFS = new FileStream(lpcmFilePath, FileMode.Create))
{
lxFS.Write(lnByte, 0, lnByte.Length);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("エラー発生!\n" + ex.Message);
}
finally
{
if (isSuccessResponse)
{
Console.WriteLine($"【レスポンスヘッダ】\n{sbResponseHeaders.ToString()}\n" +
$"【レスポンスボディ】\n{lpcmFilePath}\n\n" +
$"【音声ファイルの再生・変換について】\n{howToPlayLpcm}\n");
// ※howToPlayLpcmメンバ変数は重要性が低いので下で宣言している
}
else
{
Console.WriteLine($"【レスポンスヘッダ】\n{sbResponseHeaders.ToString()}");
}
}
Console.ReadKey();
}
}
// 操作方法の案内(※もちろんサンプル実行において必須ではない)
const string howToPlayLpcm = @".lpcm音声ファイルを再生/変換したい場合は、Audacity(Windows/macOS/Linux対応)が便利。
メニューバーから[ファイル]−[取り込み]−[ロー (Raw) データの取り込み]をクリックして、先ほど出力された「ダウンロード.lpcm」ファイルを開く。
[Raw データの取り込み]ダイアログでは以下を指定する。
・[エンコーディング]: 「Signed 16-bit PCM」
・[バイト順序]: 「ビックエンディアン (バイトオーダ)」
・[チャンネル]: 「1 チャンネル (モノラル)」
・[オフセットの開始]: 「0」バイト
・[インポートするために]: 「100」%
・[サンプリング周波数]: 「16000」Hz
取り込んだ.lpcm音声ファイルをWAV形式やMP3形式に変換して保存するには、メニューバーから[ファイル]−[オーディオの書き出し]を実行する。";
……後略……
以上でサンプルは完成だ。各自でサンプルを実行して、図12のように表示されれば成功だ。「音声ファイルを再生/変換してみる」節で説明した手順に従い(図12の画像内の説明を読んでもよい)、音声ファイルを再生してみてほしい。
docomo音声合成APIの使い方に、C#だからといって特別なところはない。C#でREST APIを使いこなせれば問題なく実装できるはずだ。本稿をきっかけにぜひ試していただけるとうれしい。
なお今回はエーアイ提供サービスのAPIの使い方に絞って説明している。HOYA提供サービスなど他のAPIを使う場合は、URLやテキストデータのフォーマットが異なるために、本稿のコードからある程度書き直す部分が発生するので注意してほしい。
Copyright© Digital Advantage Corp. All Rights Reserved.