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を用いてテキストを合成音声にする流れ
- 無料アカウントを作る。
- 30日間無料のBing Speech APIのAPIキーを取得する(キーは二つ出るけどどっち使っても変わらない)。
- APIキーをPOSTしてBearerトークンを取得する。
- 読み上げて欲しい文章の入ったSSMLとBearerトークンをPOSTしてバイナリデータを受け取る。
- 受け取ったバイナリデータをBase64にしてaudioタグのsrcに入れる。
- ボタンを押すと音が流れる。
- 楽しい🎉
細かい説明
今回のプログラムの中ではPOSTして取ってきたBearerトークンをもう一度POSTし直さなければならず、その部分は同期的に行わなければならないため、後半のバイナリデータを受け取るPOSTの部分を関数にして、前半のBearerトークンを取得する部分の関数の中にネスト状に入れています。
また、バイナリデータを受け取る部分はjQueryで実行すると書く量が増えると言うことだったので、その部分はXMLHttpRequestで実行しています。
ソースコード
//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