ドコモAPIを使った音声合成を使ってみる
- この記事は書きかけです。
ドコモからスマートフォンで利用できるAPIが公開されているので、使ってみます。
顔・画像・音声(入力・出力)など様々なAPIがあるのですが、今回は音声合成のAPIを利用してみます。
APIの概要 | docomo Developer support | NTTドコモ
注意事項
- 利用には利用規約の同意とアカウント登録が必要です。(Facebook/Googleアカウントでのログイン可能)
- テスト版は簡単な申請で、即日利用可能ですが、本番で利用するには審査がある場合があります。
音声合成を使う
音声合成APIは
- スマホ用SDKの「音声合成 SDK (Powered by エーアイ)」
- WEB APIの音声合成【Powered by HOYAサービス】
- WEB APIの【Powered by NTTアイティ】音声合成API の3種類があります。
音声合成 | docomo Developer support | NTTドコモ
HOYAの音声合成WEBAPI
- 以下の感じで試すことができます。 *「XXXXXXXX」の部分は発行されたAPIKEYに置き換えてください。
- iOSとPCのChromeで動くのは確認できましたが、AndroidとIEでは音声は再生されません。
- PC版Chromeでは、タッチなしで音声再生が可能ですが、iOSではタッチイベントがないと、audio.play();が実行されないようです。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <HTML> <HEAD> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.min.js"></script> </HEAD> <BODY> <script> jQuery(function($) { xhr = new XMLHttpRequest(); xhr.open('POST', 'https://api.apigw.smt.docomo.ne.jp/voiceText/v1/textToSpeech?APIKEY=XXXXXXXX', true); xhr.responseType = 'arraybuffer' xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded") var datax = "text=しゃべる内容&speaker=hikari&emotion=happiness&emotion_level=2&format=ogg"; xhr.onload = function (e) { if( this.status == 200){ view = new Uint8Array(this.response); blob = new Blob([view], { "type" : "audio/wav" }); URL = window.URL || window.webkitURL; audio = new Audio(URL.createObjectURL(blob)); //audio.play(); } }; data = datax; xhr.send(data); }); function sound(){ alert("start"); audio.play(); } </script> <p><a onClick="sound()">このテキストを押すと挨拶します!</a></p> </BODY> </HTML>
【Powerd by エーアイ】音声合成 SDK for iOS v1.0.1をつかう
サンプルプロジェクトの実行
- SDKをダウンロード(AITalk音声合成SDK_for_iOS_1.0.1)
- sampleフォルダのaiTalkSample.zipを解凍
- aiTalkSample.xcodeprojをXcodeで開く
- ViewController.mの
NSString * const APIKEY = @"";
にAPIKEYを設定
5ビルドすると以下のような画面が表示され、音声が再生されます。
実際のプロジェクトに組み込んでみる
//ドコモ音声合成APIヘッダー #import "AiTalkError.h" #import "AiTalkProsody.h" #import "AiTalkSsml.h" #import "AiTalkTextToSpeech.h" #import "AiTalkVoice.h" #import "AiTalkVoiceBase.h" #import "AuthApiKey.h" #import "SdkError.h"
- (void)viewDidLoadあたりで、APIKEYを利用して音声合成を初期化
// 開発者ポータルから取得したAPIキーの設定 [AuthApiKey initializeAuth:@"XXXXXX"];
- その後こんな感じ
//音声再生 AiTalkSsml * ssml = [[AiTalkSsml alloc]init]; AiTalkVoice * voice = [[AiTalkVoice alloc]initWithVoiceName:@"nozomi"]; [voice addText:[NSString stringWithFormat:@"のこり あと%dスタンプです。",placeDictionaryElementsNum - getStampNum]]; [ssml addVoice:voice]; AiTalkTextToSpeech * search = [[AiTalkTextToSpeech alloc]init]; AiTalkError *sendError = [search requestAiTalkSsmlToSound:[ssml makeSsml] onComplete:^(NSData *data) { NSLog(@"onComplete"); [self playAudio:data]; } onError:^(SdkError *receiveError) { [self onError:receiveError]; }]; if(sendError){ [self onError:sendError]; }
上記実行するには別途、サンプルプロジェクトの
- -(void)onError:(NSError *)error
- -(void)playAudio:(NSData *)data
- -(Byte *)setHeader:(long)dataLength
- -(NSData)addHeader:(NSData)data
をコピーするなどして持ってくる必要があります。
Androidでも動かす
- やったのはMac+AndroidStudio
- Android版のサンプルプロジェクトはそのままではビルドできない
サンプルの実行のためにやったこと
エラー: libpng error: Not a PNG file
- ic_launcher.png を別のPNGファイルで上書き(3箇所)
エラー: Error:(1, 1) \65279 は不正な文字です。
- MainActivity.javaをUTF-8(BOM無し)で保存し直し
エラー: Error:duplicate files during packaging of APK /Users/***/AndroidStudioProjects/AitalkSample/app/build/outputs/apk/app-debug-unaligned.apk
- エラー内容になるように app/build.gradleに以下を記述
android { packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' } }
手元のプロジェクトに組み込んでみる
import jp.ne.docomo.smt.dev.common.exception.SdkException; import jp.ne.docomo.smt.dev.common.exception.ServerException; import jp.ne.docomo.smt.dev.aitalk.AiTalkTextToSpeech; import jp.ne.docomo.smt.dev.aitalk.data.AiTalkSsml; import jp.ne.docomo.smt.dev.common.http.AuthApiKey;
- Classの先頭で宣言
// 音声文字列SSML private AiTalkSsml ssml; // 非同期タスク private AitestAsyncTask task; // 警報ダイアログ private AlertDialog.Builder dlg; static final String APIKEY ="xxxxx";
- onCreateで呼び出す
// API キーの登録 AuthApiKey.initializeAuth(APIKEY); try{ // SSMLテキスト作成 ssml = new AiTalkSsml(); ssml.startVoice("nozomi"); ssml.addText("こんにちは"); ssml.endVoice(); // 音声変換を実行し、音声を出力する。 // メインスレッドではHTTP通信できないので別スレッドで task = new AitestAsyncTask (dlg,AitestAsyncTask.henkan_ssml_sound); task.execute(ssml); }catch (Exception ex){ Log.e(TAG, ex.toString()); }
// 非同期タスクのバックグラウンド実行部分 @Override protected byte[] doInBackground(Object... params) { byte[] resultData = null; try { // 要求処理クラスを作成 AiTalkTextToSpeech search = new AiTalkTextToSpeech(); // 要求処理クラスにリクエストデータを渡し、レスポンスデータを取得する switch (_henkan){ case henkan_ssml_sound: resultData = search.requestAiTalkSsmlToSound(((AiTalkSsml)params[0]).makeSsml()); break; case henkan_ssml_aikana: resultData = search.requestAiTalkSsmlToAikana(((AiTalkSsml)params[0]).makeSsml()).getBytes(); break; case henkan_ssml_jeitakana: resultData = search.requestAiTalkSsmlToJeitakana(((AiTalkSsml)params[0]).makeSsml()); break; case henkan_aikana_sound: resultData = search.requestAikanaToSound((String)params[0]); break; case henkan_aikana_jeitakana: resultData = search.requestAikanaToJeitakana((String)params[0]); break; case henkan_jeitakana_sound: resultData = search.requestJeitakanaToSound(((String)params[0]).getBytes("Shift_Jis")); break; default: return null; } // 音声変換の場合は、スピーカに出力 switch (_henkan){ case henkan_ssml_sound: case henkan_aikana_sound: case henkan_jeitakana_sound: // 音声出力用バッファ作成 int bufSize = AudioTrack.getMinBufferSize(16000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT); // ビッグエディアンをリトルエディアンに変換 search.convertByteOrder16(resultData); // 音声出力 AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, 16000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, bufSize, AudioTrack.MODE_STREAM); at.play(); at.write(resultData, 0, resultData.length); // 音声出力待ち Thread.sleep(resultData.length/32); break; } } catch (SdkException ex) { isSdkException = true; exceptionMessage = "ErrorCode: " + ex.getErrorCode() + "\nMessage: " + ex.getMessage(); } catch (ServerException ex) { exceptionMessage = "ErrorCode: " + ex.getErrorCode() + "\nMessage: " + ex.getMessage(); } catch (Exception ex){ exceptionMessage = "ErrorCode: " + "**********" + "\nMessage: " + ex.getMessage(); } return resultData; } @Override protected void onCancelled() { } @Override protected void onPostExecute(byte[] resultData) { if(resultData == null){ // エラー表示 if(isSdkException){ _dlg.setTitle("SdkException 発生"); }else{ _dlg.setTitle("ServerException 発生"); } _dlg.setMessage(exceptionMessage + " "); _dlg.show(); } } }