January
SU MO TU WE TH FR SA
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31


February
SU MO TU WE TH FR SA
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28


March
SU MO TU WE TH FR SA
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31


April
SU MO TU WE TH FR SA
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30


May
SU MO TU WE TH FR SA
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31


June
SU MO TU WE TH FR SA
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30


July
SU MO TU WE TH FR SA
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31


August
SU MO TU WE TH FR SA
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31


September
SU MO TU WE TH FR SA
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30


October
SU MO TU WE TH FR SA
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31


November
SU MO TU WE TH FR SA
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30


December
SU MO TU WE TH FR SA
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

2011年02月28日(月) 9/3℃

Update: 22:06:13
出直してまいりましたッ!(`∇´ゞ
昨日のFFTによる畳み込み処理にはバグがありました。
畳み込み処理をすると結果は畳み込む前よりちょっと長くなるのですが、その長くなった分を捨ててしまっていました。(笑) 結果、境界を整合したつもりが整合になっていなかったんですね。(爆)
ちゃんと整合できるようになったので、タップ数と同じサンプル数のデータを一気に処理できるようになって、畳み込み処理がチョー速くなりました。(^^)v

Update: 22:16:16
自分メモ。
というわけで、およその処理負荷量は以下のようになっています。

CPU: AMD Athlon II X2 235e Dual Core 2.7GHz
アルゴリズム バーチャルサラウンド無し バーチャルサラウンド有り
時間軸畳み込み
(SSE命令を使用)
10% 20%
FFTを使用した畳み込み
(SSE命令未使用)
3% 6%

FFTによる畳み込み処理のバグが取れたので もうこれで良いかな。(笑)




2011年02月27日(日) 16/0℃

Update: 14:37:17
オーディオプロセッサ。
先日「畳み込み処理がSSE命令を使って2.5倍速くなった」と書きましたが、人間の欲とは恐ろしいもので(爆) もっと速く出来ないか試してみました。 結果、25%程速くなりました。
時間軸での畳み込み処理はもうこれ以上速くは出来そうにないのですが、FFTを使って周波数領域で畳み込みを行うと格段に速くなります。 理論上の話ですが、N をタップ数とすると、時間軸での処理量は NxN に比例しますが、周波数領域では NxlogN に比例します。 従ってタップ数が大きくなればなるほど FFT を使った処理の方が有利になります。
しかし、ここには落とし穴があって、FTT はタップ数に対応する時間で周期性を仮定しているので FFT の境界で不整合が発生します。 これを回避する方法の一つとして、処理するデータをタップ数分一気に処理してしまわずに、小さいブロックに分割してシフトしながら処理するようにします。 ブロックを小さくすると処理後のデータのクオリティは高くなりますが FFT の恩恵がなくなってしまいます。
私のオーディオシステムでは タップ数は2048ですが、処理データのブロックの大きさは256サンプルが限界でした。 ブロックサイズを128サンプルにするとクオリティは良くなりますが FFTを使用しなかった場合(SSE命令を使用して時間軸で畳み込み)より処理時間が余計にかかるようになってしまいました。 ブロックサイズを512サンプルにすると 処理速度はFFTを使用しなかった場合の2倍くらいになりますが、音質が悪くなりました。
因みに、FFT を使った畳み込み処理では SSE 命令を使用していません。 SSEを使用したオプティマイズ(Vectorization)をイネーブルにすると 何故か巧く動かなくなってしまうんです。 多分、データのアライメントの問題だと思います。
出直してくるかもしれません。(笑)

Update: 15:06:56
自分メモ。
オーディオプロセッサに使用しているCPUは AMD Athlon II X2 235e Dual Core 2.7GHz です。
このシステムの上でのおよその処理負荷量は以下のようになっています。

アルゴリズム バーチャルサラウンド無し バーチャルサラウンド有り
時間軸畳み込み 10% 20%
FFTを使用した畳み込み 7% 15%

このシステムで動かす上では全く問題ない負荷量ですが、将来消費電力の低いもっと遅いCPUに置き換えるかもしれないことを考えると、速くしておくに越したことは無いと思います。(笑)




2011年02月26日(土) 10/2℃

Update: 16:48:00
オーディオプロセッサ。
今週は仕事から帰ってから ちまちまと Matrix Equalizer を作っていました。 因みに、Matrix Equalizer って言葉があるかどうかは不明です。 右写真のように、マトリックスの上にポイントを移動して イコライザーのパラメータを調節するってものです。


Update: 16:56:23
オーディオプロセッサ。
Matrix Equalizer は、慣れると通常のグラフィックイコライザよりも遥に使い良いです。
私が作ったのは、基本的にパラメトリック・イコライザです。 センターは 100Hz と 16kHz の2点固定なのですが、Qを幅広く調節できるし、強調・抑制が独立に出来るので、実質3バンドを調整できます。 スピーカー等の特性補正は済んでフラットな特性になっているオーディオデータに対して適用する目的のものですので、機能的には十分ですね。
良いものができました。。。。(笑)




2011年02月20日(日) 8/3℃

Update: 20:49:20
昨日久しぶりに散髪に行きました。 今の所に引っ越してきて2回目の散髪です。
1回目は近所の床屋に行ったのですが、なんかどんくさくて。。。(爆) 床屋ってーとまぁ要らない髭剃りまでディフォルトで付いてきます。 剃って貰っている時ジョリジョリと良い音がしていて「良く剃れているのね」と思っていたのですが、床屋から出て頬を触ったら 全く剃れていませんでした。 剃れていない音だったんですね。(爆) そういうわけで、もうその床屋には行かないことにしたのです。
で、今回はカットオンリーの店に入ってみました。 所要時間10分、シャンプーなしです。 シャシャッと切ってもらってスッキリしました。 1000円です。




2011年02月19日(土) 10/-1℃

Update: 05:06:54
オーディオプロセッサ。
完成したつもりが、チマチマといぢってます。(爆)
「オーディオプロセッサが動いているワークディレクトリを samba を使って Windows から見える様にすれば Windows 上で GUI スクリプト(Tcl/Tk)を動かしてコントロールできるよね」って事に今頃気が付いて(笑) 設定をやっていました。 ついでにパラメータ調整もやり始めてしまって、やっと気が済みました。(爆)
あぁ、ねむッ!(>o<)

Update: 16:35:22
pino のマロングラッセ味が売っていたので買ってみました。
「限定」って魔性の言葉だよね。





2011年02月18日(金) 14/3℃

Update: 16:10:21
アミューズメント・エキスポに来ました。
てか幕張遠い。(爆)( ̄∀ ̄)


Update: 16:57:35
歩き疲れたので休憩中。(≧∇≦)





2011年02月15日(火) 9/1℃

Update: 21:45:44
昨日のぼた雪の天気から打って変わって、今日は良い天気でした。
午前中は富士山がクッキリでした。





2011年02月14日(月) 6/-2℃

Update: 21:32:54
オーディオプロセッサ。
以前なんとかもう少し高速化できないか検討中みたいな事を書きましたが、週末畳み込み処理を色々検討して、現在の単純処理からSSE命令に置き換えて 2.5倍速くなりました。
畳み込み処理というのは 2重ループの積和演算で、処理するデータと畳み込みのカーネルは反対向きの順に掛け算処理されます。 私の先週までの畳み込み処理のコードは以下のようになっています。

typedef float afloat __attribute__ ((__aligned__(16)));

void    fir_process(FIR *pfir, int ndata, afloat * __restrict__ s, afloat * __restrict__ d)
{
    int     i, k, offset;
    afloat  v;

    // copy data to the shift buffer
    offset = pfir->ldata - ndata;
    memmove(&pfir->data[0], &pfir->data[ndata],  sizeof (afloat) * offset);
    memcpy (&pfir->data[offset], s, sizeof (afloat) * ndata);

    // check offset
    if ((offset -= (pfir->ntaps - 1)) < 0)    offset = 0;

    // perform convolution
    for (k = 0; k < ndata; ++k, ++offset) {
        v = 0;
        for (i = 0; i < pfir->ntaps; ++i) {
            v += pfir->kernel[i] * pfir->data[offset + i];
        }
        d[k] = v;
    }
}
ここで、pfir->ldata は シフトバッファ(pfir->data[])のサイズで、pfir->ntaps はタップ数(カーネルバッファ pfir->kernel[] のサイズ)です。 カーネルは予め逆方向にセットされているものとして処理しています。 こうすることによって データをシフトバッファへ転送する処理が順方向で単純になって、結果的に2重ループ処理全体が速くなるようになっています。
シフトバッファを使う理由は、ストリームデータを連続的に畳み込んでいくためです。 固定長のデータを畳み込むためなら シフトバッファなど要りません。
今回SSE命令を使用して高速化した畳み込み処理のベースになったコードは以下のようになります。
typedef float afloat __attribute__ ((__aligned__(16)));

void    fir_process(FIR *pfir, int ndata, afloat * __restrict__ s, afloat * __restrict__ d)
{
    int     i, k, offset;
    afloat  v;

    // loop for all the data
    for (k = 0; k < ndata; ++k) {
        // shift buffer
        memmove (&pfir->data[1], &pfir->data[0], sizeof (afloat) * pfir->ntaps);

        // copy data to the shift buffer
        pfir->data[0] = s[k];

        // perform convolution
        v = 0;
        for (i = 0; i < pfir->ntaps; ++i) {
            v += pfir->kernel[i] * pfir->data[i];
        }
        d[k] = v;
    }
}
このコードでは カーネルは順方向にセットされているものとして処理しています。 代わりにデータが逆方向に入るように処理されています。 2つのコードを比べると分かりますが、後者は内側のループが単純でSSE命令で高速化することが可能になります。 しかしながら、データを逆方向に入れる処理にちょっと時間がかかるので、内側のループをSSE命令で高速化しても、全体としては前記のコード程は速くならないんですね。
で、今回は内側のループだけでなく データをシフトバッファへ転送する処理ごとSSE命令にしてやりました。 コードは以下のようになります。
typedef float afloat __attribute__ ((__aligned__(16)));
typedef float v4sf   __attribute__ ((vector_size(16))) __attribute__((aligned(16)));

void    fir_process(FIR *pfir, int ndata, afloat * __restrict__ s, afloat * __restrict__ d)
{
    int     i, k, ntap;
    v4sf    *pk, *p0, *p1, *p2, *p3;
    afloat  *g0, *g1, *g2, *g3;
    v4sf    a0, a1, a2, a3;
    afloat  *f0 = (afloat *)&a0;
    afloat  *f1 = (afloat *)&a1;
    afloat  *f2 = (afloat *)&a2;
    afloat  *f3 = (afloat *)&a3;

    // set pointers
    ntap = pfir->ntaps / 4;
    pk   = (v4sf *)pfir->kernel;
    p0   = (v4sf *)pfir->data[0];
    p1   = (v4sf *)pfir->data[1];
    p2   = (v4sf *)pfir->data[2];
    p3   = (v4sf *)pfir->data[3];
    g0   = pfir->data[0];
    g1   = pfir->data[1];
    g2   = pfir->data[2];
    g3   = pfir->data[3];

    // loop for all the data
    for (k = 0; k < ndata; k += 4) {
        // shift buffers
        for (i = ntap - 2; i >= 0; --i) {
            p0[i+1] = p0[i];
            p1[i+1] = p1[i];
            p2[i+1] = p2[i];
            p3[i+1] = p3[i];
        }
        g0[1] = g1[2] = g2[3] = g3[0];
        g0[2] = g1[3] = g3[1];
        g0[3] = g3[2];

        // copy data to the shift buffers
        g0[0] = g1[1] = g2[2] = g3[3] = s[k];
        g1[0] = g2[1] = g3[2] = s[k+1];
        g2[0] = g3[1] = s[k+2];
        g3[0] = s[k+3];

        // perform convolution
        a0 = (v4sf){0, 0};
        a1 = (v4sf){0, 0};
        a2 = (v4sf){0, 0};
        a3 = (v4sf){0, 0};
        for (i = 0; i < ntap; ++i) {
            a0 = __builtin_ia32_addps (a0, __builtin_ia32_mulps (pk[i], p0[i]));
            a1 = __builtin_ia32_addps (a1, __builtin_ia32_mulps (pk[i], p1[i]));
            a2 = __builtin_ia32_addps (a2, __builtin_ia32_mulps (pk[i], p2[i]));
            a3 = __builtin_ia32_addps (a3, __builtin_ia32_mulps (pk[i], p3[i]));
        }
        d[k]   = f0[0] + f0[1] + f0[2] + f0[3];
        d[k+1] = f1[0] + f1[1] + f1[2] + f1[3];
        d[k+2] = f2[0] + f2[1] + f2[2] + f2[3];
        d[k+3] = f3[0] + f3[1] + f3[2] + f3[3];
    }
}
先の2つのコードではシフトバッファは1つだけでしたが、新しいコードでは4つになっています。 これは、4つのシフトパターンを予め作っておき 一気に積和処理を行えるようにするためです。
だらだらと長いコードですが、このコードだと最初のコードの2.5倍、2番目のコードの3〜4倍速くなります。




2011年02月13日(日) 8/-2℃

Update: 02:03:04
音楽関係。。
昨日。。。てか、もう日が変わってしまったので(笑) 一昨日ですかね。 金曜日は、久しぶりに mihimaru GT を聴きまくっていました。 ヒロコさんの声は良いですね。 声を出した途端に空気が変わります。(^O^)きゃー
ヒロコさんは、私の萌えポイント3点をクリアしている唯一のアーティストです。(爆)*^^* その萌えポイントとは

  • 声が可愛くてちょいハスキー
  • 性格が天然
  • 綺麗・可愛い
です。 いやぁ、たまらんですな。

Update: 02:42:06
音楽関係2。。。
今日。。。てか、もう日が変わってしまったので(爆) 昨日ですかね。 土曜日は、ももいろクローバーを聴きまくっていました。 しかも、

  • 行くぜっ!怪盗少女
  • 走れ
  • オレンジノート
  • ココ☆ナツ
  • ミライボウル
の5曲の超リピートです。(笑) 良いですね、ももクロはイケイケで大好きです。(爆)




2011年02月11日(金) 2/1℃

Update: 12:10:20
今日はちょ〜雪だと聞いていたのですが、たいして降ってないし。。。。(笑)
がっかりだぁ。。。。

Update: 12:13:28
最近 MON 等の音楽チャネルでは、JPOPだと AKB48 ばっかりなんですよね。 いいかげんもういいわ。(笑)
嫌いではないですが(爆)、音楽としては退屈なので、他のをもっとかけて欲しいです。

Update: 12:29:37
先月前半にももいろクローバーの曲について書きましたが、先週末だったかな、去年の12月24日にあった初単独ライブの模様がCSテレ朝で放送されたので、録画して観ました。 目当ては新曲の「ミライボウル」です。
リップシンクでしたが、まぁあれだけ運動量のある振り付けだったら 当然だと思います。 てか、あれで声が出せたら 超人的な心肺能力です。(爆)
「曲の構成は平凡なAA'BC」と書きましたが、DメロとEメロもありました。 Dメロはヒップホップ系、Eメロは間奏(だけど歌あり)って感じでした。(笑) いやぁ、豪華絢爛盛り沢山&斬新で良いですね。 ももクロらしいわくわくする曲です。
今年の目標の2番目くらいに

  1. ももいろクローバーからは目を離さないぜッ!
ってのを入れておくべきでした。(爆)

Update: 12:37:28
一昨日「GCCでOpenMPを使ってもどういうわけかマルチスレッドになりませんでした」ってかきましたが、使っていたプラグマの記述が

#pragma omp parallel for
とするべきところ
#pragma omp for
としてしまっていたので、再度挑戦してみました。
・・・が、やっぱりどういうわけか駄目でした。 出直してきます。。。(笑)




2011年02月10日(木) 8/0℃

Update: 00:05:06
オーディオプロセッサ。
なんとかもう少し高速化できないか、引き続き検討中です。
で、今日は(てかもう日が変わってる(^^;) __builtin_ia32_mulps() って関数を使ってみました。 先日の日記の for ループの部分を

    v4sf  a, *p1, *p2;
    float *f = (float *)&a;

    v  = 0;
    p1 = (v4sf *)pfir->kernel;
    p2 = (v4sf *)pfir->data;
    for (i = 0; i < (pfir->ntaps / 4); ++i) {
        a = __builtin_ia32_mulps (p1[i],   p2[i]);
        v  += f[0] + f[1] + f[2] + f[3];
    }
    d[k] = v;
で置き換える感じです(実際はもうちょっと工夫しました)。
しかし、先日と同じ理由で、やっぱりこれまでの単純ループの方が僅かに速いです。
また出直してきます。。。(笑)

Update: 00:15:32
オーディオプロセッサその弐。
GCC V4.2 以降で OpenMP が使えるって事を知って試してみました。
今は、オーディオチャネル毎に畳み込み処理を意図的にスレッドにしてマルチコア処理しています。それを止めて 代わりに

    #pragma omp for
    for (c = 0; c < NCHANNELS; ++c) {
        fir_process (G_pfir[c], LBUFFER, pmed->buffs[c], pdst->buffs[c]);
    }
としてみました。 fir_process() が畳み込みの処理です。 fir_process() の中身は、上記の(先日の)ループ処理(+α)みたいになっています。
しかし、どういうわけかマルチスレッドになりませんでした。 勿論、コンパイルとリンクに -fopenmp オプションを指定しましたよ。 リンクに -fopenmp オプションを指定しなかったら 「スレッド生成に関連する関数が無い」とかいう感じのエラーが出たので、ちゃんと #pragma は効いているっぽです。。。
というわけで、これまた出直してきます。(笑)




2011年02月06日(日) 10/2℃

Update: 16:05:47
オーディオプロセッサ。。。というかGCC。
昨日オーディオプロセッサを公開しましたが、もうちょっと高速化できないかと検討中です。 特に FIR の畳み込み処理部分を速くしたいです。
GCCのオプションに vectorization という「SSE命令を使用して積和演算のループをベクター計算する」というものがあります。 当然今もオプションを指定しているのですが、全く効果が無いってことが分かりました。(ToT)
具体的には

    -O3 -ftree-vectorize -ffast-math -ftree-vectorizer-verbose=6
を指定しています。 -O3 を指定すると -ftree-vectorize もディフォルトでオンになるので指定する必要はないのですが、気分によって(笑) -O2 を指定することもあるので、いつもつけています。 -ftree-vectorizer-verbose=6 は、vectorization の処理メッセージを出力するオプションで、どのくらい効率化が図れたかが分かるようになっています。 メッセージからすると全く効果無しです。
で、調べたら、x86系CPUの場合 -msse/-msse2 オプションも同時に指定しないといけないらしいです:
    -O3 -ftree-vectorize -msse2 -ffast-math -ftree-vectorizer-verbose=6
で、このオプションを指定してコンパイルすると確かに vectorization が有効になっているようなのですが。。。
    ../COMMON/fir.c:170: error: alignment of array elements is greater than element size
みたいなエラーが出てしまいます。 バッファのアライメントに起因するエラーらしいのですが、対処方法不明です。
ネットで調べると 最新のでは2009年05月くらいの GCC のメーリングリストが出てきて バグ(?)としてレポートされているらしいのですが、どうやら今も修正はされていないらしいです。。。。orz
因みに、gcc のバージョンは 4.4.1 です。
インラインアセンブラを使用して SSE 命令で書き直しても良いのですが。。。せっかくCで書いているので、あまりやりたくないですね。 せめて対策方法でも分かれば。。。です。

Update: 18:27:43
GCC vectorization。
-msse/-msse2 オプションを使用しなくても vectorization 出来るという方法を見つけたので 試してみました。
因みにこの人の記事を参考にさせてもらいました。
で、変更は、fir.c の中の

    v = 0;
    for (i = 0; i < pfir->ntaps; ++i) {
        v += pfir->kernel[i] * pfir->data[i];
    }
    d[k] = v;
というループを
    v  = 0;
    p1 = (f4vec *)pfir->kernel;
    p2 = (f4vec *)pfir->data;
    for (i = 0; i < (pfir->ntaps / 4); ++i) {
        a.v = p1[i].v * p2[i].v;
        v += a.f[0] + a.f[1] + a.f[2] + a.f[3];
    }
    d[k] = v;
としました。 ここで、
typedef float v4sf __attribute__ ((vector_size (16)));
typedef union {
    v4sf v;
    float f[4];
} f4vec;
と定義されています。
このループは1サンプル(1/48000秒)につきタップ数分(2048回)るので、ちょっと高速化しただけで 格段に効率が上がるんです。 てか、オーディオプロセッサの負荷は殆どこのあたりの部分で生成しています。(爆)
しかし、残念ながら余計に遅くなってしまいました。 理由は、上記のループの前処理にあります。 今のオーディオプロセッサは上記のループは使用せず、前処理も含めて もう少し効率の良い処理を行っています。 つまり、前処理も含めると vectorization しても効率が上がらないって事ですね。
出直してきます。。。(爆)




2011年02月05日(土) 12/1℃

Update: 19:27:43
オーディオプロセッサ。
てか、2月になって初めての更新ですね。(笑)
やっとオーディオプロセッサのレポートを完成しました。
興味がある人は読んでやって下さい。(笑)