SVX日記

2004|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|04|05|06|07|08|09|10|11|12|
2025|01|02|03|04|05|06|

2004-07-07(Wed) CUIのメディアプレーヤ完成

  昨日は、ジュークボックスアプリのユーザインタフェイス側を作ったので(上っ面だけだけど)、今日は実際にメディアファイルを再生させるミニコマンドを作ることにした。ここでワザワザ「mp3ファイル」でなく「メディアファイル」と書いたのは、昨日述べたように、Windows的にはmp3を再生するのもaviを再生するのも同じような手間なので、両方に対応可能なミニコマンドを作る予定だからである。つまり、車載ビデオプレーヤも実現できるように作ってしまうのである。

  当初はC++BuilderのTMediaPlayerコンポーネントでも利用することになるんかなーと思っていたのだが「ファイル名を与えるとファイルを再生、終わったら終了」という単純動作を突き詰めていくと、なんのことはない、WindowsAPIのmciSendStringを呼ぶだけで済んでしまうようである。結果、Bolandのフリー版C++コンパイラだけで十分実現可能であった。

  余談だが、このmciSendStringというAPI、文字列を渡してまるで会話するかのようなインタフェイスのAPIである。

mciSendString("おぅっ、そこに'xxxx.mpeg'っつぅけったいなファイルがあんだけどよ、開けるかぃ?", NULL, 0, NULL);
mciSendString("よっしゃ、そのけったいなヤツをとっとと再生してくんな、景気よく画面いっぺぇにな", NULL, 0, NULL);
mciSendString("べらんめぇ、いまどこ再生してんだぃ、あぁん?", answer, 0, NULL);

という調子である(実際には英語だが)。これだと、引数の順番を考えなくていいし、デフォルトのままでいいパラメータなら指定しなければいいので、かなり楽である。通常のWindowsのAPIはウィンドウひとつ開くにも多数のパラメータが必要なので、それもこういう会話インタフェイスにしてもらえたら楽だろうな。

  と、脱線はコレくらいにして、一応ソースバイナリを置いておこう。ソースは100行弱と短く、しかもほとんどがC++Builderのヘルプからコピペしただけの定型文、キモは例のmciSendStringの3行程度だけである。使い方はコマンドラインにファイル名(1個だけ)を指定するだけ。

  というわけで、今日はビジュアルがなくてスマンが、ココまでである。



2012-07-07(Sat) tDiary用簡易CAPTCHA

  よく考えたら、こういうのをCAPTCHAといっていいのかわからんが、まぁ、コメントスパムを拒否するのに十分な効果はあるのでよしとする。

  まずは、フィルタ側。

 ・/usr/local/share/tdiary/misc/filter/tcaptcha.rb
 
 # -*- coding: utf-8; -*-
 
 require 'digest/md5'
 
 module TDiary
     module Filter
         class TcaptchaFilter < Filter
             def comment_filter( diary, comment )
 
                 common_key = 'abc'              # 任意の文字列(プラグイン側と合わせる)
 
                 rw = @cgi.params['tcaptcha'].dup
                 dm = Digest::MD5.hexdigest((rw << common_key).sort.join(':'))
 
                 $stdout.sync = 1
 
                 if(result = (@cgi.params['tcaptcha_digest'][0] == dm))
 # =begin            # 強引なメッセージの出し方をするので、クッキーを食わせられなくなる
                     print "Content-Type: text/html\n\n"
                     print "<HTML><HEAD><META http-equiv='Content-Type' content='text/html; charset=UTF-8'></HEAD><BODY>\n"
                     print "<H1>Thank you for your comment</H1>\n"
                     print "<P>コメントありがとうございました。</P>\n"
                     sleep 5
 # =end
                 else
                     print "Status: 406 Not Acceptable\n"
                     print "Content-Type: text/html\n\n"
                     print "<HTML><HEAD><META http-equiv='Content-Type' content='text/html; charset=UTF-8'></HEAD><BODY>\n"
                     print "<H1>Tiny CAPTCHA challenge failed</H1>\n"
                     print "<P>Tiny CAPTCHA 認証に失敗しました。</P>\n"
                     print "<FONT color='white'><!--\n"  # 以降を強引に不可視にしようとする努力
                     sleep 5
                     raise 'Tiny CAPTCHA challenge failed'
                 end
 
                 comment.show = false            # 要査閲にするなら
 
                 result
             end
         end
     end
 end
 
 __END__

  つぎが、プラグイン側。

 ・/usr/local/share/tdiary/misc/plugin/tcaptcha.rb
 
 # -*- coding: utf-8; -*-
 
 require 'digest/md5'
 
 def comment_body_label
 
     common_key = 'abc'                          # 任意の文字列(フィルタ側と合わせる)
 
     q = @options['tcaptcha.question'] ||= '質問: 鳥の鳴き声'
     r = @options['tcaptcha.rights'] ||= ['チュンチュン', 'コケコッコ', 'ピヨピヨ', 'カァー', 'ホーホケキョ', 'ガァガァ']
     w = @options['tcaptcha.wrongs'] ||= ['ワン', 'ニャー', 'ブゥブゥ', 'ヒヒーン', 'パォーン', 'ガォー']
 
     rs = r.shuffle
     rw = ([rs.shift] + (rs + w).shuffle[0, 5]).shuffle
     dm = Digest::MD5.hexdigest(((r & rw) << common_key).sort.join(':'))
 
     h = ''; id = 0; rw.each {|i|
         h << "\t\t\t\t\t<INPUT type='checkbox' id='tc%d' name='tcaptcha' value='%s'><LABEL for='tc%d'>%s</LABEL>\n" % [id += 1, i, id, i]
     }
     h << "\t\t\t\t\t<INPUT type='hidden' name='tcaptcha_digest' value='%s'>\n" % dm
 
     tcaptcha = <<-FORM
 #{q}
 #{h}                </div>
                 <div class="textarea">
                     コメント
     FORM
     tcaptcha.chomp
 end
 
 def comment_body_label_short
     '本文'                                      # 未対応
 end
 
 __END__

  よく見れば「なんと強引な」という実装方法だが、アドイン形式では、私の要望を満たす実装方法は、これしかなさそうだ、という結論になった。

  ついでに、もひとつ。tDiaryをバージョン3に上げたからか、counterプラグインがおかしくなり、カウンタが景気よく回りすぎる症状が出てしまう。再訪問のカウント回避機能が効いていない。

  どうも再訪問のカウントは、基本クッキーを使って回避しているらしいのだが、調べるとブラウザまでクッキーが届いていない。仕方ないので、counterプラグインのみならず、tDiary本体にもデバッグ行を入れまくり、動作をトレースする。すると、なんだかコメントスパム等に対するフィルタ処理の関係で、各プラグインが2回ずつ呼ばれるようになってしまっている。counterプラグインは関数定義だけでなく、ロードされただけで実行される処理を持っており、2回連続で呼ばれると副作用でクッキーを吐かなくなってしまうようだ。

  いろいろ対処の仕方を考えたものの、結局「グローバル変数」を使って、1回目はスルー、2回目以降は動作するという「ハブ」みたいなパッチでごまかすことにした。


www.itline.jp /usr/local/share/tdiary/misc/plugin # diff -U 4 counter.rb.org counter.rb
--- counter.rb.org	2012-04-29 20:27:26.000000000 +0900
+++ counter.rb	2012-07-08 02:44:54.290040583 +0900
@@ -476,16 +503,19 @@
 	def kiriban_today?
 		TDiaryCounter.kiriban_today?
 	end
 
+if($q_meet_again_q)
 	tdiary_counter_cookie = TDiaryCounter.run(@cache_path, @cgi, @options)
 	if tdiary_counter_cookie
 		if defined?(add_cookie)
 			add_cookie(tdiary_counter_cookie)
 		else
 			@cookie = tdiary_counter_cookie if tdiary_counter_cookie
 		end
 	end
+end
+$q_meet_again_q = true
 
 	def kiriban
 		if kiriban?
 			msg = @options["counter.kiriban_msg"] ? @options["counter.kiriban_msg"] : ""

  いずれも、裏技的なコードで、コミュニティに還元できそうなコードではない。たはっ。


2014-07-07(Mon) ねずみ花火、コーヒー味

  今度はCoffeeScriptに手を出してみた。

  思えば、その存在には、確かに気づいていて、なにげに横目でニラみつつJavaScriptを使っていた気はする。なんとなく「->記法」と「インデント記法」とが気に食わなくて敬遠していたような気もする。しかし、結果としては、ベストなタイミングで乗り継ぐことができた気がしている。結局、ブラウザはJavaScriptコードに対してエラーを吐くわけで、多少なりともJavaScriptが読めなければ、実用できないから。

  しかし、こういう存在は「スクリプト言語」ではなく「プリプロセッサ」というのではないか。確かに「this」の洪水に直面すれば、それだけでもどうにかしたくなる気持ちはわかるが、こんなアホなアイデアを実際に実装した挙句、人気が出てしまうという事態が、まさか実際に起き得ることとは思わなかった。

  というのも、既に自分が似たようなことをやっていて、我ながらアホだと自覚しつつ、それでも気分良く実用に供していたので。

  まさに「ブラウザが吐いた、JavaScriptコードに対するエラーを見て、JavaScriptコードを確認してから、CoffeeScriptを直す……」という作業感覚が「PICアセンブラが吐いた、PICニーモニックコードに対するエラーを見て、PICニーモニックコードを確認してから、PIC80ニーモニックコードを直す……」という作業感覚と完璧に同じなのだ。

  CoffeeScriptがプログラミング言語として認められているとなると、私も言語設計者の仲間入りなのか!? ……ま、んなこた、どうでもいいが。

  画像の説明

  というところで、先の「ねずみ花火」をCoffeeScriptで書きなおしてみた。ほぼ忠実に書き直したので動作は変わらない。にしても、Rubistのオイラには、極めて受け入れやすい構文だな。なんといっても、コーヒー好きのオイラには、CoffeeScriptという名前だけで、ご飯3杯はイケるほどだし。コードは読みやすく修正もしやすくなり、量も20%削減された。抵抗があった「->記法」と「インデント記法」だが、全然悪くはなかった。個人的には利用頻度の高い「var_x and do」と「x ? y : z」が使えないのは気にはなったが、まぁ、気になるほどではない。

  ……が、まてよ。ここは、上記の記法を可能にするScript仕様を、また新たに作り出すべきなんじゃないだろうか。プリプリプロセッサ……じゃなくてSugerScriptとしよう。いや、そんな考えは甘すぎるというならば、MilkScriptでまろやかにすればいい。いやいや、そこはロシア語で考えるべきだというならJamScriptでゴチャつかせるのもいいが、もう、そんなのシラフでやってられないのならIrishScriptで酔っ払っちまうのもいい。

  ……CoffeeScriptは無限の可能性を示してくれたといえよう。


2018-07-07(Sat) マニュアルトランスミッション車の初心者マーク

  ガキがいるのにもかかわらず、カミさんがAT限定免許なのにもかかわらず、ツーシーターでMTのロードスターを買うという暴挙を遂げてから半年になる。

  そういう引け目もあって、カミさんには「MT車の運転は楽しいから、AT限定解除して運転したら?」なんて、そう本気でもなく勧めていたのだが、本人がそう本気でもない感じのままヤル気を出し始めた。で、広い公園の駐車場で何度か練習につきあいつつ、自動車学校に通い始めたら、アッサリと合格してしまった。

  AT限定解除教習に学科はなく、4時間の実技と、卒業検定だけなのだが、既にアラフォーにもかかわらず、なんとストレート合格だ。自分のカミさんながら、ちょっと誇らしい。まぁ、そもそもニブいタイプだったら、最初から、大事なロードスターを運転させようなんて思わないだろうが。

  とはいえ、本人は自信満々というワケでもなく、少々不安そう。というわけで、しばらくの間だけでも、初心者マークを付けることにした。とはいえ、普通の初心者マークでなく、MT車の運転が初心者であることを示すもの。

  画像の説明

  チョイチョイと黒いマジックで塗っただけだが、どうだろうか。我ながら、いいデザインだとは思うものの、肝心のカミさんには即座に却下されそうではあるが……。


2019-07-07(Sun) とうとう歌ってみたをやってみたった

  先日、スクールの発表会で筋肉少女帯の「ゾロ目」を歌い、自分的にまずまずだったこともあり、いわゆる「歌ってみた」をやりたくなってきてしまった。まぁ、聴かれてもそれほどまでには恥ずかしくないレベルにはなってるかな、と。で、朝っぱらからカラオケ屋に収録(?)に向かう。いまは、カラオケ屋から直接に動画投稿できる時代なのだ……などといいつつ、実はこれまで既に2回ほど収録の練習をしており、今回は3回目なのだが。

  歌い始めてしばらくネットの調子が悪かったようだが、1時間半で14曲歌い、5曲を撮影した。本命は「ゾロ目」。それ以外の撮影は練習のつもり。

  いつもはPCMレコーダで録音しているのだが、カラオケ屋で収録したものを聴くと感じが違う。PCMレコーダだと、スピーカから出た声と生声がミックスされるが、収録の場合はマイクに入った声だけになるからだろう。前々回、いつもどおりにエコーをゼロにしたら、さすがにちょっと寂しい感じになってしまったので、前回エコーを5にしてみた。PCMレコーダだと、実は部屋の壁によるエコー効果が得られるので、ゼロでも十分に上手く聴こえてしまうのだ。

  で、家に帰って、自分で聴いてみた。思った以上にエコーが強い。同じ5でも、部屋によって効果に差があるようだ。最近、歌のために耳が鍛えられすぎてしまって、ガチで細かい音の違いのわかる男になってしまった気がする。

  画像の説明

  で、結果「ゾロ目」のデキは……平常比9割といったところ。とはいえ、100点を目指しているといつまでも公開できないので、バージョン1としてアップしてしまうことにした。

  画像の説明

  で、まだ練習中ではあるが、意外とシャウトが決まったので、進捗率7割くらいではあるが、勢いで「ワルキューレは裏切らない」も、バージョン1としてアップすることにした。

  しかし、こんな形でネットに顔出しすることになろうとは。1ヶ月前までは金髪ですらなかったのに。歌は怖いな、さすがは兵器だな。

  画像の説明

  調子に乗ってネタも作ってみた。主にドライブしながらシャウトの練習していて思いついたもの。いや、別にネタとしてPCの中に作っただけで、実際に印刷してロードスターに貼ろうとは思ってないけどね……貼るとこないし。


2022-07-07(Thu) 格納庫っぽいのを作るため、ヤクの毛を刈りまくる

  と、いうわけで、星空ができたので、次は格納庫を作ってみることにする。といっても、ロクにドット絵を描いたことがないオイラだ。既存の作品を研究しつつではあるが、描き続ければすぐに上達するだろうから、描き始めのは習作としてどうせ捨てることになるだろうと思いつつ、チマチマと描き始める。

  と、始めてすぐに色の選択が難しいことに気づいた。GIMPを使っているのだが、色が多すぎて選ぶのが容易ではない。そもそも、カラーピッカーが使いづらい。逆に、ある程度、使える色を制限したほうが選びやすい。

  で、いろいろ調べたところ、どうもHLS色空間が使いやすそうだ。「色の系統→色のくすみ度→陰影」の順で選べる。欲しかったのはまさにコレ感。ウェブ上に色サンプルを見つけたのだが、冗長な配置やカラーコードの逆引きができないことにイライラ……で、いっそのこと自作。

  画像の説明

 require './TrueLegacyGraphicsCairo'
 
 def hsl2rgb(h, s, l)
     if(l < 50)
         max = l * 100 +        l  * s
         min = l * 100 -        l  * s
     else
         max = l * 100 + (100 - l) * s
         min = l * 100 - (100 - l) * s
     end
     if(   h <  60)                                                  #   0: 赤
         r = max; g =        h  * (max - min) / 60 + min; b = min    #       緑が増える
     elsif(h < 120)                                                  #  60: 黄
         g = max; r = (120 - h) * (max - min) / 60 + min; b = min    #       赤が減る
     elsif(h < 180)                                                  # 120: 緑
         g = max; b = (h - 120) * (max - min) / 60 + min; r = min    #       青が増える
     elsif(h < 240)                                                  # 180: シアン
         b = max; g = (240 - h) * (max - min) / 60 + min; r = min    #       緑が減る
     elsif(h < 300)                                                  # 240: 青
         b = max; r = (h - 240) * (max - min) / 60 + min; g = min    #       赤が増える
     else                                                            # 300: マゼンタ
         r = max; b = (360 - h) * (max - min) / 60 + min; g = min    #       青が減る
     end                                                             # 360: 赤
     [r / 10000.0, g / 10000.0, b / 10000.0]
 end
 
 win = LegacyGraphics.new(nil, nil, nil, nil, 1536, 2048, 16, 8, 0, { :file => 'hsl', :type => 'png' })  # png/pdf/svg/ps
 
 ps = { :font_family => '../default.bmf', :tx => 1, :ty => 1, :layout_width => 120, :layout_alignment => 'left' }
 12.times {|h0|
     xx = h0 % 4 * 512 + 64; yy = h0 / 4 * 512
     6.times {|s0|
         11.times {|l0|
             h = h0 * 30         # H: 色相 0-360
             s = 100 - s0 * 20   # S: 彩度 0-100
             l = 100 - l0 * 10   # L: 輝度 0-100
             win.fill(x = xx + (s0 << 6), y = yy + (l0 * 50), x + 60, y + 46, c = hsl2rgb(h, s, l))
             win.text(x + 1, y + 1, '%02X%02X%02X' % [c[0] * 255, c[1] * 255, c[2] * 255], l0 < 6 ? 0 : 7, ps)
         }
     }
 }
 
 win.refresh
 win.close

  と、すぐにこういう「ヤクの毛刈り」をやりだす性格だから、本来の目的が進まないんだよな、と思いつつも、そういうのをクリアにしないと気が済まないし、クリアにするのが楽しいんだから仕方ない。

  で、ある程度、背景のパターンを描いたところで、今度はステージのエディットが面倒になってきた。16進数を並べるだけなんだが、それがシコタマ面倒くさい。あー、いかんて、いかんて、次はステージエディタ作り始めちま……で、結局は自作。簡易なものだけれど。

  画像の説明

  つうわけでとりあえず、それっぽい背景ができた。自分の想定よりも、だいぶそれっぽくなった気がする。よっしゃよっしゃ。

  画像の説明

  さて、次は、なんの毛を刈……じゃなくて、何を進めようかしらん。


2023-07-07(Fri) 東北ドライブ7日目

  画像の説明

  画像の説明

  画像の説明

  画像の説明

  画像の説明

  画像の説明 画像の説明

  画像の説明 画像の説明

  画像の説明

  画像の説明

  本日の走行距離は343.2km。累計距離は1901.4km。