2011-12-16

Tortoise SVN 1.7.2 の問題のメモ

Tortoise SVN 1.7.2 でチェックアウトするとき、「不適切な引数に遭遇しました」と出ることがある。SSLの証明書に因るバグらしい。

  1. 1.7.2をアンインストール
  2. 1.7.1をインストール
  3. 開きたかったリポジトリをチェックアウトして証明書を永久に受け入れる
  4. 1.7.2をインストールしなおす

以上の手順で解決するそう。

面倒なら1.7.1に戻したままでもいいだろう。もしくは戻さずにHTTPを使うか。

2011-11-17

dumb raytracer-ish

ダウンロード

ray.zip

gist:1369770

去年できそこないのレイトレーサを作った。オブジェクトの配置がハードコードで、反射を一度しかしていないので3Dエンジンとしては使えない。シェーダの勉強をしていたと思ったらいつのまにか出来上がっていたものだが、これ以上ほうっておくと忘れそうなのでまとめておく。

操作

操作 | 効果
-- | --
左ドラッグ | ライトを操作
マウスホイール | カメラ前進・後退
右ドラッグ | カメラ見回し
右クリック | モード切り替え (鏡面とか)
左クリックしながらマウスホイール | ズーム (視野角の調整)
右クリックしながらマウスホイール | スレッドの増減 (増やすと早くなるかも)
マウスホイールをクリック | リセット
Hキー | ヘルプを隠す

2011-07-15

vvvvでSkypeの声にエフェクトをかける

ヘリウム声で通話がしたかったので調べた。ノイズ対策にも使おうとしたがあまりうまくいかなかった。

ループバックするサウンドデバイスとVSTホストがあれば何でもいいが、TiVSoundとvvvvを使った例をメモする。

手順

  1. SkypeのデバイスにTiVSoundを指定する
  2. vvvvでAudioInとAudioOutだけ置いたパッチを動かす
  3. 間に好きなVSTエフェクトを挟む

エフェクトひとつ

設定はこうする。

加工する対象SkypeのスピーカSkypeのマイクvvvvのAudioInvvvvのAudioOut
自分の声通常のスピーカTiVSound通常のマイクTiVSound
通話相手の声TiVSound通常通りTiVSound通常のスピーカ

vvvvの使い方

vvvvはノードを配線して色々なことができる多目的ツール。配線したものをパッチと呼ぶ。パッチをファイルに保存する時の拡張子は.v4p (vvvv patchの略)。保存や開くのは中クリックで行う。

どこか空白をダブルクリックするとノードの一覧が出る。名前を打ちこんで絞り込むことができる。選択するとノードが出現する。

ノードの追加

ノードの上辺には入力端子が並んでいる。ノードの下辺には出力端子が並んでいる。端子の数はノードによって異なる。

端子を左クリックすると配線が伸び、右クリックすると値を変える事ができる。例えばAudioInとAudioOutは右上の端子を右クリックすると接続先のデバイスを選択できる。また、AudioOut の上の左から2番目の端子は右クリックしながら上下に動かすとボリュームを調整することができる。

AudioOutのボリューム端子

端子が通る信号には種類があり、種類の合わない端子はつなぐことができない。例えば IOBox (Value Advanced) は数値を入出力するが、これをAudioOutにつないで音を鳴らす事はできない。AudioOutはAudioという種類の端子を持つからだ。

端子の種類

AudioInはAudioを出力するので、これをAudioOutにそのまま繋げばとりあえずアンプができあがる。

identity

vvvvとVSTエフェクト

vvvvは起動時にvstというフォルダ以下にあるDLLをすべて読み込む。

VSTエフェクトは、vvvvではAudio入力とAudio出力が一つずつあるノードになる。ということで、AudioInとAudioOutの間に配線すれば音を変えることが出来る。demuxはSwitcherというノードでできるようだが、muxやミキサーをするノードが見当たらない。これは気が向いたら探そうと思う。

エフェクトひとつ

VST自体のウィンドウはノードを右クリックすると出したり隠したりできる。

注意

エフェクト内部のパラメータはノードを置いただけでは保存されず消えてしまう。パラメータを保存するには、つまみの値をノードの端子として引き出す必要がある。

中クリックでNew Inspektor [Ctrl+I]を選択すると、ノードの詳細ウィンドウが開く。VSTのノードを選択すると端子になっていない隠れパラメータを見ることができる。

保存したい値の左の所をクリックして色を濃くすると端子になる。

エフェクトひとつ

値を端子にすると、そのつまみの制御はvvvvが握ることになるので、VST側のウィンドウでつまみを動かしてもすぐに元に戻ってしまう。AudioInのボリュームなどと同様、vvvvの端子を右クリックして設定することになる。

エフェクトとパラメータ

IOBoxとつなぐと値が視認できて便利。この上にvvvv標準のオシレータなどをつなぐと自動で値の変わるアレが作れる。

VSTiと組み合わせればリズムマシンなども作れると思うが、Skypeで使ってもうるさがられただけだったので割愛する。

2011-06-27

JavaScript: new無しで基本型のコンストラクタを呼んだ時の挙動

Closure Compilernew無しのArray()を使うのを見て興味を持ったので調べた。適当にまとめるとこうなる。

型名挙動
Object
String
Boolean
Number
引数をその型に変換
Function
Array
RegExp
Error
newがあるときと同じ
Datenew Date().toString()と同様
(引数は無視される)

Dateだけ挙動が不思議。

以下ECMA Scriptの仕様書より抜粋。

15.2.1 The Object Constructor Called as a Function
When Object is called as a function rather than as a constructor, it performs a type conversion.

15.2.1.1 Object ( [ value ] )
When the Object function is called with no arguments or with one argument value, the following steps are
taken:
1. If value is null, undefined or not supplied, create and return a new Object object exactly as if the standard
built-in Object constructor had been called with the same arguments (15.2.2.1).
2. Return ToObject(value).

15.3.1 The Function Constructor Called as a Function
When Function is called as a function rather than as a constructor, it creates and initialises a new Function
object. Thus the function call Function(…) is equivalent to the object creation expression new
Function(…) with the same arguments.

15.4.1 The Array Constructor Called as a Function
When Array is called as a function rather than as a constructor, it creates and initialises a new Array object.
Thus the function call Array(…) is equivalent to the object creation expression new Array(…) with the
same arguments.

15.5.1 The String Constructor Called as a Function
When String is called as a function rather than as a constructor, it performs a type conversion.
15.5.1.1 String ( [ value ] )
Returns a String value (not a String object) computed by ToString(value). If value is not supplied, the empty
String "" is returned.

15.6.1 The Boolean Constructor Called as a Function
When Boolean is called as a function rather than as a constructor, it performs a type conversion.
15.6.1.1 Boolean (value)
Returns a Boolean value (not a Boolean object) computed by ToBoolean(value).

15.7.1 The Number Constructor Called as a Function
When Number is called as a function rather than as a constructor, it performs a type conversion.
15.7.1.1 Number ( [ value ] )
Returns a Number value (not a Number object) computed by ToNumber(value) if value was supplied, else
returns +0.

15.10.3.1 RegExp(pattern, flags)
If pattern is an object R whose [[Class]] internal property is "RegExp" and flags is undefined, then return R
unchanged. Otherwise call the standard built-in RegExp constructor (15.10.4.1) as if by the expression new
RegExp(pattern, flags) and return the object constructed by that constructor.

15.11.1 The Error Constructor Called as a Function
When Error is called as a function rather than as a constructor, it creates and initialises a new Error object.
Thus the function call Error(…) is equivalent to the object creation expression new Error(…) with the
same arguments.

2011-03-18

murmurをMac OS X 10.4 PowerPCでコンパイルする

Mumbleというオープンソースのボイスチャットのソフトを見つけた。サーバの実装はMurmurというらしい。自宅で動かしているサーバはPowerPCのOS X Tigerという古いもので、murmurの最新版ではサポートされていない。でもいじりまわした結果なんとか動いたのでメモする。

うまくいくまでの経緯

まず、DarwinPortsにmurmurがあったのでそれを単純に入れてみようとした(sudo port install murmur)。依存関係のビルドは、qt4-macというportの作業領域に6GBほどHDDを食われたものの、特に問題なくすんだ。しかし、murmur自体のビルドでエラーになった。ログを見るとコンパイラの引数に-arch i386という文字があったので最新版はとりあえず諦めた。

DarwinPortsを諦めてtarballを野良ビルドすることも考えたが、サーバとして動かすなら、ビルドするだけではすまない。LaunchDaemonsなどに登録する必要がある。plistなどを用意する作業と天秤にかけて、Portfile(DarwinPortsのビルド手順を記述したスクリプト)をいじってみることにした。

検索してみると、昔のバージョンではppcでも動いていたような報告がある。Portfile歴史をさかのぼってみると、1.2.2(この記事時点での最新 r75458)になる前のバージョンは1.1.8だった。とりあえずr69125のPortfileを落として野良ビルドしたところ、qmakeが見つからないというエラーが出た。

最新版のPortfileではqt関係の記述が変わっていたので、それを見ながら適当にPortfileをいじった。ビルドしなおすとエラーが変化して、リンク時にエラーが出た。/usr/bin/ld: Undefined symbols: _Gestaltという見覚えのあるエラーが出ていたので、リンカのオプションに-framework CoreServicesをつけるようなMakefileを生成するようにqmakeに引数を与えるようPortfileを修正した(こういうとき自動化は怖い)。

いじったPortfile

できあがったPortfileは以下。そのうちフィードバックしたい。

使い方としては、このPortfileを空のフォルダに置き、そこにcdしてsudo port installとすればよい。

追記

umurmurというQtに依存しないクローンがあった。ビルドがmake一発と簡単な上1.2のプロトコルにも対応しているので、こっちを入れるほうがよさそう。

野良ビルドのメモ:

  • libconfigというライブラリはDarwinPortsにあるものとは別物らしい
  • 定数_POSIX_PRIORITY_SCHEDULINGを#undefする必要がある

2011-02-14

Thumbs.dbのフォーマット

WindowsのThumbs.dbはOLE Compound DocumentとかStructured Storageという形式でできている。Wordのdocファイルにも使われていて、要はアーカイブの一種らしい。

この形式を解凍する方法自体はメモしない。ここでは内部にどういうファイルが入っているかだけ書く。

これを読み込むライブラリは、Rubyではruby-oleジェム、PerlならWin32::OLE、JavaならApache POIのPOIFS、CならCOMのIStorageとかPOLEというライブラリがある。

ディレクトリの内容

そのたぐいのライブラリで解凍すると、中身のディレクトリはこうなっている。フォルダはなく、いくつかバイナリファイルが入っているだけ。

  • Catalog
  • 01
  • 1
  • 11
  • 2
  • 21
  • 3
  • 4
  • 5
  • ...

内容としては、名前が数字のファイルがJPEGファイルにヘッダがくっついたもので、Catalogにその数字と実ファイル名の対応付けが記録されている。

各サムネイルにはIDがついている。

不思議なのはIDとファイル名が文字列として反転しているというところ。

idが5のサムネイルは5というファイルに入っているが、idが13のサムネイルは31というファイルに入る。

カタログ (Catalog ファイル)

Catalogのフォーマットは先頭からこう。数値は全てUint32LE。

  • ヘッダが16バイト
    • 4バイト~8バイト目にサムネイル数
  • サムネイル数だけ以下を繰り返し(各エントリのサイズは不定)
    • エントリ全体のサイズ
    • ID
    • 更新日時
    • 何かの数値
    • UTF16LEでファイル名
    • 0でパディング

サムネイル (0, 1, 11, 2, 21 ..)

サムネイル画像のファイルの内容は以下のとおり。

  • ヘッダ (12バイトか16バイト)
  • JPEGファイル

ヘッダの長さは可変というか、12バイトか16バイトかのどちらからしい。

本来ならヘッダにあるタイプを調べるべきなのだろうが、とりあえずJPEGファイルの頭にあるマジックナンバー(0xFF 0xD8)を目印にすればヘッダの内容を読む必要はない。

実装

画像を吐き出すだけのツールを作るなら、流れはこうなる。

  • Structured Storageのライブラリを使ってthumbs.dbを読み込む
  • Catalogファイルを取り出して上のフォーマットで解釈する
  • 各ファイル名についてサムネイルを取り出し、JPEGファイルをその辺に書き出す

これをRubyとC++で書いた。

手抜き

以上はlibforensicsというPythonのライブラリのlf.win.shell.thumbsdbのコードを読んだだけなので、このライブラリを使うのが一番早いかもしれない。

こういった解体処理をする必要があるのは元のファイル名が欲しいときだけで、サムネイル画像を取り出したいだけならまじめに読み込む必要はない。

無圧縮で暗号化もされていないので、thumbs.dbを普通に開き、JPEGのSOI(0xFF 0xD8)とEOI(0xFF 0xD9)を探して次々書き出せばすむ。

(copied from tumblr)

2011-02-06

android.util.Config.DEBUGが役に立たない

public static final boolean DEBUG

If this is a debug build, this field will be true.

android.util.ConfigというクラスにDEBUGというbooleanの定数がある。説明の通りデバッグビルドかどうかこの定数を通じて調べることができる。これは嘘ではないのだが、アプリケーション(apk)のビルドのことではなく、デバイスがデバッグビルドかどうかというのを見るものらしい。つまり、アプリケーションの開発者の欲しがるような「LOGのメソッドを呼ぶか切り替えるスイッチ」ではない。

代案としてあがっているものはどれも微妙に意味あいが違う。

  • ユニットテストを真面目に書く (・・・ようにすれば、デバッグのためのログは必要なくなるはず)

ウィザードモードなんかをつけたい場合には応用がきかない。

  • デバッガが接続されているか調べる

if (Debug.isDebuggerConnected()) { ... }

見た目に綺麗だが、「実機に入れて動かしていたら落ちたのでログを見たい」という場合とは使いどころが合わない。

  • マニフェスト(AndroidManifest.xml) の属性設定でDebuggableを切り替え、それを調べる

if ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { ... }

おそらくこれが最もまっとうな方法。設定ファイルに書き込む欄があり、それを調べる手段があるのだから、利用しない手はない。ApplicationInfoを取得するのに手間がかかるのが難点。

  • 自前で定数を定義する

static import com.example.android.app.StaticConfig.DEBUG;

if (DEBUG) { ... }

上に出た代案の問題はどれも問題がある。条件がコンパイル時定数でないので最適化されず、結局コンパイル後にも条件の判定やLOG文は残ってしまう。「#ifdefの代わり」としては結局これしかないように思う。

DEBUG = true; DEBUG = false; を手で切り替える手間を省けないかと考えている。antの-pre-compileタスクあたりにコード生成を挟めばよいのだろうか。

(copied from tumblr)

2011-01-07

異種enumの比較を防ぐ

Visual C++ 2010はenum classがない(SP1でもC++0xの機能を増やす予定はないとか)。

でも、enum classがなくても型チェックはある程度できる。

enum WeaponType {
    WEAPON_TYPE_MOCHI,
    WEAPON_TYPE_SALMIAKKI,
};
enum TreatsType {
    TREATS_TYPE_MOCHI,
    TREATS_TYPE_SALMIAKKI,
};
// the trick is here
void operator ==(WeaponType, int);
void operator !=(WeaponType, int);
void operator ==(int, WeaponType);
void operator !=(int, WeaponType);
void operator ==(TreatsType, int);
void operator !=(TreatsType, int);
void operator ==(int, TreatsType);
void operator !=(int, TreatsType);
void test() {
    if (WEAPON_TYPE_MOCHI != WEAPON_TYPE_SALMIAKKI) { // ok
        eat();
    }
    if (WEAPON_TYPE_MOCHI == TREATS_TYPE_SALMIAKKI) { // error!
        scream();
    }
}

enum#defineconstの羅列より短いから使っているだけなので、自分は使わないだろう」と思っていたが、昨日別種のenumを比較するコードを書いてバグを出し、30分悩んだ。なので少なくとも型チェックのところは欲しいと思うようになった。

type-safe enumのようなクラスでラップするという方法は確実だが、文字数がとても多いのでそらで書き下せる自信がない。「enumのまま型チェックをする」方法を考えていたら上記の方法を思いついた。

つまり、「比較のoperatorを定義して、実装しない」という方法だ。

最初は operator ==(WeaponType, TreatsType); のように定義していたが、 enum の数を増やすと爆発することに気づいて int に変えた。 int でないほうがエラーは多少読みやすい。

返り値を void でなく bool で定義すると、リンク時のエラーになる。 void で定義するとコンパイル時にエラーが出てくれるのでそうした。