SQLの窓

2016年05月27日


Google Chrome で音声認識の結果をブログの投稿テキストエリアに転送するブックマークレット



この Seesaa ブログでもテストしました(リッチテキストは使用できません)。ブックマークレットを実行すると、ページの左上に IFRAME のウインドウを埋め込みます。その後、ページ内の対象のテキストエリアにカーソルを置くと開始ボタンが使えるようになるので『開始』をクリックします。

あとは、マイクで話すだけです。時々固まって動作しなくなる場合があるのでその場合は、『終了ボタン』を押すして再度開始ボタンを押して再開して下さい。

終了ボタンを押すと停止状態になり、テキストエリア内のテキストを編集しても結構です。最後に改行等を入れて開始ボタンで再開すると、最後の位置に追加して行きます。

長い時間何も音声入力が無いと自動的に終了します。

Google Chrome で音声認識の精度は想像以上に凄いです。学生がおもしろがって般若心経を朗読はじめたんですが、見事に認識されました(凄い)。

※ 前後にある文章や語句によって結果を変更して行くのがリアルタイムに見て取れる場合があります
▼ ブックマークレット登録用リンク
音声からテキスト

コードのメインは、Google Chrome での音声認識処理 を使用しています。但し、処理部分は私のサイトの recognition.js の中にあります。
parent.document.getElementById("if").style.position='absolute';
parent.document.getElementById("if").style.width='400px';
parent.document.getElementById("if").style.height='100px';
parent.document.getElementById("if").style.left='0px';
parent.document.getElementById("if").style.top='0px';
parent.document.getElementById("if").style.zIndex=0x7FFFFFFF;
parent.document.getElementById("if").style.borderColor='#000000';
parent.document.getElementById("if").style.borderWidth='1px';
parent.document.getElementById("if").style.borderStyle='solid';

parent.window.ds = parent.document.createElement('div');
parent.window.ds.setAttribute('id','ds')
parent.document.body.appendChild(parent.window.ds);
parent.document.getElementById("ds").style.position='absolute';
parent.document.getElementById("ds").style.width='400px';
parent.document.getElementById("ds").style.height='100px';
parent.document.getElementById("ds").style.left='8px';
parent.document.getElementById("ds").style.top='8px';
parent.document.getElementById("ds").style.zIndex=0x7FFFFFFE;
parent.document.getElementById("ds").style.backgroundColor='#000000';

var userAgent = window.navigator.userAgent.toLowerCase();

if (userAgent.indexOf("msie") > -1) {
	parent.document.getElementById("ds").style.filter='alpha(opacity=18)';
}
else {
	parent.document.getElementById("ds").style.opacity=.18;
}


str="";
str+="<body style='margin:0;background-color:#ffffff;padding:15px;'> \n";
str+="<style> \n";
str+=" * { \n";
str+=" font-family:";
str+=" \"メイリオ\", Meiryo, \"MS Pゴシック\"; \n";
str+=" font-size:12px;";
str+="} \n";
str+="</style> \n";

str+="<"+"script src=\"//ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js\"></"+"script> \n";
str+="<div id=\"check\"></div> \n";
str+="<input id=\"st_btn\" type=\"button\" value=\"開始\" disabled> \n";
str+="<input id=\"ed_btn\" type=\"button\" value=\"終了\" disabled> \n";
str+="<div id=\"info\" style='display:inline-block;'></div> \n";
str+="<div id=\"result\"><div id=\"first_text\"></div></div> \n";

str+="<"+"script> \n";
str+="$( function(){ \n";
str+=" \n";
str+="	load_action(); \n";
str+=" \n";
str+="} ); \n";
str+="</"+"script> ";


str+="</body> \n";

document.write( str );
document.close();

parent.scroll(0,0);

var target_textarea;
var rec = null;
var target;
var back_text = "";

function load_action() {

	parent.focus();

	var iid = setInterval(function(){
		parent.focus();
		target_textarea = parent.document.activeElement;
		if ( (target_textarea.tagName).toLowerCase() == "textarea" ) {
			$("#check").text("textarea を取得しました");
			clearInterval(iid);
			$("#st_btn").prop("disabled", false);
			$("#ed_btn").prop("disabled", false);
		}
		else {
			$("#check").text("テキストエリアにカーソルを置いて下さい");
		}
	},500);

	if ( typeof webkitSpeechRecognition === 'undefined' ) {
		$("#info").text("使用できません");
		$("#st_btn").prop("disabled", true);
		$("#ed_btn").prop("disabled", true);
	}
	else {

		target = $("#first_text");

		// インスタンス作成
		rec = new webkitSpeechRecognition();
		// 初期設定
		rec.lang = "ja-JP";
		rec.interimResults = true;
		rec.continuous = true;
		// イベント登録
		rec.onerror = function(){
			$("#info").text("error");
		};
		rec.onnomatch = function(){
			$("#info").text("nomatch");
		};
		rec.onsoundstart = function(){
			$("#info").text("音検知");
		};
		rec.onsoundend = function(){
			$("#info").text("soundend");
		};
		rec.onspeechstart = function(){
			$("#info").text("スピーチ開始");
		};
		rec.onspeechend = function(){
			$("#info").text("スピーチ終了");
		};

		rec.onstart = function(){
			$("#info").text("開始");
		};
		rec.onend = function(){
			$("#info").text("終了");
		};
		// 結果テキストの作成
		rec.onresult = function(ev){
			var obj = ev.results;
			for (var i = ev.resultIndex; i < obj.length; i++ ) {
				if( obj[i].isFinal ) {
					target.text(obj[i][0].transcript);
					target = $("<div></div>");
					$("#result").append(target);
					target_textarea.value = back_text + $("#result").text();
				}
				else {
					target.text(obj[i][0].transcript);
				}
			}
		};	

		// ボタンイベント
		$("#st_btn").on("click",function(){

			back_text = target_textarea.value;

			rec.start();
			$("#st_btn").prop("disabled",true);
		})
		$("#ed_btn").on("click",function(){
			rec.stop();
			$("#st_btn").prop("disabled",false);
			$("#result").html("<div id=\"first_text\"></div>");
			target = $("#first_text");

		})
	}

}
途中、IE のチェックをしているところがありますが、汎用テンプレートを使用して作成しているからです。

改行を入れて読みやすくしたブックマークレットコード
javascript:
var%20wnd=document.createElement('iframe');
wnd.setAttribute('id','if');
wnd.frameBorder=0;
document.body.appendChild(wnd);
var%20url='';
if((location.href).substr(0,5)=='https'){
	url='https://lightbox.sakura.ne.jp/toolbox/recognition.js';
}else{
	url='http://toolbox.winofsql.jp/recognition.js';
}
wnd.contentWindow.document.write('<script src=\''+url+'\' charset=\'utf-8\'></script>')
最後の document.write は DOM でも書けますが、どうせ js のほうでは画面を作成するのに document.write を使用せざるを得ないので同じ事です。初期画面の HTML や JavaScript の文字列化はこちらから実行できます。
( プログラム用文字列 の JavaScript ボタン )


posted by lightbox at 2016-05-27 22:41 | JavaScript オブジェクト | このブログの読者になる | 更新情報をチェックする

2016年05月24日


Google Chrome での音声認識処理






試したくて、マイクを見にヤマダ電機に言ったら、最低でも780円くらいしたのでやめて、古い WEBカメラを引っ張り出して使ったら使えました。

参考にしたのは、こちら。確認した仕様はこちら。

処理の中核を確認して、音声が途切れる毎に表示 DIV を追加する方式にして実装しました。イベントはたくさんありますが、直接必要なのは result イベントです。全く音声がない時間が5秒くらい? 続くと自動的に終わります。

Google Chrome の設定のプライバシーのコンテンツの設定でマイクを選びました。例外の管理では、一度ブロックしたサイトの削除して使えるようにできます。



ソースコード
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>

<input id="st_btn" type="button" value="開始">
<input id="ed_btn" type="button" value="終了">
<div id="info"></div>
<div id="result">
	<div id="first_text"></div>
</div>
<script>
var rec = null;
var target = $("#first_text");

$(function(){
	if ( typeof webkitSpeechRecognition === 'undefined' ) {
		$("#info").text("使用できません");
		$("#st_btn").prop("disabled", true);
		$("#ed_btn").prop("disabled", true);
	}
	else {
		// インスタンス作成
		rec = new webkitSpeechRecognition();
		// 初期設定
		rec.lang = "ja-JP";
		rec.interimResults = true;
		rec.continuous = true;
		// イベント登録
		rec.onerror = function(){
			$("#info").text("error");
		};
		rec.onnomatch = function(){
			$("#info").text("nomatch");
		};
		rec.onsoundstart = function(){
			$("#info").text("音検知");
		};
		rec.onsoundend = function(){
			$("#info").text("soundend");
		};
		rec.onspeechstart = function(){
			$("#info").text("スピーチ開始");
		};
		rec.onspeechend = function(){
			$("#info").text("スピーチ終了");
		};

		rec.onstart = function(){
			$("#info").text("開始");
		};
		rec.onend = function(){
			$("#info").text("終了");
		};
		// 結果テキストの作成
		rec.onresult = function(ev){
			var obj = ev.results;
			for (var i = ev.resultIndex; i < obj.length; i++ ) {
				if( obj[i].isFinal ) {
					target.text(obj[i][0].transcript);
					target = $("<div></div>");
					$("#result").append(target);
				}
				else {
					target.text(obj[i][0].transcript);
				}
			}
		};	

		// ボタンイベント
		$("#st_btn").on("click",function(){
			rec.start();
			$("#st_btn").prop("disabled",true);
		})
		$("#ed_btn").on("click",function(){
			rec.stop();
			$("#st_btn").prop("disabled",false);
		})
	}
	
});

</script>

関連する記事

ほぼ、Google Chrome 限定ですが Web Speech API の現時点での実装と問題点回避方法



posted by lightbox at 2016-05-24 02:08 | JavaScript オブジェクト | このブログの読者になる | 更新情報をチェックする

2016年05月14日


ほぼ、Google Chrome 限定ですが Web Speech API の現時点での実装と問題点回避方法

※ 長い文章を試すと、安全なのは 80文字くらいでした。長すぎると壊れて、Google Chrome を再起動する必要がありました

何故か最初のロードで speechSynthesis.getVoices() は一覧を取って来ません。この一覧でコンボボックスを作る必要があるので、とても回りくどい事をしています。

インターネットを調べましたが、過去のものは動かないものが多く、理由はこの Voice をセットしていない事が原因でした。

また、Firefox では、 about:config で、media.webspeech.synth.enabled を設定する必要があるのと、設定しても Voice が一つしか取得されず英語のみで、何故か Microsoft の 『Microsoft Anna - English (United States)』で、仕様がイマイチよく解りません。コンボボックスは選択しなくても英語を入力すれば話してくれます


UI を jQuery で実装
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<textarea id="target_text" style='width:300px;height:60px;'>こんにちは</textarea><br>
<input type="button" id="speech_button" value="実行">
<select id="voice"></select>

<script>
var voice_types;

$(function(){

	if ( typeof SpeechSynthesisUtterance === 'undefined' ) {
		$('#target_text').val("Web Speech API は使えません");
	}

	// 何故か初回が取れない
	voice_types = speechSynthesis.getVoices();
	if ( voice_types.length == 0 || $('#voice').children().length == 0 ) {
		// 無かった場合、もう一度
		get_voice_type ()
	}

	$("#speech_button").on("click", function() {
		// 念のために取り出す
		voice_types = speechSynthesis.getVoices();
		if ( voice_types.length == 0 ) {
			// 再取得で無かった場合、もう一度
			get_voice_type ();
			return;
		}

		var speech = new SpeechSynthesisUtterance($("#target_text").val());

		// 選択した言語を使う
		speech.voice = voice_types[ $('#voice > option:selected').val() ];

		speechSynthesis.speak(speech);

	});


});

function get_voice_type () {
		setTimeout(function(){
			voice_types = speechSynthesis.getVoices();
			$('#voice > option').remove();
			for( i = 0; i < voice_types.length; i++) {
				$('#voice').append($('<option>').text(voice_types[i].name).val(i));
			}
			$('#voice').prop("value",11);
		},100);
}

</script>

ここでは使っていませんが、pitch は声の高さを 0.1 づつで 1.6 ぐらいが限界みたいです(それ以上はかすれた変な声)。rate は速さで 0.1 づつ増やせますが、1.8 くらいで聞き取りは限界です。

関連する記事

Google Chrome での音声認識処理



posted by lightbox at 2016-05-14 14:57 | JavaScript オブジェクト | このブログの読者になる | 更新情報をチェックする
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。

Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。

また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。

※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです

対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。


※ エキスパートモードで表示しています

アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります

<% if:page_name eq 'archive' -%>
アーカイブページでのみ表示される内容
<% /if %>

<% if:page_name eq 'category' -%>
カテゴリページでのみ表示される内容
<% /if %>

<% if:page_name eq 'tag' -%>
タグページでのみ表示される内容
<% /if %>
この記述は、以下の場所で使用します
container 終わり

フリーフォントで簡単ロゴ作成
フリーフォントでボタン素材作成
フリーフォントで吹き出し画像作成
フリーフォントではんこ画像作成
ほぼ自由に利用できるフリーフォント
フリーフォントの書体見本とサンプル
画像を大きく見る為のウインドウを開くボタンの作成

CSS ドロップシャドウの参考デモ
BUTTONS (CSS でボタン)
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり