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 オブジェクト | このブログの読者になる | 更新情報をチェックする

2014年10月08日


JavaScript の String と Date オブシェクトに、9999/99/99 書式の文字列日付を取り出すメソッドを追加する

月と日が一桁の場合、前に "0" を付加する為に、String.prototype.right がある事が前提です。 

文字列からのアプローチは、年・月・日の区切り文字に対して sdate を実行します(引数に Date オブジェクト)。
Date オブジェクトからのアプローチは、日付オブジェクトに対して、sdate を実行して、引数には区切り文字を指定します 
<script type="text/javascript">
String.prototype.right = function(n){
	var str = this.valueOf();
	str = str.substr(str.length-n,n);
	return str;
}
String.prototype.sdate = function(dt){
	var str = this.valueOf();
	var today = dt.getFullYear() + str + ("0"+(dt.getMonth()+1)).right(2) + str + ("0"+dt.getDate()).right(2);
	return today;
}
Date.prototype.sdate = function(str){
	var today = this.getFullYear() + str + ("0"+(this.getMonth()+1)).right(2) + str + ("0"+this.getDate()).right(2);
	return today;
}

console.log( "-".sdate(new Date()) )
console.log( (new Date()).sdate("-") )
</script>



タグ:javascript
posted by lightbox at 2014-10-08 11:16 | JavaScript オブジェクト | このブログの読者になる | 更新情報をチェックする

2014年02月01日


JavaScript オブジェクト作成の4態

1) 通常 JSON 記法
2) 無名 function 記法
3) 有名 function による インスタンス作成
<script type="text/javascript">

var obj1 =  {
	myobj: {
		func: function(){ return this.value },
		value: "OK"
	}
}

var obj2 = new function() {
	this.myobj = {
		func: function(){ return this.value },
		value: "OK"
	}
}

function obj() {
	this.myobj = {
		func: function(){ return this.value },
		value: "OK"
	}
}

var obj3 = new obj();

console.dir(obj1.myobj.func());
console.dir(obj2.myobj.func());
console.dir(obj3.myobj.func());
console.dir(obj1);
console.dir(obj2);
console.dir(obj3);
</script>


この結果では当然ですが、有名ファンクションでは『型』は function 名となっています。

さらに、もう一つの形として、Object 型の最初から myobj を設定しておくと以下のようになります。
<script type="text/javascript">
Object.prototype.myobj = {
	func: function(){ return this.value },
	value: "OK"
}

var obj1 =  {
}

var obj2 = new function() {
	this.myobj = {
		func: function(){ return this.value },
		value: "OK"
	}
}

console.dir(obj1.myobj.func());
console.dir(obj2.myobj.func());
console.dir(obj1);
console.dir(obj2);
</script>


空のオブジェクトでも、myobj が使用可能になりますが、全てのオブジェクトに対して定義されてしまいます。しかし、同名のプロパティを上書きするとそちらが有効になります。





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

2013年03月22日


JavaScript の function を new したものが、JSON フォーマットで記述した『Object』と同じである事のテスト

function は、他の言語で言うところの『クラス』のようなものですが、実際の扱いはそんなに簡単ではありません。しかし、少なくともこの二つが『インスタンス』である事は間違いなさそうです。

ですから、jQuery の .data メソッドでエレメントに対して『インスタンス』を保存する事ができるという事なので、以下の記事での改造はそれを利用しています。

使いどころが難しいですが、入力をコンボボックス化する jQuery プラグインの実装が不便だったので、modify しました。
そもそも、jQuery のプラグインは、jQuery の インスタンスに対してのメソッドの実装です。プラグインは、jQuery の機能を借りて、そのメソッドが特有の振る舞いをするような『オブジェクト』を window の中に作りだします。
そして、再度そのオブジェクトを操作する為にそのオブジェクトを参照する必要があるので、どこかに保存しなければなりません。
当然、再度参照するなら jQuery の インスタンスを介して( 実際はセレクタで選択されたもの )再度参照されるべきだと思います。
しかし、このプラグインでは単純に配列を作ってそこへ push していただけなので、『何番目に何が居る』かをプログラマは番号で呼び出すようになっていました。
これでは意味が無いので プラグインが作った『オブジェクト』を .data で 元の要素に保存しました。
( jQuery で console.log(typeof $("#test")); と実行すると object と表示される )
<script type="text/javascript">
function myJson() {
	this.prop = "JSON";
}

j1 = new myJson();
console.log( typeof j1 );
console.dir( j1 );

j2 = { prop: "JSON" };
console.log( typeof j2 );
console.dir( j2 );
</script>


さらに、new でインスタンスが作成された時に、プロパティとして持っているものは function 内で this で定義する事になっていますが、これは、function の名前に対する prototype に インスタンスをセットしてもいい事になっているので、こんな書き方ができる事になります。
<script type="text/javascript">
function myJson1() {
}
function myJson2() {
	this.prop = "JSON";
}

myJson1.prototype = new myJson2();

j1 = new myJson1();	  // 本体にプロパティは無い
console.dir( j1 );        // プロトタイプに prop があります
console.log( j1.prop );   // JSON と表示される
</script>
さらに、ほんの少し書き方が違いますが、以下のようにしても同じ結果を得る事ができます。

call メソッドは、他の『クラス』の実際に記述されているプロパティに対して効果があるので、継承したいプロパティは function 内に書いておく必要があります。

つまり、結果が同じようでいて、JSON フォーマットの記述である『オブジェクト』ではできない事が function で書くと可能になるという事です。
<script type="text/javascript">
function myJson1() {
	myJson2.call( this );
}
function myJson2() {
	this.prop = "JSON";
}

j1 = new myJson1();	  // 本体にプロパティは無い
console.dir( j1 );        // 本体に prop があります
console.log( j1.prop );   // JSON と表示される
</script>


タグ:javascript JSON
posted by lightbox at 2013-03-22 01:10 | 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 終わり