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|07|

2004-12-06(Mon) テレビ、壊れる

  近頃、我が家のテレビの調子が悪い。92年春に購入した製品であり、もうすぐ丸13年になるのだから、まぁ調子が悪いほうが自然といえば返す言葉もない。症状は「しばしば左チャンネル、まれに右チャンネルから音が出ない」というもの。当然ながら「まれにまったく音が出なくなる」という事実を含む。非常に微妙な壊れ方だ。一方で映像に関してはゼンゼン問題なし。むしろこのテレビのメーカーがソニーであることを考えると、13年近く経ってこの壊れ方で収まっているという状況は「ソニータイマーが壊れている」とも表現できよう。ラッキーだ。

  画像の説明

  私はエンジニヤであるから、故障の症状から自然といろいろ勘ぐってしまう。音を大きくすると症状が出にくくなる、ということからすると、おそらく音声回路のハンダ浮きであると思われる。聞いたところによると、ソニーの製品は音質や画質などには非常にコダわっている反面、その作りは非常に安っぽいことが多いそうだ。早いハナシ「設計は素晴らしいが耐久性がない」ということ。おそらく、音声回路のハンダ浮き以外にもあちこち潜在的な病気を抱えているであろう。

  で、どうするか。なんでも近年、文明開化が進んだせいで今では21型のテレビなんぞは2万以下が相場らしい。一方で修理の相場は1万円。これではヘタに修理などせず、新しいものを買ったほうがよいではないか。まさに、ザンギリ頭を叩くしかない状況なのである。映像が全く問題ないのに買い換えるというのも、もったいないハナシだ。それしきのことで、あんなデカいものを右から左へ動かす元気はないのである。

  一方で、チマタでは薄型テレビという軽薄なテレビが高いくせにバカ売れと聞く。2万で買えるテレビに10万以上も出すのはどうなのか。なに? 地上はデジタル? 地上がデジタルになるとな? ちがう? そのうち地上波デジタルという面妖な電波に置き換わるため、今テレビを買い換えるのはムダとな? ももも。

  そうなのである。こんな過渡期の状況で買い換えるのはイヤなのである。なんとか自分で修理できないものか。そう考えてしまうのは当然なのである。しかし修理をするためには、ひとつ障害がある。テレビを分解すると死ぬ場合があるらしいのである。テレビの主役であるブラウン管は非常に高い電圧を保持するため、うかつにテレビを分解して、ヘタなところに触れると、よくて「手に穴」悪くて「失明」最悪「死亡」らしいのである。うぅ、自分のシップに乗っている乗客の安全のためにハイジャック犯と刺し違えて死ぬのはともかく、自宅でテレビをイジっていて死ぬのはイヤだ。いくらなんでもそれはハズかしすぎる。末代までのハジである。

  つーわけで「分解せずに修理」を選択する私であった。どうやって「分解せずに修理する」のか。それは、心霊手術でもなく、最近ハヤりの内視鏡手術でもなく、いわばバイパス手術なのである。ヘッドフォンジャックから出力を取って外部に取り付けたアンプを通してスピーカーに出力してしまうのである。なんといっても、このバイパス手術の利点は「使い勝手に変化がない」ということに尽きる。現状のリモコンでフツーに音量の上下ができてしまうのである。がははは。

  なんだかんだで、新たな工作ネタができてウキウキの私であった。近々、部品を揃えて工作にかかろう。うっほっほー。


2023-12-06(Wed) WebAssemblyでトラディショナルな回転技術を再現

  というわけで、9月末頃に「やろう」と思い立ってから2ヶ月チョイもかかってしまった。まぁ、だらだらと取り組んでいたせいもあるが、初めての言語、かつ、相手が未知の仕様を含むアセンブラでは効率が上がらなかったのも無理はないかな。

  そう「WebAssemblyのひとつだけの使い道」とは、過去に実装に成功した「回転、拡大、縮小機能」をWebAssemblyを使って高速化することなのであった。WebAssemblyは計算やメモリ操作しかできず、複雑な関数計算も苦手なのだが、1980年台のトラディショナルな回転、拡大、縮小技術は、加算処理を山ほど行うだけなので、用途としてピッタリなのだ。

  というわけで、早速だが以下がその成果である。

  画像の説明

  wasmのコードは試行錯誤で書くには記述性が低すぎるし、可読性も低すぎる。ので、考え方の図と併せて、Rubyで作ったプロトタイプのコードも載せておく。処理はほぼ同じ。アセンブラ頭で書いたRubyコードw。これも相当に考えないと何をやっているのかわからんと思うが。

  画像の説明

rxy = vxys[v + 128]                             # サンプリングxy開始点計算用
hxy = vxys[v]                                   # 水平方向サンプリングxy間隔
vxy = vxys[v + 64]                              # 垂直方向サンプリングxy間隔
dest_adr = w * h                                # 描画先アドレス
 
dx = w >> 1; dy = h >> 1
_vx8 = dx * rxy[0] - dy * rxy[1] + (dx << 8)    # サンプリングxy開始点
_vy8 = dx * rxy[1] + dy * rxy[0] + (dy << 8)
 
begin
    x = w                                       # 水平方向カウンタを初期化
    _hx8 = (_vx8 += vxy[0])                     # サンプリング位置を垂直方向に進め、水平方向の初期位置としてセット
    _hy8 = (_vy8 += vxy[1])
    begin
        dest_adr -= 1
        _hx8 += hxy[0]; _hy8 += hxy[1]          # サンプリング位置を水平方向に進める
 
        (_hx8 < 0 or _hx8 >= (w << 8)) and next # サンプリング範囲外チェック
        (_hy8 < 0 or _hy8 >= (h << 8)) and next
 
        src_adr = (_hy8 >> 8) * w + (_hx8 >> 8) # サンプリングアドレス計算
        color = LegacyGraphics.point(src_adr & msk64, src_adr >> x64)
        win.pset(dest_adr & msk64, dest_adr >> x64, color)
    end until((x -= 1) == 0)
end until(dest_adr == 0)

  CPUが8bitの時代、大半はBASIC、処理速度が必要な部分のみ「CALL」や「USR」で機械語サブルーチンを呼び出す、というプログラミングスタイルがあったが、まさにそれと同じようなスタイルになっている。

  回転したパターンはキャッシュされるので、画面ではふたつのテトランを回しているものの、計算しているのはひとつ分だし、1回転した後はすべてキャッシュからの表示となる(呼び出し側からは透過的にそうなる)。今回のパターンは四隅までミッシリの128x128サイズなので端が欠けるが、透過処理もバッチリできているので、実用するなら256x256で回すことになるだろう(256x256なら181x181(256/√2)の大きさまで欠けずに回せることになる)。

  今回の表示は以前に作ったスケルティウスのエンジンを流用しているので、逆に言えば、既にシューティングゲームにそのまま利用できる形になっている。また別途、複数のコードをリンクする仕組みを自作しており、APIを揃えているので、リンクする物件を'rotnscl_wasm.bean'から'rotnscl.bean'に変更するだけで、WebAssemblyを使わないバージョンに戻すことができる。

  実は、JavaScriptのコンソールログを開くと、フレームレートや負荷率が出力されるようになっているのだが、WebAssemblyを使わないバージョンだと、負荷率が150%前後となり、60であるべきフレームレートが30前後に落ちてしまう。

now: ,1701848213042,frames: ,20, load: ,174,%
now: ,1701848215409,frames: ,28, load: ,180,%
now: ,1701848218383,frames: ,15, load: ,150,%
now: ,1701848220384,frames: ,32, load: ,144,%
now: ,1701848221614,frames: ,53, load: ,6,% ※キャッシュが完了
now: ,1701848222597,frames: ,62, load: ,0,%

  WebAssemblyのバージョンなら、負荷率は常に6%以下で、60のフレームレートは余裕で確保できる。

  ちなみに、今回のwasmモジュールは最大512x512のパターンまで回せるのだが、WebAssemblyを使わないバージョンだと、負荷率は2500%を越え、フレームレートは2前後と紙芝居以下のレベルになってしまう一方で、WebAssemblyバージョンなら、負荷率は概ね20%以下で、まだまだ鼻歌交じりとなる。128x128と512x512では面積比が16倍なので、150%が2400%になることは理屈に合う。WebAssemblyなら512x512でも20%前後ということは、軽く100倍を越える速度が得られているということになる。

  そりゃ、直接にニーモニックで書いて、しかも割とカリカリに最適化してあるんだから、そうこなくっちゃってとこだ。100倍というのは苦労に見合う結果に思える。先には数倍という話も聞いたが、あれはRustなどからコンパイルした場合の話なのだろうな。ちなみに「割とカリカリに最適化してある」ものの、今回はベクトル演算命令は使っていない。それを使えばさらに速度は上がるはずだ。まぁ、上がっても20%以下だろうが。

  というわけで、今回「回転」は実装したものの「拡大、縮小」は実装していない。WebAssemblyについてはすっかり習得できたので、やろうと思えばそう時間はかからないだろうが、一旦は目標達成としてまた別のことでも始めようかな。

  あー、スッキリした!


2024-12-06(Fri) 自分を見詰め、字を詰める

  ここしばらく、Factorioに夢中なのだが、仕事の方でも実にエキサイティングである。一部の勢力からの自分の評価が極めて低いことを知ってしまったのである。

  しかし、一時的には腹が立ったものの、しばらくして、その勢力からの評価が低いことは、むしろ望ましいことなのではないかということに気づいてしまった。その勢力の考え方は、決して自分の信念と相容れないからだ。一方で、自分が好きな人、尊敬する人たちからは変わらず手放しでの高評価を受け続けているのだし、まぁ、それでいいかと。

  自分としては、マズローの4段階目「承認の欲求」で自身について納得しつつ、5段階目「自己実現の欲求」を半ば達成しつつあるような心境だ。乱れた心も整いつつあるのを感じる。

  考えてみれば、生殺与奪の権利を持っているのはこっちなのだ。金もあるし。それで自ら好きな仕事だけしてればいい立場になったのだから、それが一部からの低評価との引き換えだとしても別にいいやと。自分の信念に背くような仕事の仕方すんの、ヤだしね。それさえ無理なら、袂を分かつさ。

  なんだかキャプテンハーロックの気持ちってこうだったのかもと思えてきた。どんな大勢力が相手だろうと、俺の旗の下に、俺は自由に生きる。それがアルカディア。それが男。それでいい。

  そんなこんなで、片や落胆しつつ、いつも以上に楽しんで仕事をしているのだが、その中で「字詰め」処理を行う必要が生じてしまった。Rubyで。長い一文を、例えば18文字、で折り返す処理である。これが意外と難しい。え!? そんなん、1行じゃん?

print(target.gsub(/(.{18})/, "\\1\n"))

  ……って思うかもしれんが、それは甘い。

 その話をカミさんにしたら、そんなの
借りときゃいいのに、という反応。いや
、そりゃそうだけど、2km弱を乗った
ところで何になるってんだ、などといい
つつ、なぜかRotary-EVの素晴
らしさについて語りだしてしまうオレw
。ほとんど乗らないワシらにはオーバー
 :

  まず、英文字と漢字とは幅が違うので、同じ1文字と数えると右端が揃わない。それよりなにより「禁則処理」がなされない。句点(。)や読点(、)が行頭に位置してしまっている。そんなの気にしないという向きもあろうが、自分はイヤだ。そんな処理もできないのかと思われるのは恥ずかしい。

  しかし、これが意外と難しい。組版するわけではないので、次行への追い出しのみの実装でよいのだが、先頭から処理していくと、遡るような処理が必要になるのだ。例えば、

「その時、彼は『こんにちは(笑)。』と書き込んだの。」

  を、18文字で詰めると、

「その時、彼は『こんにちは(笑)。』
と書き込んだの。」

  だが、17文字で詰めると、

「その時、彼は『こんにちは
(笑)。』と書き込んだの。」

  1文字でなく、5文字も減らす必要が生じるのだ。

  こんな典型的な処理、どこかで誰かが書いているだろう、と思って検索してみたが、良いものが見つからない。思い返せば、彼の「電脳倶楽部」の創刊号で祝一平編集長自らがPDSとしてCで書いて公開した「wrap.x」がまさにそれなのだが、コードを入手する手段がない。当時はまるで興味をそそられなかったプログラムだったが、ディスクマガジン用のリーダを書こうと思えば、そら一番に必要になるコードだよなぁ。いまさらに深く納得。

  それなりにうんうん唸って、何度か書き直して、そこそこ納得できるコードが書けたが、丸一日かかってしまった。正規表現とかを駆使すればもっとシンプルかもしれないが、そう得意ではないし、ハナモゲるのがイヤなので、使っていない。

class String
 
    def char_wide
        self.getbyte(0) < 0xE0 ? 1 : 2                          # 文字の表示幅を返す
    end
 
    def each_wline(w_max)
        loop {
            c_max = 0; w = 0; self.each_char {|c|               # 仮分割位置を算出する
                w + c.char_wide > w_max and break
                c_max += 1; w += c.char_wide
                c == "\n" and break
            }
            if(self.size > c_max)                               # 1 行分を適切に切り出す
                c = c_max.times {|cr|
                    # 行末禁則文字
                    '([{「『([{'\
                        .include?(self[c_max - cr - 1, 1]) and next     # 仮分割位置の直前(行末)の文字
                    # 行頭禁則文字
                    '),.]}、。」』っッー)]}'\
                        .include?(self[c_max - cr, 1]) and next         # 仮分割位置の直後(行頭)の文字
                    break(c_max - cr)
                }
                yield(self.slice!(0, c))
            else                                                # 残りを返す
                yield(self)
                break
            end
        }
    end
end
 
target.each_wline(38) {|l|
    puts("\t|%s|" % l)
}

  結果はこう。

	| その話をカミさんにしたら、そんなの借|
	|りときゃいいのに、という反応。いや、そ|
	|りゃそうだけど、2km弱を乗ったところで|
	|何になるってんだ、などといいつつ、なぜ|
	|かRotary-EVの素晴らしさについて語りだ|
	|してしまうオレw。ほとんど乗らないワシ|
	|らにはオーバースペックすぎるんだって、|
	|なんていいながら、サイトで見積もりまで|
	|してしまう。天井が黒くって、ロードス|
	|ターとお揃いじゃん。並べたらいい感じじ|
	|ゃない? ……って、そらそうだけど、ホ|
	|ラ、500万だってよ。500万ってさ、1年を|
	|働いてようやく余るかっていう金額だよ。|
	|MX-30のために1年働くって思ったら……い|
	|や、そういうのも意外と悪くないんじゃな|
	|いかw。オレ、チョロいなぁ……。
|
	| 前にも言ったが、代車を貸すほど効率的|
	|なプロモーションはないよなぁ。あー、|
	|やっぱり借りて、明日の仕事はブッチして|
	|しまうべきだったなぁ。人生で最高レベル|
	|の失敗をしたかもしれん……。
|

  関数名の「each_wline」の「w」は「wrap.x」へのリスペクトである。

  書いていて気づいたのだが、既に「半角、全角」という世ではないこともあって、ネット上にコードが見つからないのかもしれない。プロポーショナルな環境の場合、自分で詰めるべきではなく、CSSなどを使うべきなのだろう。

  ついでに、コードを書いたあと、思いついてChatGPTにコードを書かせてみたが、なぜか行頭禁則しか扱ってないし、動かしても何の処理もされなかった。やはり元となるようなサンプルコードがある場合限定なのかもしれん。

  あ、ちな、上記のコードには、一部の禁則文字しか定義していないし、分離禁止文字も実装していない。あしからず。