Bing Speech APIを使ってテキストを読み上げた音データをバイナリで受け取る

# Bing Speech APIとは
https://azure.microsoft.com/ja-jp/services/cognitive-services/speech/
Microsoft AzureのCognitive Servicesの一つです。
Speech to Text API （音声認識）と Text to Speech API （音声合成）の二つから成ります。
今回はText to Speech APIの方を用いて、テキストを合成音声にしてもらおうと思います。
Ajax通信のためにjQueryを利用しています。

# APIを用いてテキストを合成音声にする流れ
1. 無料アカウントを作る。
1. 30日間無料のBing Speech APIのAPIキーを取得する（キーは二つ出るけどどっち使っても変わらない）。
1. APIキーをPOSTしてBearerトークンを取得する。
1. 読み上げて欲しい文章の入ったSSMLとBearerトークンをPOSTしてバイナリデータを受け取る。
1. 受け取ったバイナリデータをBase64にしてaudioタグのsrcに入れる。
1. ボタンを押すと音が流れる。
1. 楽しい🎉

# 細かい説明
今回のプログラムの中ではPOSTして取ってきたBearerトークンをもう一度POSTし直さなければならず、その部分は同期的に行わなければならないため、後半のバイナリデータを受け取るPOSTの部分を関数にして、前半のBearerトークンを取得する部分の関数の中にネスト状に入れています。
また、バイナリデータを受け取る部分はjQueryで実行すると書く量が増えると言うことだったので、その部分はXMLHttpRequestで実行しています。

# ソースコード
```main.js
//Tokenを取得する関数。この内部でバイナリの受け取りまで行うため、使用するときはこの関数を呼び出す。
function getToken(text) {
    var apikey = "API_KEY_IS_HERE";
    var uriBase = "https://api.cognitive.microsoft.com/sts/v1.0/issueToken";
    $.ajax({
        url: uriBase,
        beforeSend: function (xhrObj) {
            xhrObj.setRequestHeader("Content-Type", "text/plain");
            xhrObj.setRequestHeader("Ocp-Apim-Subscription-Key", apikey);
        },
        type: "POST",
    })
        .done(function (data) {
            console.log(data);
            receiveVoice(data,text); //ここで受け取ったBearerトークンを送り、バイナリデータを受け取る。
        })
        .fail(function (jqXHR, textStatus, errorThrown) {
            var errorString = (errorThrown === "") ? "Error. " : errorThrown + " (" + jqXHR.status + "): ";
            errorString += (jqXHR.responseText === "") ? "" : (jQuery.parseJSON(jqXHR.responseText).message) ?
                jQuery.parseJSON(jqXHR.responseText).message : jQuery.parseJSON(jqXHR.responseText).error.message;
            alert(errorString);
        });
}

//声データのバイナリを取得する関数。getTokenの中で呼び出される。
function receiveVoice(token,text) {
    var uribase = "https://speech.platform.bing.com/synthesize";
    var params = createParam(text);  //createParam関数は、読み上げて欲しい文章をSSMLに埋め込む関数。後述。
    $(function () {
        var xhr = new XMLHttpRequest();
        xhr.open('POST', uribase, true);
        xhr.setRequestHeader("X-Microsoft-OutputFormat", "audio-16khz-32kbitrate-mono-mp3");
        xhr.setRequestHeader("Content-Type", "application/ssml+xml");
        xhr.setRequestHeader("Authorization", "Bearer " + token);
        xhr.responseType = 'arraybuffer';
        xhr.onload = function (e) {
            console.log(this.response);
            var u8 = new Uint8Array(this.response);
            var binaryString = "";
            for (var i = 0; i < u8.byteLength; i++) {
                binaryString += String.fromCharCode(u8[i]);
            }
            var base = btoa(binaryString);
            console.log(base);
            var audio_init=document.getElementById("audio_init"); //HTMLの中にaudio_initという名前のdivタグを用意して入れる。
            audio_init.innerHTML="<audio controls src='data:audio/mp3;base64," + base + "'></audio>";
        };

        xhr.send(params);
    });
}

//SSMLを作る関数。
function createParam(str) {
    return '<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="http://www.w3.org/2001/mstts" xml:lang="en-US"><voice xml:lang="en-US" name="Microsoft Server Speech Text to Speech Voice (en-US, ZiraRUS)">' + str + '</voice></speak>\n';
    //今回は英語で読み上げて欲しかったために言語は英語、話者は英語話者を利用している。
}
```

# 感想
別にバイナリデータで受け取らなくても、と途中からずっと思っていたが、何かの役に立てば幸いです。

# 参考
https://qiita.com/icoxfog417/items/1f5d7d4e5758deca349b
http://var.blog.jp/archives/62330155.html
https://qiita.com/kojiro_ueda/items/cb303ed11c1755a02ad5
https://qiita.com/yaegaki/items/909587a2dae20467c74a
https://qiita.com/Yarimizu14/items/f56123c738f12ad1844a
https://qiita.com/isseium/items/12b215b6eab26acd2afe
https://qiita.com/yaegaki/items/909587a2dae20467c74a