ちょいかじりの音声認識

発端

音声認識が普通にできるようになってずいぶん立ちます。ちゃんと音声認識やるなら、Windowsにも、あるいは認識ソフトとかもあるからそれを使えば、ずいぶん認識率の高い環境を作れます。

それは、そうなのですが、
どうやっているかの一端ぐらいは知りたい!
まぁ、イッチョカミの性ですかね。

ツールを使えばそれなりにできるけど、それぞれの意味を実感したい。新しいことをするというより、いろいろな資料の追認かな。

ということで、言語から。
何でもいいんだけど、画面に図を描く関係から最初は馴染んだ言語のほうがいいかなってことで

言語 Java JDK1.6
環境 Eclipse 3.7
Swing
入力 wave file

って環境で実施。

準備

  • まずは、音声ファイルがない。ネットから取得した講義とかのフラッシュ動画があるから、そこからWaveファイルを抽出。
  • JDKのドキュメントによると、javax.sound.sampled.* あたりに音声ファイル関連のクラスがあるので利用
  • ファイル形式とフレーム取り込み

データが16ビットだったので、2バイトをどうやって数字にするのかで躓く。
バイト型なのに負の数が入っているから、マスクかけるとかしてからシフトしないと行けないんだろうなぁ。C言語と違うところかな。

    frame[j] = audioBytes[i + j * 2 + 1] << 8 | audioBytes[i + j * 2 + 0];

    frame[j] = audioBytes[i + j * 2 + 1] * 256 + audioBytes[i + j * 2 + 0];
  • グラフ化の記述はあまり苦労しない。というか、適当に手抜き。とりあえず出るところまで作成。

音声認識(1)

  • 資料いろいろ見てみると、
  1. FFTで周波数分析
  2. スペクトルの上位成分を除去
  3. ピークを周波数の低い方からF1,F2という感じで2つ取り出す

で取り出した、F1とF2のそれぞれをX/Y軸としてグラフにすると(母音)は音素によって
固まりとなるらしい。

なので、まず、FFTを使う。
FFTは、基底が2のべき乗じゃないといけないので、データ数から2を基底にした数字を求める。
FFT自体は複素数対応だったけど、とりあえず、実数部分にセット。取り出すときも実数部分のみ。
虚数部分は無視!

音声認識(2)

FFTを使うときに気づいていたのですが、FFTは領域が繰り返すことを前提としている。
今回は、適当な範囲で区切っているので、領域の開始と終了で一致もしないし繰り返しもしない。

すると、ぶちんと切れることになるので、高周波部分にすごいノイズが乗る。

これってどうするんだろうと見ると、窓関数なるものを使うらしい。
領域の両端でゼロになるような関数を適応して非周期性のデータをFFTで使えるようにするのが窓関数らしい。・・・なるほどそれで窓関数っているのか。
それにしても、8192個のFFTなのに結構早い。1秒どころかほとんど瞬時。昔、1024個のFFTで1秒なんて言ってたのが夢のようなスピードだわ。これなら、リアルタイムでFFTってのも可能だわな。

音声認識(3)

窓関数にハミング関数ってモノを使う。説明によると、音声認識みたいなものではよく使われているとのこと。
で、スペクトル、出るには出たけど、よく見る形と全然違う。
どうやら、縦軸(強度)の方は、対数として扱うみたい。
早速変更
お、出たでた

音声認識(4)

次に特徴(フォルマントという)抽出
スペクトルはそのままだと、ガタガタなので、スペクトルから高周波成分を除く。

  1. スペクトルをFFT適応
  2. FFTの内容から適当な部分以降の成分を0にする(データが領域で左右対称なので前の方と後ろのほうを残して残りをゼロにする)
  3. 今度はFFTの逆変換をしてスペクトルに重ねる。

これで、高次のガタガタがない線になるので、ピークを抽出して、周波数の低い方から2つ取り出す。

次回へ

なんとなく雰囲気はわかった。
後は、解析する範囲を音を確認しながら指定できるようにしないといけないので、GUIの作成。
で、音ごとに解析して、フォルマントを整理。ってことかな。

JAVAだけでも結構出来るもんだな。