SyntaxHighlighter

2011-12-23

Interface1月号の内容を試してみた

12月18日にKinect(など)ハッカソンというのがあったので参加してきました。
ちょうどInterfaceの1月号にKinectの特集が掲載されていたので、それを読んで勉強していました。勉強したのは、「Kinectのマイクロホン・アレーによる音声信号処理」(p.112-116)です。まわり全員がほぼカメラと遊んでいる中、たぶん一人だけマイクと戯れたいたみたいです。。。自分は天邪鬼的性格らしい。

自分の能力不足でいろいろはまったけど、一応記録しておきます。


git

まず、サンプルをgitで取得、というところで躓いてしまいました。gitクライアントなど自分のWindowsに入っていません。てか、gitよく分かんない。。。
参照URLにはzip形式のファイルがあることにはあるけど、サンプルのVisual Studioのプロジェクトがないので、自分でプロジェクトの設定をしないといけない。

サンプルのコンパイル

Visual Studioのプロジェクトファイルを自分で作ることにしたけど、C/C++のプロジェクトの作り方をすっかり忘れてしまっていました。戒めのために、書いておくと、
  • ソリューションエクスプローラー内の対象のプロジェクトを選択して、プロパティを選ぶ
  • 構成プロパティ>C/C++>全体 の「追加のインクルードディレクトリ」で、openrafのヘッダファイルのあるincludeディレクトリを追加
  • 構成プロパティ>リンカー>全般 の「追加のライブラリディレクトリ」に、openrafのLibファイルのあるlibディレクトリを追加
  • 同じく、リンカー>入力 の「追加の依存ファイル」に、openraf.libを追加
  • ビルド後、実行前にexeファイルと同じディレクトリにopenraf/bin以下にあるdllファイルがあるか確認、なければコピー
リンカーの設定で、ずっとdllのディレクトリを選択していて、かなり詰まってました。。。

スピーカーの設定

入力としてKinectのみを使用するサンプルは動いたけど、出力側のスピーカーを使用するサンプル(raf_ds.cppとか)が最初うまく動かなかった。raf_openを実行しても認識してくれなかった(raf_valid_device(da)がfalseになる)。
どうやら、スピーカー>再生デバイス>構成>オーディオチャネル の値を4チャンネルにしていたのが悪さしていたらしい。設定をステレオにすると認識してくれるようになった。サンプルプログラムは2チャンネルの出力デバイスを検索しているみたい。

謎の実行時エラー

出力も使用してraf_enable_stftを使用するサンプルでたまに、libfftw3-3.dllでエラーが出てたり出なかったりした。理由は全く分からない。自分の環境がMacのBootCampを使用しているからかも???



ちょっとした音源定位

何はともあれ、Kinectの4つのマイクからデータが取得できるようになったらしいので(本当に正しくデータが取得できているのかは確認しようがないけど。。。)、最後に http://www.enjoy.ne.jp/~k-ichikawa/soundLoc3.html を参考に、相互相関を使った音源方向を推定するプログラムを書いてみました。既にKinect SDKにあるのに、完全に車輪の再発明状態です。
テストなので、normで割っているとか、かなり無駄も多いし、怪しいところ満載な感じですけど。。

/* pとかcは、openrafのサンプルコードから */
static double p[4][3] ={
 -0.115,0,0,
 0.035,0,0,
 0.075,0,0,
 0.115,0,0
};
static double T = 15; // 室温
static double c = 20.055*sqrt(T+273.15); // 音速

static double VOLUME_TH = 0.05; // 音源定位を実施する音の大きさのしきい値


double correlation(float* X, int N, int channel, int c1, int c2, int t) {
 double R = 0;
 for(int n=0;n<N;n++){
  int tmpT = t+n;
  if (t+n < 0) {
   tmpT = t+n + N;
  } else if (N < t+n) {
   tmpT = (t+n) - N;
  }
  R += X[channel * n + c1 ] * X[channel * (tmpT) + c2];
 }
 return R;
}

double calcTheta(int Tau, int c1, int c2, double* theta) {
 if (Tau == 0) {
  *theta = 0;
  return 1;
 }

 double diffTime = Tau / 16000.0;

 double delta = c * diffTime;
 double length = sqrt(pow((p[c2][0]-p[c1][0]), 2) + pow((p[c2][1]-p[c1][1]), 2) + pow((p[c2][2]-p[c1][2]), 2));
 if (length > fabs(delta)) {
  *theta = asin(delta/length) * 180.0/M_PI;
  return 1;
 } else {
  return 0;
 }
}

double calcLikelyTheta(float* X, int N, double* norm, int channel, int c1, int c2, double* theta) {
 double maxR = 0;
 int maxTau = -1000;
 for (int t=-N/2; t<=N/2; t++) {
  double R = correlation(X, N, channel, c1, c2, t)/(norm[c1]*norm[c2]);
  if (maxR < R) {
   maxR = R;
   maxTau = t;
  }
 }
 int ret = calcTheta(maxTau, c1, c2, theta);
 if (ret) {
  printf("R:%e, t:%2d theta: %5f Volume:%3f\n", maxR, maxTau, *theta, norm[c1]);
 } else {
  printf("R:%e, t:%2d theta: N/A Volume:%3f\n", maxR, maxTau, norm[c1]);
 }
 return ret;
}

/* フレーム処理を行うコールバック */
void callback(const raf_vector& input, raf_vector& output, void* info)
{
 float* X = (float*)input.data;
 float* Y = (float*)output.data;
 int channel=input.M; int N=input.N;

 double norm[4] = {0,0,0,0};
 for (int i=0; i<N; i++) {
  for (int j=0; j<4; j++) {
   norm[j] += pow(X[channel * i + j], 2);
  }
 }
 for (int j=0; j<4; j++) {
  norm[j] = sqrt(norm[j]);
 }

 if (norm[1] > VOLUME_TH) {
  int NumT = 3;
  double theta[3];
  int NumOK = 0;
  int chpair[3][2] = { {3, 0}, {2, 0}, {1, 0} };
  for (int i=0; i<NumT; i++) {
   int ret = calcLikelyTheta(X, N, norm, channel, chpair[i][0], chpair[i][1], &(theta[NumOK]));
   if (ret) {
    NumOK++;
   }
  }
  if (NumOK >= 2) {

   double meanT = 0;
   for (int i=0; i<NumOK; i++) {
    meanT += theta[i];
   }
   meanT /= (double)NumOK;
 
   double sigma = 0;
   for (int i=0; i<NumOK; i++) {
    sigma += pow(theta[i] - meanT, 2);
   }
   sigma /= (double)NumOK;
   printf("***** Theta mean:%f sigma:%f *****\n", meanT, sqrt(sigma));
  } else {
   printf("!!!!! not enough thetas !!!!!\n");
  }
 } else {
//  printf("!!!! too small volume !!!!\n");
 }

 /* ESCキーが押されたらコールバックを抜ける */
 if (kbhit()){
  if(getch()==0x1b) raf_cb_complete(info);
 }
}

書いたは良いけど、会場が静か過ぎて、音を出すのがはばかられて、テストできませんでした。。。

お家へ帰ってから確かめてみた結果は、下記のとおりです。まあ、動いていないこともないかなあ、という感じです。コマンドプロンプトなど貼り付けても理解されないだろうけど。



今回の感想

  • 音系の製作は、ハッカソンのような人が集まる場所でやるものではない。
  • 画像に比べると、音は扱いにくいかなあ、やっぱり。画像だとOpenCVという有名どころで簡単に扱えるようになっているし、応用もいっぱい考えられているけど、音だと、どんなライブラリが結局良いんだろうか??応用も音声認識と音源定位以外になにかあるのかしら?
  • OpenNIの方が発展性がありそうな気がしてきた。。

0 件のコメント:

コメントを投稿

Related Posts Plugin for WordPress, Blogger...