ならば

音とかで遊んでいたログ

まばたきの記録

The Eye Tribe Trackerでまばたきの検出もできると書いたので実際にやった。

視線は無視して、まばたきの検出だけで良ければ被験者の負担はとても軽くなる。
理由は、視線の座標を取得する場合には、視線と画面上の位置の対応付けが狂わないようにキャリブレーションの後はできる限り頭を固定する必要があるのに対して、まばたきは赤外線カメラから見たときの瞳孔の有無だけ分かれば良いので、デバイスが発する赤外線の範囲に目が正面向きで入ってさえいれば画面に対する相対的な位置はどうでもいいためである。
というわけで、目が隠れなければ多少頭を振ろうがコーヒーを飲もうが自由にやっても問題なく、そればかりか一度立ち上がってキッチンでコーヒーを淹れなおしてきて座っても、立ち上がる前と同じようにまばたきは検出してくれる。

要するに被験者にとって長時間記録し続けるだけの身体的な余裕が生まれるということだ。
だから二時間以上記録した。この間ずっとDVDで映画を見ていた。

結果

コーヒー:

飲んでない。

まばたきの頻度:

平均すると、1分間に2.71回だった。
日本語版Wikipediaを見ると

まばたきの回数は子供では1分間につき約5 - 18回、大人では男性が20回、女性が15回程度といわれている[誰によって?]。

まばたき

とあって一瞬とても不安になったが、英語版には

when the eyes are focused on an object for an extended period of time, such as when reading, the rate of blinking decreases to about 3 to 4 times per minute

Blink

とあるので、ずっと映画に見入ってたからこんなものだろう多分。確かに今これを書いているときはもっと頻度が高い。

まばたきの分布:

1分あたりのまばたき発生回数は有意水準5%でみたときにポアソンしているという目論見。

回数 0 1 2 3 4 5 6 7以上
観測度数 3 26 31 38 24 9 4 0
期待度数 9.02 24.4 33.02 29.78 20.15 10.91 4.92 2.79

カイ二乗検定で、
\chi^2 = 10.55
\chi_6^2(0.05) = 12.59
というわけで、ポアソンしていないとは言えない。

目のサービス稼働率
  • 平均まばたき間隔 (MTBB) を、まばたきから次のまばたきまでの時間の平均値とする。
  • 平均まばたき時間 (MTTO) を、目を閉じてから開けるまでの時間の平均値とする。
  • このとき、目のサービス稼働率は次の式で表すことができる。

\frac{\rm MTBB}{{{\rm MTBB} + {\rm MTTO}}

実測値は、MTBBが21.89秒、MTTOが0.22秒。したがって、何かに見入っているときの稼働率は99.00%ということになる。
ミッションクリティカルシステムレベルではない。

The Eye Tribe Trackerを使った視線の記録

昨年末にThe Eye Tribe Trackerという$99の視線追跡デバイスが発売された。待ちわびた。
今のところ開発者向けのパッケージだけがあり、専用のトラッキングバイスAPI仕様、SDK、サンプルプログラム、チュートリアルが用意されている。

早速注文して年明けに届いたのでいろいろ遊んでみた。

ディスプレイの前にある細いスティック状のものが専用デバイスで、きしめんのように平たいUSBケーブルでPCと接続する*1
視線で何かをコントロールする使い方もありだけど、今回は何かをやっているときの視線をひたすら記録した。

弾幕シューティング

rRootageで弾避け。
視線は黄色い円。

一番最初にこれを持ってきておいてなんだが、視線では表せない周辺視野でどこまで見えているかも重要だと思う。

お絵かき

持ち腐れのペンタブレットで、参考資料なしで干支を描く。
視線は青い円。

ド下手なのは別にして明らかにミスしているところがあるが、二回目を撮る気になれなかった。

楽譜作成

LilyPondのフロントエンドFrescobaldiを利用した。
視線は青い円。

視線がちょくちょく画面外に出るのは慣れない位置にあるキーボードのキーを頻繁に確認しているため。
録画をサブディスプレイでやっていて、普段とは違う姿勢でキーを叩いているのでよく間違えている。

写真閲覧

Little Visualsからダウンロードしてきた画像をスライドショーで眺める。
視線は黄色い円。

特にこれといって見るべきところのない画像は視線がさまよう。

補足

手抜きの影響

どこを見ているかを調査する目的で今回のように視線の跡を動画に描く場合、まじめに(学術研究とかで)やるなら、被験者の目にはその跡が見えないようにするべきだと思う。
たとえば、視線の座標だけ別のログで取っておいて後から動画に重ねて描画するとか。

今回作ったプログラムは手抜きをして、視線の座標に合わせて画面上にそのまま円を出すようにしているので*2ちょうど目を向けているところに変な丸い物体がずっと付いてくるという状況になっている。
動画を撮る前にいろいろ試して目を慣らしているつもりだけど、全く影響がないとは言えない。
特に弾幕シューティングの場合は円が中途半端に意識に上ってくると弾幕と混同する。

まあでも、シューティングを除けば影響はあっても少しかな。
思い立ってマインスイーパーをやってみたら、ほぼ見ているマスに円が張り付いてくる。

そんなことより上の動画は追跡精度の高さも示している。
$99のデバイスで、ちょっとキャリブレーションやっただけでこれだけの精度が出るというのは素晴らしい。

The Eye Tribeというベンチャー

この素晴らしいデバイスを開発・販売しているThe Eye Tribe社は設立から二年くらいのベンチャーで、この素晴らしいデバイスを作るために立ち上げられた。
創業メンバーにはコペンハーゲンIT大学で作られていたオープンソースの視線追跡ソフトITU Gaze Trackerの開発コアメンバーがいて、要するに大学でやっていたプロジェクトを事業化したということだと思う。
事業化の理由は、需要があることが分かったので経済的な障害をなくしてフルタイムで取り組みたいと考えたかららしい。

こういう経緯があるので、ITU Gaze Tracker関連は今後開発休止になる気がする*3
確か後継ソフトがベータ版であったのだが、それも正式版は出ないだろう*4

ITU Gaze Trackerでも遊んだ経験からすると、個人的には$99で追跡精度の高い専用デバイスが買えるなら、自分で赤外線カメラ買って試行錯誤するよりはずっと良い。

その他メモ
  • 視線の座標を取ってくる程度のプログラムならサンプルプログラムが生成するDLLをインポートすれば相当簡単に作れる。
    最初の動作確認用にそういうプログラムを作ったとき、自分で書いたC#のコードは20行くらいだった。
  • 左右の目が見ている座標を別々に取得することもできる。
  • 目をつぶると視線が消失するが、実際に試したところこのときの座標は原点(0,0)になる仕様らしい。
    これを利用するとまばたきの検出もできる。左右別々に取得できることも合わせるとウィンクも検出できる。

*1:USB3.0のポートでないと動作しないと公式サイトに書いてある

*2:こうすると単にデスクトップを録画するだけで済むのでとても楽

*3:確認してない

*4:確認してない

普通奏者、快速奏者

「この奏者は『急行』、四分音符より長い音を演奏します」
みたいな。


作り方

ここからは技術的な話。

この画像はLilyPondのソースファイルから生成した楽譜の画像そのままで、生成後は全く手を加えていない。○の列は、LilyPond内部の音楽表現では歌詞として扱われるようになっている。ソースファイルには各○の描画コマンドを一個一個書いているわけではなくて、LilyPondの処理系*1にメロディとスキップする音の長さを与えて自動生成させた。

ソースファイル。
前半に歌詞の自動生成に使うSchemeの関数定義がある。後半が普通の(?)楽譜作成用のコード。

\version "2.16.2"

LOCAL = #(list "普通" (ly:make-duration 5) #'darkgreen)
RAPID = #(list "快速" (ly:make-duration 3) #'blue)
EXPRESS = #(list "急行" (ly:make-duration 2) #'green) 
LIMITED = #(list "特急" (ly:make-duration 1) #'red)
CAGE = #(list "4'33''" (ly:make-duration 0) #'black) 

%% from http://comments.gmane.org/gmane.comp.gnu.lilypond.general/64060
#(define (ly:duration=? dur1 dur2)
   (not (or (ly:duration<? dur1 dur2)
            (ly:duration<? dur2 dur1))))

#(define (stop-mark col)
   "draw a stop mark"
   (markup #:vspace 1.5
     #:raise 1.5
     #:with-color col (#:draw-circle 1.4 0.8 #f)))

#(define (make-a-name name col)
   "make a tag name"
   (list
    (make-music 'ContextSpeccedMusic
      'context-type 'Bottom
      'element (make-music 'PropertySet
                 'value (markup
                         #:line
                         (#:with-color col name))
                 'symbol 'vocalName))
    (make-music 'ContextSpeccedMusic
      'context-type 'Bottom
      'element (make-music 'PropertySet
                 'value 3
                 'symbol 'fontSize))))

#(define (make-a-word dur1 dur2 col)
   "make a stop mark with 'col in verse if 'dur1 > 'dur2"
   (make-music
    'LyricEvent
    'duration
    dur1
    'text
    (if (ly:duration<? dur2 dur1)
        (stop-mark col)
        (stop-mark #'white))))

makeVerse = #(define-music-function
              (parse location music list) 
              (ly:music? list?)
              "make a verse"
              (make-music 'SequentialMusic 'elements
                (append
                 (make-a-name (car list) (caddr list))
                 (map (lambda (x)
                        (make-a-word x (cadr list) (caddr list)))
                   (extract-all-durations music)))))

%% from http://lists.gnu.org/archive/html/lilypond-user/2012-03/msg00642.html
#(define (extract-all-durations music)
   (map! (lambda (m) (ly:music-property m 'duration))
     (extract-music music
       (lambda (m) (ly:duration?
                    (ly:music-property m 'duration))))))


\paper {
  #(set-paper-size "a4")
  markup-system-spacing #'minimum-distance = 12
  system-system-spacing #'minimum-distance = 38
}

\header {
  title = "線路は16小節続くよ"
  tagline = ##f
}

melody = \relative c'' {
  g4. d8 g8. d16 g8. a16 | b2 g | c4. c8 g4 a | b2. r4 | \break
  g4. d8 g8. d16 g8. a16 | b2 g | a4. a8 a4 b | a2. r4 | \break
  a4. a8 gis8. a16 b8. a16 | g!2 d | c'4 c g a | b2. r4 | \break
  e,4. fis8 g8. fis16 g8. e16 | d4. d8 g2 | b4. c8 b4 a | g2. r4|
  \bar "|."
}

<<
  \new Voice {
    \tempo 4 = 120
    \key g \major
    \melody
  }
  \new Lyrics \lyricmode { \makeVerse \melody \LOCAL }
  \new Lyrics \lyricmode { \makeVerse \melody \RAPID }
  \new Lyrics \lyricmode { \makeVerse \melody \EXPRESS }
  \new Lyrics \lyricmode { \makeVerse \melody \LIMITED }
  \new Lyrics \lyricmode { \makeVerse \melody \CAGE }
>>


LilyPond内部では音楽はSchemeのリストとして表現されている。
ある音楽構造がどのようなリストで表現されるか、というのは \displayMusic を使うと分かる。
左上がLilyPondのコード、左下が \displayMusic の出力、右が対応する楽譜。


逆にSchemeのS式をLilyPondのソースファイルに書いて楽譜を生成することもできる。


というわけで、冒頭の画像を生成するソースファイルは大まかには次のステップで作成した。

  1. 実現したい楽譜(歌詞の部分に○を表示した楽譜)の短い例を \markup コマンドを使って書く*2
  2. \displayMusic で1の内部表現を知る
  3. 2の内部表現を生成するSchemeの関数を作る
  4. メロディから各音の長さを抽出する関数を作る
    (実際はネットでそれらしきものを検索したらソースコードのコメントに書いてあるサイトに見つかったのでひっぱってきた)
  5. メロディから4の関数を使って得られる結果をもとに、3の関数を使って歌詞を作るように処理をまとめる

1から3の流れは公式ドキュメントに例示されている。
Extending LilyPond: 1.3.3 Doubling a note with slurs (example)

大まかにはこの通りだが、実際にはLilyPondもSchemeScheme以前に関数型言語の考え方もよく分かっていないので試行錯誤しかしていない気がする。何ひとつ分かってないではないか*3


備忘録

今回知ったLilyPondとの戯れ方をメモしておく。

Q:
「普通」、「急行」のように、歌詞の前に名前を付けるにはどうしたらいいのか?
A:
vocalName で設定する。歌詞の番号の場合は stanza で、これらは表示される位置も異なる。
Q:
休符にも歌詞を割り当てるにはどうしたらいいのか?
A:
\lyricmode で歌詞を作成し、語句にメロディとは別に長さを指定する。
\addlyrics や \lyricsto だと、メロディのリズムが強制的に当てはめられて、この場合は休符はスキップされる。
Q:
楽譜のタイトルと五線譜のスペースを調整するにはどうしたらいいのか? 五線譜と五線譜の間のスペースは?
A:
\paper の中で設定可能
Q:
調号を指定しているのに、音符にも -is や -es を付けないと変化記号が表示されないのはなぜか?
A:
そういう仕様。公式ドキュメントでも注記されている。

調号は 譜刻される 臨時記号にだけ影響を与え、音符の ピッチ には影響を与えません!

2.1.2 臨時記号と調号


Q:
変化記号のナチュラルを表示するにはどうしたらいいのか?
A:
g! のように "!" を付ける。
補足:
楽譜の記法としては不要だが注意喚起のためにあえて表示したい場合に使う。冒頭の楽譜の10小節目のgが例。

*1:実際はそこから呼び出されるSchemeの処理系

*2:歌詞は普通は単なる文字列だが、\markup を使うとお絵かきもできる

*3:LilyPondとはたまに戯れたくなるのだが、その頃には大体戯れ方を忘れている

大局将棋の駒の成りの系統樹

大局将棋の駒の成りの関係を表す系統樹を作った。大局将棋を指す人は参考にしてください。
当たり前だけどひとつの駒が成れるのは一回だけなので、歩兵が五回成って最終的に大鷲になれるわけではない。
下のリンク先の画像サイズは3253×2315。


というのは動作確認の結果なので実は内容はどうでもいい。確認したかったのはGraphvizで、小さい連結成分を大量に含むグラフのレイアウトが縦や横に異常に長くならないようにするための方法。

成りの系統樹のように連結成分が多いグラフのdotファイルに対して、特に何の対策も取らずにdotコマンドを実行すると、横に異常に長いレイアウトになる。
オリジナルの画像サイズは13292×539で、このままだと横に長すぎて はてなフォトライフに登録できないので75%に縮小した。


この縦横比を改善するために、いくつかのコマンドを組み合わせて使う。
次のコマンドの出力が最初に挙げた画像。

$ ccomps -x ./in.dot | dot | gvpack -array | neato -n2 -Tpng -o ./out.png

パイプの連結をばらして個別に説明。

  • ccompsコマンド
    前にも書いたけど、グラフの連結成分を分離して、その結果をDOT言語で出力する。デフォルトでは連結成分ごとにサブグラフになるが、-xオプションを付けるとグラフに分離される。
  • dotコマンド
    gvpackコマンドの入力にはレイアウト情報が必要なのでdotコマンドを挟んでレイアウト情報を作る。
  • gvpackコマンド
    複数のグラフをひとつのレイアウトに収めて単一のグラフにして出力する。-arrayオプションを付けると入力元の各グラフを行列のように整列して並べてくれる。デフォルトだと整列せず、密になるように押し込もうとするのでもっとコンパクトにレイアウトされるけど、グラフによっては見にくくなる。
  • neatoコマンド
    gvpackコマンドの結果にはレイアウトとしてノードなどの位置情報が既に含まれているので、これ以上レイアウトを変えてほしくない。そこで、neatoコマンドに-nオプションを付けて、指定した位置情報に基づいてグラフを描画させる。-nだけだと多少の調整が入る(ノード同士が重ね合わさらないように位置をずらすとか)けど、-n2にすると指定した通りの位置情報が保たれる。
    普通は有向グラフはdotコマンドでレイアウトするけど、dotコマンドだと-nオプションは無視されるのでneatoコマンドを使う。


ところで、unflattenコマンドは孤立点が多いときとか木構造でリーフが多いときに全体的なレイアウトの広がりを圧縮したい場合には使えるようだが、今回のケースでは上手くいかなかった。

Pythonで音楽学

Python向けライブラリmusic21の初歩*1

music21は音楽学のためのライブラリで、音楽構造の抽出、変換、編集などといったことを記号操作によって行う。外部プログラムと連携して、抽出した情報の可視化もできる。
記号操作と書いたのは、MusicXMLABCのような記譜用のマークアップ言語で書かれたファイルを入力として読み込んで、内部でも音楽要素を記号として扱って処理を実行するから。音響信号は扱えないので、WavやらMP3といった音声ファイルは読み込めない。


Pythonインタラクティブモードであれこれ試すと楽しい。音楽学はよくわからないので*2、簡単にできる範囲で可視化したりして遊んだ。

ファイルを読み込んで楽譜を表示する。
楽譜の表示にはMuseScoreかFinale(Finale Reader)が必要。どちらのソフトも演奏機能があるので聴くこともできる。

>>> from music21 import *
>>> s = converter.parse('/home/naraba/program/python/music21/etude10-05.xml')
>>> s.show()

読み込んだ作品はショパンエチュード 作品10 第5番(G♭ major*3 )、愛称「黒鍵」。


こういう曲。


音楽の調を調べる。

>>> s.analyze('key')
<music21.key.Key of F# major>

F♯ majorはG♭ majorの異名同音調


オブジェクトの階層構造は伝統的な西洋音楽の階層構造を反映していて、上からScore(総譜)、Part(パート譜)、Measure(小節)、Note(音符)などのようになっているらしい。「黒鍵」はピアノ曲で右手と左手のパートがあって、メタデータ部を合わせると3つのパートとなる。

>>> s
<music21.stream.Score 181616076>
>>> len(s)
3
>>> s[0], s[1], s[2]
(<music21.metadata.Metadata object at 0xed6a08c>, 
<music21.stream.Part piano>, 
<music21.stream.Part piano>)


左手パートの小節数は86。左手パートの冒頭10小節のピアノロールを表示。

>>> len(s[2])
86
>>> s[2][:10].plot('pianoroll')



右手パートの小節数も当然86。右手パートに含まれる音符の長さのヒストグラムを表示。

>>> len(s[1])
86
>>> s[1].plot(format='histgram', values='quarterLength')

四分音符の長さが基準で1。横軸目盛の割り切れない数の表示が困ったことになっているが、ほとんど16分音符の三連符*4しかない。


右手パートに含まれる音符のピッチクラスごとの数を集計。それに続いて、ピッチクラスの重み付き散布図を表示。

>>> s[1].pitchAttributeCount('name')
{u'E-': 163, u'D-': 254, u'F': 1, u'B-': 162, u'A-': 218, u'G-': 181}
>>> s[1].plot(format='scatterweighted', values='pitchclass')

作品の愛称「黒鍵」の由来通り、右手パートはほとんどがピアノの黒鍵の音だけど、一音だけFの音(ファ)がある。音の長さもこの作品ではレアな四分音符。


F音がどの小節にあるのか探して、その小節だけ楽譜を表示。

>>> for m in s[1].getElementsByClass('Measure'):
...     for p in m.pitches:
...             if p.name == 'F': print m.measureNumber
... 
66
>>> s[1][66].show()


全体を長2度上に移調して、右手パートのピッチクラスの散布図を表示。

>>> s.transpose(2, inPlace=True)
>>> s[1].plot(format='scatterweighted', values='pitchclass')

白鍵と黒鍵の音が混じる作品になった。



メモ:LilyPondのファイルフォーマットは複雑すぎるという話

残念ながら、music21はLilyPondの記法で書かれたlyファイルを読み込めない*5プロジェクト公式の課題トラッカーで表明されている通り、この制約は今のところというわけではなく、今後もずっと続く模様。

理由も上のリンク先に書いてあるが、lyファイルにはSchemeのコードまで書けたりして、他のプログラムが楽譜として解析するには複雑すぎる。仮に一度解析プログラムを作り上げたとしてもLilyPondは活発なプロジェクトだし、構文もよく変更されるので追随するのが大変だろう。

おそらく同じ理由で、lyファイルをMusicXMLとかABCに変換するプログラムも(探した限りでは)ない。lyファイルを他のファイルフォーマットに変換する機能をLilyPond自体に追加するのが他の手段との比較上は一番楽な気がするけど、LilyPondプロジェクトにそういう計画は(探した限りでは)ない。

ちなみに、music21はMIDIファイルなら読み込める。そこで、LilyPondでlyファイルからMIDIファイルを出力して、この出力をmusic21に読み込ませればいいのではないかというと、MIDI出力の時点で楽譜の一部の要素が欠落してしまう。MIDIは演奏データをデジタル転送するための規格で、記譜のための仕様は十分ではないので、MIDI出力でサポートされない要素があるのが理由。

諦めた。

*1:というか、ちょっと動かしてみただけ

*2:それでなぜmusic21に手を出したかというと興味本位

*3:元々のMusicXMLファイルの記述に引きずられて下の楽譜の調号はおかしくなっているけど、Cには全て臨時記号で♭がつけられている

*4:四分音符の長さを1とすると八分音符は0.5で、その三分割なので0.5/3=0.166...

*5:music21で編集した結果を楽譜としてlyファイルに書き出すことはできる

ペンローズ・タイルの上でライフゲーム

ペンローズ・タイルの上でライフゲームを動かすアイデアは、数年前に
"Investigations of Game of Life cellular automata rules on Penrose Tilings: lifetime and ash statistics"*1
という論文で知った。

そのときから動くものが見たいと思っていたが、最近Readyというオープンソースのシミュレーションプログラムで実行できることがわかったので、動かしてみた。


ペンローズ・タイル上のライフゲームのパターンとして、固定物体や振動子は冒頭の論文と同じ著者によってまとめられた記事がある。グライダーのような移動物体は最近発見された

Readyでランダムな初期状態から何回も動かしてみたけど、移動物体の出現確率は格子上のライフゲームに比べて低そう。それから全体の収束も速い。

*1:Nick Owens and Susan Stepney, Automata 2008, Bristol, UK, June 2008, pages 1-34. Luniver Press, 2008

ChucK 1.3.0.0 リリース

何年ぶりなのかすら思い出せないが、8/25にChucKとminiAudicleがアップデートされた。
もう全然触ってないけど主なリリース情報だけ捕捉しておく。
注)書き方が全体的にネガティブ。


ChucK 1.3.0.0

  • Chugin, Chubgraph, ChuGen
    このネーミングはどうかと思うが、今回のリリースで一番の目玉。
    どれもChucK本体を改造せずにユニットジェネレータを拡張・作成するための機能で、次のような違いがある。実装の容易性や処理の複雑性・要求性能に応じて使い分けることとされている。

    • Chugin: C/C++で実装して予めコンパイル済みのユニットジェネレータをロードする仕組み
    • Chubgraph: 既存のユニットジェネレータを組み合わせて新しいものを作る仕組み
    • ChuGen: 音響信号を直に操作するユニットジェネレータを作る仕組み

  • WvOut2, SndBuf2
    ステレオの音響信号を入出力できるようになった。
    これでようやく、左右別々に出力して後で他のツールでミックスするという面倒な作業から解放される。

  • for文の条件式の扱い
    これも具体的な時期を覚えていないが一つか二つくらい前のChucKのバージョンからfor文の条件式が空の場合の扱いが変わった。
    それまではC系の多くの言語と同じく空の場合は真として扱われていたが、制御構文を実行するChucK VMの実装を変更する過程で、エラーとして扱われる仕様に結果的になった*1。結果的という表現を使っているのは本来は意図せずそういうことになったためだが、1.2.1.3まではVMの実装にバグがあって実行時にハングする*2
    今回のバージョンでは、コンパイル時にチェックしてコンソールにエラー出力されるようになった。

    1.2.1.3までの公式のexamplesには、この仕様変更のせいで動かなくなったコードもあったけど、それが今回修正されたかどうかまでは調べていない。


miniAudicle 0.2.1

  • ベースエンジン
    ChucK 1.3.0.0に変更。

  • Chugin用のGUI
    コンパイル済みユニットジェネレータのファイルの格納場所を指定できる設定画面が追加された。

  • まともに終了する
    Windows版でのみあった不具合が修正された。
    今までは、miniAudicleを終了させようとしてもウィンドウは消えるのにプロセスは残ったままだった。
    なお、これもWindows版のみの不具合だが、一旦ChucK VMを起動すると停止できない不具合は今回のリリースでも修正されていない。


やっぱり今後はもう触らないだろうなー。

*1:開発MLを追ってみると経緯がわかるが、なかなかひどいと個人的に思った。それとこのせいで、過去このブログに書いたいくつかのコードは今のバージョンでは動かない

*2:miniAudicleの場合、miniAudicleごと巻き込んでハングする