SVX日記
2004-07-07(Wed) CUIのメディアプレーヤ完成
昨日は、ジュークボックスアプリのユーザインタフェイス側を作ったので(上っ面だけだけど)、今日は実際にメディアファイルを再生させるミニコマンドを作ることにした。ここでワザワザ「mp3ファイル」でなく「メディアファイル」と書いたのは、昨日述べたように、Windows的にはmp3を再生するのもaviを再生するのも同じような手間なので、両方に対応可能なミニコマンドを作る予定だからである。つまり、車載ビデオプレーヤも実現できるように作ってしまうのである。
当初はC++BuilderのTMediaPlayerコンポーネントでも利用することになるんかなーと思っていたのだが「ファイル名を与えるとファイルを再生、終わったら終了」という単純動作を突き詰めていくと、なんのことはない、WindowsAPIのmciSendStringを呼ぶだけで済んでしまうようである。結果、Bolandのフリー版C++コンパイラだけで十分実現可能であった。
mciSendString("おぅっ、そこに'xxxx.mpeg'っつぅけったいなファイルがあんだけどよ、開けるかぃ?", NULL, 0, NULL);
mciSendString("よっしゃ、そのけったいなヤツをとっとと再生してくんな、景気よく画面いっぺぇにな", NULL, 0, NULL);
mciSendString("べらんめぇ、いまどこ再生してんだぃ、あぁん?", answer, 0, NULL);
という調子である(実際には英語だが)。これだと、引数の順番を考えなくていいし、デフォルトのままでいいパラメータなら指定しなければいいので、かなり楽である。通常のWindowsのAPIはウィンドウひとつ開くにも多数のパラメータが必要なので、それもこういう会話インタフェイスにしてもらえたら楽だろうな。
2012-07-07(Sat) tDiary用簡易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__
どうも再訪問のカウントは、基本クッキーを使って回避しているらしいのだが、調べるとブラウザまでクッキーが届いていない。仕方ないので、counterプラグインのみならず、tDiary本体にもデバッグ行を入れまくり、動作をトレースする。すると、なんだかコメントスパム等に対するフィルタ処理の関係で、各プラグインが2回ずつ呼ばれるようになってしまっている。counterプラグインは関数定義だけでなく、ロードされただけで実行される処理を持っており、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) ねずみ花火、コーヒー味
思えば、その存在には、確かに気づいていて、なにげに横目でニラみつつJavaScriptを使っていた気はする。なんとなく「->記法」と「インデント記法」とが気に食わなくて敬遠していたような気もする。しかし、結果としては、ベストなタイミングで乗り継ぐことができた気がしている。結局、ブラウザはJavaScriptコードに対してエラーを吐くわけで、多少なりともJavaScriptが読めなければ、実用できないから。
しかし、こういう存在は「スクリプト言語」ではなく「プリプロセッサ」というのではないか。確かに「this」の洪水に直面すれば、それだけでもどうにかしたくなる気持ちはわかるが、こんなアホなアイデアを実際に実装した挙句、人気が出てしまうという事態が、まさか実際に起き得ることとは思わなかった。
まさに「ブラウザが吐いた、JavaScriptコードに対するエラーを見て、JavaScriptコードを確認してから、CoffeeScriptを直す……」という作業感覚が「PICアセンブラが吐いた、PICニーモニックコードに対するエラーを見て、PICニーモニックコードを確認してから、PIC80ニーモニックコードを直す……」という作業感覚と完璧に同じなのだ。
というところで、先の「ねずみ花火」をCoffeeScriptで書きなおしてみた。ほぼ忠実に書き直したので動作は変わらない。にしても、Rubistのオイラには、極めて受け入れやすい構文だな。なんといっても、コーヒー好きのオイラには、CoffeeScriptという名前だけで、ご飯3杯はイケるほどだし。コードは読みやすく修正もしやすくなり、量も20%削減された。抵抗があった「->記法」と「インデント記法」だが、全然悪くはなかった。個人的には利用頻度の高い「var_x and do」と「x ? y : z」が使えないのは気にはなったが、まぁ、気になるほどではない。
2018-07-07(Sat) マニュアルトランスミッション車の初心者マーク
ガキがいるのにもかかわらず、カミさんがAT限定免許なのにもかかわらず、ツーシーターでMTのロードスターを買うという暴挙を遂げてから半年になる。
そういう引け目もあって、カミさんには「MT車の運転は楽しいから、AT限定解除して運転したら?」なんて、そう本気でもなく勧めていたのだが、本人がそう本気でもない感じのままヤル気を出し始めた。で、広い公園の駐車場で何度か練習につきあいつつ、自動車学校に通い始めたら、アッサリと合格してしまった。
AT限定解除教習に学科はなく、4時間の実技と、卒業検定だけなのだが、既にアラフォーにもかかわらず、なんとストレート合格だ。自分のカミさんながら、ちょっと誇らしい。まぁ、そもそもニブいタイプだったら、最初から、大事なロードスターを運転させようなんて思わないだろうが。
2019-07-07(Sun) とうとう歌ってみたをやってみたった
先日、スクールの発表会で筋肉少女帯の「ゾロ目」を歌い、自分的にまずまずだったこともあり、いわゆる「歌ってみた」をやりたくなってきてしまった。まぁ、聴かれてもそれほどまでには恥ずかしくないレベルにはなってるかな、と。で、朝っぱらからカラオケ屋に収録(?)に向かう。いまは、カラオケ屋から直接に動画投稿できる時代なのだ……などといいつつ、実はこれまで既に2回ほど収録の練習をしており、今回は3回目なのだが。
いつもはPCMレコーダで録音しているのだが、カラオケ屋で収録したものを聴くと感じが違う。PCMレコーダだと、スピーカから出た声と生声がミックスされるが、収録の場合はマイクに入った声だけになるからだろう。前々回、いつもどおりにエコーをゼロにしたら、さすがにちょっと寂しい感じになってしまったので、前回エコーを5にしてみた。PCMレコーダだと、実は部屋の壁によるエコー効果が得られるので、ゼロでも十分に上手く聴こえてしまうのだ。
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