連載
» 2017年04月05日 05時00分 公開

テキスト→音声変換の「Text To Speech API」の使い方と、2017年3月現在のWatsonとの違い認識系API活用入門(3)(2/3 ページ)

[岩本禎史,株式会社クロスキャット]

処理の作成

 MainWindow.xaml.csで実際にプログラムから呼び出すところを作成します。プログラムの流れとしては非常にシンプルで、以下のようになります。

  1. アクセスキーを使用してTokenを取得する
  2. Tokenを使用して、Text To Speech APIに発話させたいテキストを渡し、音声ファイルを取得する

 前回で紹介したTranslator APIと違って、サービスが対応している言語の一覧を取得するAPIがないようなので、今回はプログラム内にリストを持つことにします(プログラムを簡便にするために、今回は英語と日本語のみとします)。

 まずアクセスキーを使用してTokenを取得する処理ですが、Tokenの取得については、Translator APIのときとまったく同じです。Tokenは10分間有効なので、前回取得時から10分以上経過していたら再取得するよう、取得した時間を変数に取っておきます。

// ----------------------------------------------
// Tokenを取得する処理
// ----------------------------------------------
string GetAccessToken()
{
    try
    {
        //取得したBing Speech API の Key
        string subscriptionKey = "xxxxxxxxxxxxxxxxxxxxxxxx";
 
        WebRequest request = WebRequest.Create("https://api.cognitive.microsoft.com/sts/v1.0/issueToken");
 
        //リクエスト設定
        request.Method = "POST"; // メソッドはPOST
        request.Headers.Add("Ocp-Apim-Subscription-Key", subscriptionKey);
        request.ContentLength = 0;
 
        // リクエストを投げる
        WebResponse response = request.GetResponse();
        Stream responseStream = response.GetResponseStream();
 
        // レスポンスを受け取る
        StreamReader streamReader = new StreamReader(responseStream);
 
        // Tokenを取得した時間を取得
        tokenRefreshedTime = DateTime.Now;
 
        // レスポンス(Token)を返す
        return streamReader.ReadToEnd();
    }
    catch (WebException webException)
    {
        Stream responseStream = webException.Response.GetResponseStream();
        StreamReader streamReader = new StreamReader(responseStream);
        tblkMessage.Text = streamReader.ReadToEnd();
        return null;
    }
    catch (Exception exception)
    {
        tblkMessage.Text = exception.Message;
        return null;
    }
}

 続いて、テキストを渡して音声ファイルを取得する処理です。APIのリクエストメソッドはPOSTになります。Text To Speech APIでは、リクエストのHTTP Header に以下の値を含めるように記載されています。

Content-Type application/ssml+xml The input content type
X-Microsoft-OutputFormat 1) ssml-16khz-16bit-mono-tts, 2) raw-16khz-16bit-mono-pcm, 3) audio-16khz-16kbps-mono-siren, 4) riff-16khz-16kbps-mono-siren 5) riff-16khz-16bit-mono-pcm 6) audio-16khz-128kbitrate-mono-mp3, 7) audio-16khz-64kbitrate-mono-mp3, 8) audio-16khz-32kbitrate-mono-mp3 The output audio format
X-Search-AppId A GUID (hex only, no dashes) An ID that uniquely identifies the client application. This can be Store ID for Apps. If one is not available, the ID may be user generated per-application.
X-Search-ClientID A GUID (hex only, no dashes) An ID that uniquely identifies application instance per-installation.
User-Agent Application name Application name is required and must be less than 255 characters.

 出力される音声ファイルのフォーマット(X-Microsoft-OutputFormat)については8パターンもあって「どれを使えばよいのか」悩ましいところですが、WAVなら「riff-16khz-16bit-mono-pcm」、MP3なら「audio-16khz-128kbitrate-mono-mp3」でいいかと思います(個人的には、MP3の32kbitrateと64kbitrateは、やや音が悪い印象です。ただし、通信量を減らしたいときには有効だと思います)。

 クライアントアプリケーションを一意に識別するIDとして「X-Search-AppId」を設定する必要があります。また、その下のレベルでインストールごとのアプリケーションインスタンスを一意に識別する「X-Search-ClientID」なるものまで必要です。ここが少々分かりにくいと思いますが、実際には適当な固定値でもリクエストのたびに新たなIDを生成する形でも問題ないようです。

 ただし、「Hex only, no dashes」と書いてありますので、「16進数のみ使用可能でダッシュ(-)は使えない」ことに注意してください。今回のサンプルプログラムではリクエストごとに新たなIDを生成しています。

 リクエストヘッダの他に、パラメーターも必要になります。パラメーターに含める内容は、下記3つです。

  • 発話の言語
  • 男性の声か女性の声か
  • 発話するテキスト

 パラメーターはSpeech Synthesis Markup Language(SSML)で記述するようになっています。サンプルプログラムでは、下記のようなSSMLを作成しています。

<speak version='1.0' xml:lang='en-US'><voice xml:lang='en-US' xml:gender='Female' name='Microsoft Server Speech Text to Speech Voice (en-US, ZiraRUS)'>Microsoft Bing Voice Output API</voice></speak>

 発話の言語および男性の声、女性の声として具体的にどのような値を設定するかは、下記URLの「Supported Locales and Voice Fonts」で確認できます。

 今回のサンプルプログラムでは英語と日本語のみにしていますので、以下のようになります。

発話の言語 女性の声 男性の声
英語 en-US ZiraRUS BenjaminRUS
日本語 ja-JP Ayumi、Apollo Ichiro、Apollo
// ----------------------------------------------
// 音声ファイルを取得する処理
// ----------------------------------------------
string GetTextToSpeechAudio(string pAccessToken, string pLanguage, string pGender, string pText, string pAudioFormat)
{
    try
    {
        string url = "https://speech.platform.bing.com/synthesize";
        string speakerName = "";
        switch (pLanguage)
        {
        case "en-US":
            if (pGender == "Female") speakerName = "ZiraRUS"; else speakerName = "BenjaminRUS";
            break;
        case "ja-JP":
            if (pGender == "Female") speakerName = "Ayumi, Apollo"; else speakerName = "Ichiro, Apollo";
                break;
        }
 
        // インプットパラメーター(SSML)作成
        string requestSSML =
            string.Format(
                "<speak version='1.0' xml:lang='{0}'><voice xml:lang='{0}' xml:gender='{1}' name='Microsoft Server Speech Text to Speech Voice ({0}, {2})'>{3}</voice></speak>",
                pLanguage,
                pGender,
                speakerName,
                pText
            );
 
        // リクエスト作成
        HttpWebRequest request = WebRequest.CreateHttp(url);        
        request.Method = "POST";
        request.ContentType = "application/ssml+xml";
        if (pAudioFormat == "WAV")
        {
            request.Headers.Add("X-Microsoft-OutputFormat", "riff-16khz-16bit-mono-pcm");
        }
        else
        { 
            request.Headers.Add("X-Microsoft-OutputFormat", "audio-16khz-128kbitrate-mono-mp3");
        }
        request.Headers.Add("X-Search-AppId", (Guid.NewGuid().ToString()).Replace("-", ""));
        request.Headers.Add("X-Search-ClientID", (Guid.NewGuid().ToString()).Replace("-", ""));
        request.Headers.Add("Authorization", "Bearer " + pAccessToken);
        request.UserAgent = "SpeechToTextDemoApp";
 
        // パラメーターをUTF8でエンコード
        byte[] bytes = System.Text.Encoding.UTF8.GetBytes(requestSSML);
        request.ContentLength = bytes.Length;
 
        // パラメーターをリクエストに追加
        System.IO.Stream s = request.GetRequestStream();
        s.Write(bytes, 0, bytes.Length);
        s.Close();
 
        // リクエスト送信/レスポンス取得
        WebResponse webResponse = request.GetResponse();
 
        // レスポンスをストリームに変換
        Stream responseStream = webResponse.GetResponseStream();
        if (responseStream == null)
        {
            throw new Exception("responseStreamがNULLです。");
        }
 
        // レスポンスの中身を音声ファイルとして保存
        string audioFile = "";
        if (pAudioFormat == "WAV")
        {
            audioFile = System.Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"\TextToSpeech.wav";
        }
        else
        {
            audioFile = System.Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"\TextToSpeech.mp3";
        }
        FileStream fs = new FileStream(audioFile, FileMode.Create, FileAccess.Write);
 
        byte[] readData = new byte[1024];
        while(true)
        {
            // データを読み込む
            int readSize = responseStream.Read(readData, 0, readData.Length);
            if (readSize == 0)
            {
                // 全てのデータを読み込んだら抜ける
                break;
            }
            // 書き込む
            fs.Write(readData, 0, readSize);
        }
 
        fs.Close();
        responseStream.Close();
 
        return audioFile;
 
    }
    catch (WebException webException)
    {
        Stream responseStream = webException.Response.GetResponseStream();
                StreamReader streamReader = new StreamReader(responseStream);
        tblkMessage.Text = streamReader.ReadToEnd();
        return null;
    }
    catch (Exception exception)
    {
        tblkMessage.Text = exception.Message;
        return null;
    }
}

 前述の処理にコンストラクタやボタンクリック時のイベントを含めると、最終的には「MainWindow.xaml.cs」は、こちらのようになります(※クリックしてダウンロードできます)

 どうでしょうか。特に難しいところはないのではないかと思います。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。