(1) XMOS社から発売されている VocalFusion スピーカー開発キット (XVF-3000) と
(2) Google Cloud Platform (GCP) 上で利用できる Speech API、
(3) そして無料で利用できる音声合成システム Open JTalk を利用して、
自分(男性)の声を女性の声に変更する、ボイス・チェンジャーを作ってみましたので、ご紹介します。
VocalFusion スピーカー開発キット (XVF-3000) のビデオは、以下のページからも紹介しています。
https://www.xmos.com/products/voice/vfusion
- VocalFusion スピーカー開発キット (XVF-3000) :
- 4つの Digital MEMS Microphone と、USB-PHY / PDM マイク インターフェース / Voice DSP / スピーカーインターフェース (I2S) を搭載した XVF-3000 ボイス・プロセッサ による、Voice User Interface 評価ボード
- Google Cloud Platform (GCP), Speech API:
- Google 社が提供するクラウド・サービス。様々なサービスやAPI が用意されており、素早く開発を行うことができる。
- Open JTalk
- HTS Working Group が開発・提供する Free の Text-to-Speech ソフトウェア。オープンソースライセンスの一つである、Modified BSD ライセンスが設定されている。多謝。
1.全体構成
今回は、MAC OSX を ホストマシンとしています。VocalFusion スピーカー開発キット (XVF-3000)と MAC OSX を、USB接続します。
また、VocalFusion スピーカー開発キット (XVF-3000)の Audio Out (3.5mm Jack) とスピーカーを接続します。
MAC OSX は、インターネットに接続し、Google Cloud Platform に接続できるようにします。
2. Google Cloud Platform 上の設定
Google Cloud Platform では、「無料トライアル」のサービスを提供しています(2017年6月13日現在)。まずは、この無料トライアルから、始めることをお勧めします。
はじめて使用される方は、アカウント作成含め各種設定が必要になります。はじめるための様々な情報は Web から入手できますので、以下にいくつかURLをご紹介いたします。
- Google Cloud Platform スタートガイド
- 第0回 Google Cloud Platformをはじめよう! アカウント登録~画像認識APIを試してみよう
- https://book.mynavi.jp/manatee/detail/id=65673
(非常にわかりやすく纏めて頂いています。お勧めです。多謝)
Speech API, Translate API, Natural Language API を有効にします。
各APIを有効にする方法は、前述の「第0回 Google Cloud Platformをはじめよう」のページ中ごろに、[1] APIの有効化 の記述がありますので、それを参考にして設定を行います。次に、認証キーを入手します。
前述の「第0回 Google Cloud Platformをはじめよう」では、「[2] APIキーの発行」の箇所で、APIキーを入手する方法を紹介していますが、ここでは、「サービスアカウントキー」を生成します。生成の手順は、以下のGoogleのページをご参照ください(まだ日本語のページが無いようです) https://developers.google.com/identity/protocols/application-default-credentials (How the Application Default Credentials workの箇所をご参照ください)
生成した JSON ファイルは、ローカルのファイルとして保存します。インターネットで公開することの無いようにします。他の人からもアクセスできないように管理します。
最後に、環境変数に、Google Cloud Platform 上のProject 名と、保存した JSON のファイル名を設定します。
以下の例は、MAC OSX で、Terminal 上で環境変数を設定した例です。export GCLOUD_PROJECT=xxx-yyy-zzz
export GOOGLE_APPLICATION_CREDENTIALS="xxxxxxxxx.json"
3.Node のインストール
以下のURLにアクセスして、Node をインストールします。 バージョン v6.* をインストールします(2017年6月13日現在)。https://nodejs.org/
4. Homebrew のインストール(MAC OSXのみ)
MAC OSX の Terminal 上で、以下のコマンドを実行します。/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
5. SOX のインストール
SOX は、Sound eXchange という Open Source のソフトウエアです。これをインストールします MAC OSX の Terminal 上で、以下のコマンドを実行します。brew install sox
6. Open JTalk のインストール
MAC OSX の Terminal 上で、以下のコマンドを実行します。brew install open-jtalk
7. 実行ファイルの準備
今回の実行プログラムは、Node.js で記載されています。 このプログラムは、Github 上の Example を、参考に(コピーして)作成しています。Google Could Platform Node.js Examples:
https://github.com/GoogleCloudPlatform/nodejs-docs-samples
(7-1) package.json (必要となるNode Moduleを記載)
(7-2) voiceChange.js (Node.js プログラム)
実際の記述は、このページの末尾に記載します。
8. 環境設定
前述の 2. の箇所で紹介した環境変数が設定されていることを確認し、前述の package.json ファイルがある directory で、以下のコマンドを実行します。
(MAC OSX の Terminal 上で、以下のコマンドを実行します)
npm install
9. 実行前の設定(Speech 開始と終了の設定)
VocalFusion スピーカー開発キット (XVF-3000) では、音声を認識する音の閾値を設定することができます。これを、VAD (Voice Activity Detection) と呼んでいますが、この閾値をコントロールすることで、発話完了をより正確に制御することができます。
発話完了が正確に制御できれば、発話開始から音声認識アプリケーションの応答までの時間が素早くなることが期待できます。
以下では、その他の、一般に公開されている情報を使用して、その閾値を変更する方法を紹介します。
前述の「npm install」で、次のファイルがインストールされます。
node_modules/node-record-lpcm16/index.js
このファイルでは、発話開始と終了を、ボイスチェンジャー・ツール側で認識するための閾値(threshold)の値を設定しています。これらは Default で、null に設定されています。
var defaults = {
sampleRate: 16000,
compress: false,
threshold: 0.5,
thresholdStart: null,
thresholdEnd: null,
silence: '1.0',
verbose: false,
recordProgram: 'rec'
}
前述の、thresholdStart, thresholsEnd の値を、環境に合わせて 0.2 - 0.5 に設定します。
静かな環境であれば、null の設定でも良いですが、多少ノイズのある環境ですと、ツール側が発話完了を認識するのに時間がかかる場合があります。
その場合には、thresholdEnd の値を設定することで、発話終了が確実に認識され、結果的に、ボイスチェンジャー・ツールのレスポンスも早くなります。
10. 実行
MAC OSX の Terminal 上で、以下のコマンドを実行します。
実行後、日本語で発話します(何か喋ります)。
node voiceChange.js listen -l ja -t en
結果として、次のような表示となり、加えて、自分が喋った内容が、女性の声(音声合成)で繰り返されます(再生されます)。以下では、「今日の天気を教えてください」と喋ったケースです。
(ja) 今日の天気を教えてください
(en) Please let me know what's the weather like today.
*** Natural language analysis - parts of speech in Japanese:
NOUN: 今日
PRT: の
NOUN: 天気
PRT: を
VERB: 教え
PRT: て
VERB: ください
喋った内容が、Speech API により、日本語のテキストに変換されています。
また、そのテキストを、Translate API に入力し、英語の文章に変換しています。
最後に、日本語のテキストを、Natural Language API に入力し、解析しています。
Natural Language API で、日本語の文章を名詞や動詞に分解できますので、その結果を使って、様々な応答を実装することが容易になります。
※
Google Natural Language API 以外にも、自然言語を解析するフリーのツールがあります。"MeCab" という、Node で使用可能なフリーのツールもあります。"Node Mecab" で検索すると様々なページで、使用方法を紹介しています。
11. VocalFusion スピーカー開発キット (XVF-3000) による、遠くからの音声入力とエコーキャンセル
VocalFusion スピーカー開発キット (XVF-3000)を前述のように接続することで、離れた場所から音声入力が可能となります。
その他、ホスト(この例では、MAC OSX)からVocalFusion スピーカー開発キットを介して音楽を再生した場合には、マイクに入力された音声と音楽のうち、音声だけをホストに送ることが可能です。
このため、音楽再生中でも、クリアな音声をホストに送ることになるため、音声認識率が向上することが期待できます。
12. 実行ファイルの記述
実際の記述を以下に紹介します。
これは、あくまで 「とりあえず、動いた」というレベルの内容であり、様々環境下での動作検証を行なったものではございませんので、何卒、ご了承ください。
(あくまで、Node.js も知らない人が、Google Cloud Platform の Example をコピーして動かしてみたレベルのものとご理解ください。公開するのも恥ずかしいですが、ご参考まで。フィードバック歓迎です。)
(12-1) package.json (必要となるNode Moduleを記載)
--------------------------------------------------------------------------{
"name": "nodejs-docs-samples-speech",
"version": "0.0.1",
"private": true,
"license": "Apache Version 2.0",
"author": "Google Inc.",
"scripts": {
"test": "cd ..; npm run st -- --verbose speech/system-test/*.test.js"
},
"dependencies": {<
"@google-cloud/translate": "0.8.0",
"@google-cloud/speech": "0.9.0",
"@google-cloud/language": "0.10.2",
"@google-cloud/storage": "1.0.0",
"node-record-lpcm16": "0.3.0",
"yargs": "7.0.2"
},
"engines": {
"node": ">=4.3.2"
}
} --------------------------------------------------------------------------
(12-2) voiceChange.js (Node.js プログラム)
--------------------------------------------------------------------------
/**
* Copyright 2016, Google, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This application demonstrates how to perform basic recognize operations with
* with the Google Cloud Speech API.
*
* For more information, see the README.md under /speech and the documentation
* at https://cloud.google.com/speech/docs.
*/
'use strict';
var TMPTEXTFILE = "tmp000_mei.txt";
var TMPWAVFILE = "tmp000_mei.wav";
var OPENJTALKDIR = "/usr/local/Cellar/open-jtalk/1.10_1";
var OPENJTALKEXEC = "/usr/local/bin/open_jtalk";
var OPENJTALKDIC = OPENJTALKDIR + "/dic";
var OPENJTALKVOICE = OPENJTALKDIR + "/voice/mei/mei_happy.htsvoice";
var OPENJTALKCMD = OPENJTALKEXEC + " -x " + OPENJTALKDIC + " -m " + OPENJTALKVOICE + " -ow " + TMPWAVFILE + " -r 0.9 " + TMPTEXTFILE ;
var AFPLAYCMD = "afplay " + TMPWAVFILE + "; rm -f " + TMPTEXTFILE + " " + TMPWAVFILE;
///////////////////////////////////////////////////
function execOpenJTalk (text) {
const execSync = require('child_process').execSync;
var fs = require('fs');
fs.writeFileSync(TMPTEXTFILE, text);
const result1 = execSync(OPENJTALKCMD);
const result2 = execSync(AFPLAYCMD);
}
/////////////////////////////////////////////////////////////
function analyzeSyntaxOfText (text) {
// [START language_syntax_string]
// Imports the Google Cloud client library
const Language = require('@google-cloud/language');
// Instantiates a client
const language = Language();
// Instantiates a Document, representing the provided text
const document = language.document({ content: text });
// Detects syntax in the document
document.detectSyntax()
.then((results) => {
const syntax = results[0];
console.log('\n*** Natural language analysis - parts of speech in Japanese:');
syntax.forEach((part) => {
console.log(`${part.partOfSpeech.tag}:\t ${part.text.content}`);
});
console.log('\n');
})
.catch((err) => {
console.error('ERROR:', err);
});
}
///////////////////////////////////////////////////////////////////
function translateText (text, langOrg, target) {
// [START translate_translate_text]
// Imports the Google Cloud client library
const Translate = require('@google-cloud/translate');
// Instantiates a client
const translate = Translate();
translate.translate(text, target)
.then((results) => {
let translations = results[0];
translations = Array.isArray(translations) ? translations : [translations];
translations.forEach((translation, i) => {
console.log(`(${target}) ${translation}`);
if (target == "ja") {
execOpenJTalk(translation);
}
//Analysis
if (langOrg == "ja") {
analyzeSyntaxOfText(text);
}
if (target == "ja") {
analyzeSyntaxOfText(translation);
}
});
})
.catch((err) => {
console.error('ERROR:', err);
});
}
///////////////////////////////////////////////////////////////////////
function myTextToSpeech (text, languageCode, languageCodeTo) {
//printout result of speech-to-text
process.stdout.write("\n(" + languageCode +") " + text + "\n\n");
//translate
translateText(text, languageCode, languageCodeTo);
if (languageCode == "ja") {
execOpenJTalk(text);
}
}
//////////////////////////////////////////////////////////
function streamingMicRecognize (encoding, sampleRateHertz, languageCode, languageCodeTo) {
// [START speech_streaming_mic_recognize]
const record = require('node-record-lpcm16');
// Imports the Google Cloud client library
const Speech = require('@google-cloud/speech');
// Instantiates a client
const speech = Speech();
const request = {
config: {
encoding: encoding,
sampleRateHertz: sampleRateHertz,
languageCode: languageCode
}
};
// Create a recognize stream
const recognizeStream = speech.createRecognizeStream(request)
.on('error', console.error)
.on('data', (data) => myTextToSpeech(data.results, languageCode, languageCodeTo));
// Start recording and send the microphone input to the Speech API
record.start({
sampleRateHertz: sampleRateHertz,
threshold: 0
}).pipe(recognizeStream);
console.log('Listening, press Ctrl+C to stop.');
}
require(`yargs`)
.demand(1)
.command(
`listen`,
`Detects speech in a microphone input stream. This command requires that you have SoX installed and available in your $PATH. See https://www.npmjs.com/package/node-record-lpcm16#dependencies`,
{},
(opts) => streamingMicRecognize(opts.encoding, opts.sampleRateHertz, opts.languageCode, opts.languageCodeTo)
)
.options({
encoding: {
alias: 'e',
default: 'LINEAR16',
global: true,
requiresArg: true,
type: 'string'
},
sampleRateHertz: {
alias: 'r',
default: 16000,
global: true,
requiresArg: true,
type: 'number'
},
languageCode: {
alias: 'l',
default: 'en-US',
global: true,
requiresArg: true,
type: 'string'
},
languageCodeTo: {
alias: 't',
default: 'en-US',
global: true,
requiresArg: true,
type: 'string'
}
})
.example(`node $0 listen`)
.wrap(120)
.recommendCommands()
.epilogue(`For more information, see https://cloud.google.com/speech/docs`)
.help()
.strict()
.argv;
--------------------------------------------------------------------------
(その他の投稿については、右上の「ページ」をご確認ください)
0 件のコメント:
コメントを投稿