ならば

音とかで遊んでいたログ

グラフ可視化・操作ツールGephi

Gephiというオープンソースのグラフ可視化・操作ツールがある。
これを書いている時点ではアルファ版だけど、機能的には十分使えるレベルだし、安定性も悪くない。さらにアルファ版にしてはドキュメント類が豊富で、チュートリアルの日本語訳も既にある。

前に使ったデータセットを使いまわして簡単なグラフを描いた。将棋タイトル戦の番勝負で対局したことのある棋士の関係図。今回は全てのタイトルのデータをひとつのグラフに統合した。

  • 頂点
    • 大きさ: 番勝負に出た回数(対局数ではない)に比例して大きい
    • 色: タイトル保持者として挑戦を受けた回数(同上)が多いほど赤に近い
    • レイアウト: 非常に大雑把に左側より右側の頂点のほうが年代が若い
    • 向き: 挑戦者→タイトル保持者
    • 太さ: 番勝負で相手になった回数(同上)に比例して太い


IEだと表示されないかも。その場合はhttp://zoom.it/IzJq

Gephiに読み込ませるために用意したデータは、挑戦者→タイトル保持者、という対局関係のセットだけだが、Gephiの使い方が分かればこの程度の図なら数分で作れる。



公式サイトの動画によると、Gephiは「げふぃ」と読むようだ。

音のストロボ効果

ストロボ効果とは、周期的な動きをある間隔でサンプリングした場合に起こる視覚現象のこと。例えば、走っている車を映した映像でホイールが逆回転しているように見えることがあるのはこの現象。

同じようなことは聴覚でも考えられる。

ただ、普通は扱う音に対してサンプリング周波数が充分に大きいので、視覚ほどはっきりと分かる形で現れることはあまり多くないのだと思う。デジタル録音で一般的なサンプリング周波数のひとつは44.1kHzだけど、この半分の周波数の音はヒトの可聴域の上限を超えたあたりである。

というわけで、あえてサンプリング周波数をかなり低くして、はっきり分かるように音のストロボ効果を起こしてみた。実世界で鳴っている音の録音ではなくて、SuperColliderで生成する音でサンプリング周波数を変えて実験。

   

やっていること。なお、スクリーンキャプチャ時の録音のサンプリング周波数は48kHz。

  1. サンプリング周波数44.1kHzで、1200Hzのサイン波を鳴らす。
    この場合は、1200Hzはナイキスト周波数以下なので折り返し雑音も発生せず1200Hzの高さの音が流れる。
  2. サンプリング周波数を1200Hzに変更して、1200Hzのサイン波を鳴らす。
    音響信号をサンプリングすると、いつも同じ値しか得られないため振幅のある波にならない。その結果、音が全く鳴らない。
    視覚現象で車のホイールが回転していないように見える現象に相当する。
  3. サンプリング周波数1200Hzで、1400Hzのサイン波を鳴らす。
    音響信号をサンプリングすると、波の高さがずれていく。角周波数は 2π*(1400-1200) なので、200Hzの高さの音が流れる。
    視覚現象で車のホイールが実際よりゆっくり回転しているように見える現象に相当する。
  4. サンプリング周波数1200Hzで、1000Hzのサイン波を鳴らす。
    音響信号をサンプリングすると、波の高さがずれていく。角周波数は 2π*(1000-1200) なので、200Hzの高さの音が流れる。ただし、角周波数が負なので、波の進行は逆になる。
    視覚現象で車のホイールが逆回転しているように見える現象に相当する。
  5. サンプリング周波数1200Hzで、200Hzのサイン波を鳴らす。
    この場合は、200Hzはナイキスト周波数以下なので折り返し雑音も発生せず200Hzの高さの音が流れる。

3と4について大雑把な図を描いた。
青い波が音の波。背景の薄い波はサンプリング周波数を持つサイン波で、破線の垂直線のタイミングで音の波がサンプリングされる。
   



標本化定理のお勉強だった。もっと楽しげなことに応用したい。

ローレンツアトラクタを聴く

SuperColliderに取り組み始めた。

できそうな範囲で遊んでみようということで、ローレンツアトラクタを使って音を鳴らす。
ついでにOSCでProcessingと連携して絵も描く。


SuperColliderのプログラム。
x座標が左右の定位に、y座標が音高に、z座標が音量に対応。

(
    SynthDef(\pfm, {|pan, freq, mul|
        var sig;
        sig = Pan2.ar(SinOsc.ar(freq, 0, mul), pan) * 
                EnvGen.kr(Env.perc(0.05, 0.1, 0.4, -8), doneAction: 2);
        Out.ar(0, sig);
    }).send(s);

    r = Routine({
        var sigma = 10, rho = 28, beta = 8/3, dt = 0.01;
        var pos = [20, 1, 0], px, py, pz, nx, ny, nz;
        var osc = NetAddr("127.0.0.1", 12000);
        loop {
            px = pos[0] + (sigma * (pos[1] - pos[0]) * dt);
            py = pos[1] + ((pos[0] * (rho - pos[2]) - pos[1]) * dt);
            pz = pos[2] + ((pos[0] * pos[1] - (beta * pos[2])) * dt);
            pos = [px, py, pz];
            nx = clip2(px / 25, 1);
            ny = 10.0 * py + 330;
            nz = clip2(10.0 / pz, 1);
            Synth(\pfm, [pan: nx, freq: ny, mul: nz]);
            osc.sendMsg("/pos", nx, ny, nz);
            0.05.wait;
        };
    });
)
r.play;
r.stop;


Processingのプログラム。
OSCを使った連携テストのためだけ。

import oscP5.*;
import netP5.*;

final int MAX = 500;
final int FPS = 20;
final int WSZ = 600;
final int PORT = 12000;
final color BGC = 0x333333;

OscP5 osc;
float pos[][];
int tail;
int init;

void setup() {
  tail = 1;
  init = MAX;
  pos = new float[MAX][3];
  background(BGC);
  smooth();
  frameRate(FPS);
  frame.setAlwaysOnTop(true);
  size(WSZ, WSZ);
  osc = new OscP5(this, PORT);
}

void draw() {
  translate(WSZ / 2, 50);  
  background(BGC);
  int idx, idxn, c;
  for (int i = init; i < MAX - 3; i++) {
    idx = (tail + i) % MAX;
    idxn = (idx + 1) % MAX;
    stroke(constrain(i, 50, 255), constrain(i, 50, 255), 0);
    strokeWeight(pow(pos[idx][2] * 3, 2));
    line(pos[idx][0] * WSZ / 2, WSZ - pos[idx][1], 
          pos[idxn][0] * WSZ / 2, WSZ - pos[idxn][1]);
  }
}

void oscEvent(OscMessage msg) {
  if (msg.checkAddrPattern("/pos")) {
    pos[tail][0] = msg.get(0).floatValue();
    pos[tail][1] = msg.get(1).floatValue();
    pos[tail][2] = msg.get(2).floatValue();
    if (init > 0) init--;
    tail = (tail + 1) % MAX;
  }
}


いくつもあるsyntax sugar(なのか?)が全然甘くない。むしろ辛い。saltだよ!

LilyPondの回帰テスト

LilyPondの回帰テストについて調べたのでメモ。特に出力された楽譜の画像の比較について。

回帰テストとはソフトウェアテストの一種で、プログラムを変更したときに今まで動いていた他の(意図した箇所とは違う)部分にまで影響が及んでいないかを確認するために行われる。

プログラムに何らかの変更を加えるたびに実行するのが望ましいが、プログラムが大きいと確認しないといけない機能の数が膨大になり、テストの実行も人間がやると時間がかかる。でも単純で同じことの繰り返しになる部分も多いのでそこはコンピュータにやってもらおう、ということでできる範囲で自動化されることもよくある。

LilyPondにも部分的に自動化された回帰テストがある。テスト項目はLilyPond Regression Tests。LilyPondに新しいバージョン番号が付けられるときに、このテスト項目について新旧のバージョンで出力された楽譜の画像を比較して違いがある部分が意図したものかどうかが確認される。
たとえば、バージョン2.13.5-0と2.13.6-1で違う楽譜の一覧のように違いがあった楽譜のテスト項目だけが自動で一覧化されるので、これを人間が見比べる。


一覧にはテスト項目ごとに上のように楽譜が出力される。左が旧バージョンの出力で、右が新バージョンの出力に「差異」を重ねた画像。右のぼやけた部分が「差異」で、左の楽譜において音符などの要素があったところを表す。

右の画像はImageMagickのツールを使って生成されている。

compare -depth 8 -dissimilarity-threshold 1 旧楽譜.png 新楽譜.png 差.png
convert  -depth 8 差.png -blur 0x3 -negate -channel alpha,blue -type TrueColorMatte -fx 'intensity' ぼやけた.png
composite -compose atop -quality 65 ぼやけた.png 新楽譜.png 右の画像.png



今回気になったのは、右の画像をどうやって作っているのか、ということともうひとつ、回帰テストの実行プログラムはどうやって新旧で出力された楽譜が違うと判定しているのか、ということだった。
後者は実は楽譜の画像そのものを比較しているわけではなくて、楽譜上の音符などの要素の位置(数値)を新旧で比較して、それが閾値を超えているかで判定しているようだ。

ChucKでsleep sortを実装

sleep sortが面白い。
時間制御といえばstrongly-timedなプログラミング言語ChucK。140字以内を目指した。


sleepする時間の単位が音響信号のサンプリング間隔なので(constant factorの意味で)速い。

囲碁+ボロノイ図

囲碁の対局で碁石を母点にしてボロノイ図を描くとどうなっていくか見たくなって作った。
碁石の色に対応させて二色で領域を塗り分けると勢力図のようなものができる。
GoGuiベース。動画の対局者は黒白共にGNU Go 3.8のデフォルトレベル。


ボロノイ図囲碁に応用するという発想は結構あると思うが、評価関数の一部などとして有用かどうかは置いておいて、単にボロノイ図を描いただけ。
盤上の格子点のみを対象にした離散ボロノイ図にすることも考えたけど、あえてやめた。人間だと囲碁の勢力の大雑把な把握は離散的ではない気がするから。

MeCabで古文の形態素解析

中古和文UniDicという形態素解析辞書を使うと、MeCabで古文の形態素解析ができる。


辞書のアーカイブを適当な場所に解凍して、mecabの-dオプションでその場所を指定すれば、すぐに試せる。

$ echo "思ひつつ寝ればや人の見えつらむ夢と知りせば覚めざらましを" | mecab -d ./UniDic
思ひ	動詞,一般,*,*,文語四段-ハ行,連用形-一般,オモウ,思う,思ひ,オモイ,オモヒ,和,思ふ,オモウ,オモフ,オモウ,*,*,*,*,*,*,2,C1,*
つつ	助詞,接続助詞,*,*,*,*,ツツ,つつ,つつ,ツツ,ツツ,和,つつ,ツツ,ツツ,ツツ,*,*,*,*,*,*,*,動詞%F4@1,*
寝れ	動詞,一般,*,*,文語下二段-ナ行,已然形-一般,ネル,寝る,寝れ,ヌレ,ヌレ,和,寝,ヌ,ヌ,ヌ,*,*,*,*,*,*,0,C3,*
ばや	助詞,終助詞,*,*,*,*,バヤ,ばや,ばや,バヤ,バヤ,和,ばや,バヤ,バヤ,バヤ,*,*,*,*,*,*,*,*,*
人	名詞,普通名詞,一般,*,*,*,ヒト,人,人,ヒト,ヒト,和,人,ヒト,ヒト,ヒト,ヒ濁,基本形,*,*,*,*,0,C3,*
の	助詞,格助詞,*,*,*,*,ノ,の,の,ノ,ノ,和,の,ノ,ノ,ノ,*,*,*,*,*,*,*,名詞%F1,*
見え	動詞,一般,*,*,文語下二段-ヤ行,連用形-一般,ミエル,見える,見え,ミエ,ミエ,和,見ゆ,ミユ,ミユ,ミユ,*,*,*,*,*,*,1,C1,*
つ	助動詞,*,*,*,文語助動詞-ツ,終止形-一般,ツ,つ,つ,ツ,ツ,和,つ,ツ,ツ,ツ,*,*,*,*,*,*,*,動詞%F4@0,*
らむ	助動詞,*,*,*,文語助動詞-ラム,連体形-一般,ラム,らむ,らむ,ラム,ラム,和,らむ,ラム,ラム,ラム,*,*,*,*,*,*,*,"動詞%F2@1,形容詞%F4@1",*
夢	名詞,普通名詞,一般,*,*,*,ユメ,夢,夢,ユメ,ユメ,和,夢,ユメ,ユメ,ユメ,*,*,*,*,*,*,2,C3,*
と	助詞,格助詞,*,*,*,*,ト,と,と,ト,ト,和,と,ト,ト,ト,*,*,*,*,*,*,*,"名詞%F1,動詞%F1,形容詞%F2@-1",*
知り	動詞,一般,*,*,文語四段-ラ行,連用形-一般,シル,知る,知り,シリ,シリ,和,知る,シル,シル,シル,*,*,*,*,*,*,0,C2,*
せ	助動詞,*,*,*,文語助動詞-キ,未然形-一般,キ,き,せ,セ,セ,和,き,キ,キ,キ,*,*,*,*,*,*,*,動詞%F4@0,*
ば	助詞,接続助詞,*,*,*,*,バ,ば,ば,バ,バ,和,ば,バ,バ,バ,*,*,*,*,*,*,*,"動詞%F2@0,形容詞%F2@-1",*
覚め	動詞,一般,*,*,文語下二段-マ行,未然形-一般,サメル,覚める,覚め,サメ,サメ,和,覚む,サム,サム,サム,*,*,*,*,*,*,1,C1,*
ざら	助動詞,*,*,*,文語助動詞-ズ,未然形-補助,ズ,ず,ざら,ザラ,ザラ,和,ず,ズ,ズ,ズ,*,*,*,*,*,*,*,"形容詞%F4@-1,動詞%F3@0",*
まし	助動詞,*,*,*,文語助動詞-マシ,終止形-一般,マシ,まし,まし,マシ,マシ,和,まし,マシ,マシ,マシ,*,*,*,*,*,*,*,*,*
を	助詞,接続助詞,*,*,*,*,ヲ,を,を,ヲ,ヲ,和,を,ヲ,ヲ,ヲ,*,*,*,*,*,*,*,*,*
EOS

小野小町

よくある応用1。単語出現頻度ランキング。

日本三大随筆で使われている形容詞Top10。カッコ内の数字は全形容詞の中の割合。

順位 枕草子 方丈記 徒然草
1 をかし (12.0%) なし (29.4%) なし (19.8%)
2 いみじ (9.0%) 多し (3.4%) よし (8.6%)
3 なし (7.6%) 近し (3.4%) をかし (5.4%)
4 めでたし (3.6%) おなじ (2.8%) いみじ (4.3%)
5 にくし (3.3%) むなし (2.8%) 多し (4.0%)
6 よし (2.8%) やすし (2.8%) 久し (1.9%)
7 白し (2.0%) 深し (2.8%) めでたし (1.6%)
8 多し (1.5%) せばし (1.7%) あやし (1.3%)
9 疾し (1.5%) ちひさし (1.7%) 同じ (1.3%)
10 わろし (1.4%) ともし (1.7%) 深し (1.3%)
- - 久し (1.7%) 若し (1.3%)
- - 遠し (1.7%) -
- - 重し (1.7%) -

枕草子は「をかし」の文学。


よくある応用2。マルコフ連鎖で作文。

入力は竹取物語形態素2-gram。

今は下してよ。翁も塗籠の内の絹、綿、錢など、ある山寺に、三寸ばかりなる人は、火にくべて燒かせ給ふ。御官冠つかう奉りて死ぬばかりなり。然る所へ罷らむずるも、「大伴大納言は、數多の年渡りける唐土船の王卿といふ。赫映姫、月出づれば、車に乘りて罷りぬれば、逢ひ奉る。辛うじて、「東の海に紛れむと思す。いま金少しの事なり」と申す。『いづれ劣勝おはしまさねば、ゆかしき物見せ給へらむには死なぬ藥も食はず思ひつゝ、かの裘、我が國に生まれぬるとならば、この皇子に婚ひ奉り給ひね。人ないたく侘びさせ奉らせ給ひて、赫映姫容貌世にあるまじきに、明暮見馴れたる赫映姫外に置きて見て、「斯ばかり守る所に、物思にはあれど、猶めでたく思しめさるゝ事堰きとめ難し。斯く難き事をば、あな嬉しと喜びて、分ちて求め奉れども、歸るまで齋をし、白玉を實としき。これを燒きて見つれば、裘の樣高くうるはし。これを聞きてぞ、思ふに違ふ事をば、赫映姫のいはく、「おいらかに、同じ所に籠り居、或は唱歌をし、白玉を實とし、白玉を實として居たる人あり。それに白銀を根としき。或人の志疎かならざりし。玉の枝につけても、人聞き恥かしく覺え給ふなりけり。貝を取らす。中將人々を引具して率ておはしまさむ。この月に出でて、歌詠み加へて持ちて入りたり。


ひどい気がする。