最近Androidアプリの開発をいまさらながらはじめました。今回作ったのは、音声をAndroidスマートフォンに入力し特定の音が鳴っていないか検出するアプリ。FFTで周波数解析して、周波数領域でピーク検出をするのが簡単そうです(時系列データだとノイズの影響が大きく難しそうだった)。それで、AndroidでFFTって、何がいいのかな…と調べると、標準APIのVisualizerの話がよく出てきました。…が、単純にFFTするだけなら、JTransformsが使いやすかった、という記事です。
AndroidでFFTをするには
周波数解析といえばなんといってもフーリエ変換(フーリエ解析)です。フーリエ変換とは、かいつまんで言うとあらゆる波をsin波とcos波で表現しましょうというものです。1秒間で一回りするsin波を1Hzと表現し、1秒間に10周するsin波を10Hzといいます。そういう無数の○○Hzを組み合わせると、人の音声など複雑な波形も作ることができますよと、そういう話です。
フーリエ変換を実際にコンピュータで用いるときはFFT(Fast Fourier Transform,高速フーリエ変換)というアルゴリズムが用いられます。アルゴリズムなので実装は色々あります。ライブラリもいっぱいあります。何を使うか、悩ましい。
FFTとは、DFT(Discrete Fourier Transform, 離散フーリエ変換)を高速にしたものです。DFTとは、フーリエ変換を離散的に表現したもので、離散があるように連続フーリエ変換もあります。しかし、コンピュータは突き詰めると0と1です。連続的な表現は擬似的にしかできません。したがって、コンピュータでは連続ではなく離散フーリエ変換が使われます。
まぁ細かい話は置いといて、要は音声信号をFFTで周波数解析したいけれど、Android初めてだし、自分で実装とか嫌だし、どのライブラリを使えばよいのかなと、そういう話です。
標準のAPIがあるが…
調べていると、どうやらAndroidは標準のAPIでFFTができるらしい。なんと素晴らしい。「Visualizer | Android Developers」です。ふむふむ……ふむ……。
うーん、正直ちょっと扱いづらいなぁ、と。私はshort型の配列に取り込んだデータをFFTしたいだけなんですが、オーディオ用のライブラリということもあってか、オーディオのオブジェクトから作成する感じで取り扱いが面倒臭い。あとgetFftメソッドで「The capture is an 8-bit magnitude FFT」ってマジかや……今回の目的からは8-bitでもいいっちゃいいんだけど、うーん。
なんだかなーと思っていたところ、「Android/JavaでFFT(高速フーリエ変換) | Androidアプリ開発日誌 | WEB道」の記事でJTransformsというライブラリの使用例がありました。おお、なんてシンプル。配列にデータ突っ込んで引数に取るだけ、まさにこんなのを求めていました。たいへんに参考になりました。
ということで、「JTransforms - Piotr Wendykier」よりjarファイルをダウンロードして、「androidstudioにライブラリ用のjarファイルを追加する方法 - うさがにっき」にしたがいインポートしました。パッケージはorg.jtransforms.fft.FloatFFT_1Dを使わせてもらいました。変換後はデータの並びが実数、虚数、実数、虚数…でサンプリング周波数の半分まで(サンプリング周波数の半分までしか使えません。詳しくはサンプリング定理で調べる)しっかりとありました。
ということで、AndroidでFFTするならJTransformsが使いやすいですよという話でした。ちょっとマニアックだろうか。
初Androidアプリ開発。色々なことが初めてづくしで苦労しました……音声を取り込んでFFTはそれなりに需要もあるように思うので、そのうちまとめて記事にしたいですね。
コメント
コメント一覧 (2件)
お世話になっております。
このたびは貴重な記事を拝見させていただきまして、大変ありがとうございました。
実際に実装したところで問題が発生してしまい、そのご相談としてコメントいたします。
AndoroidをUSBデバッグで開発していまして、JTransformsでFFT解析したところ、音をミュートにして静かな部屋でスペクトルを可視化したところ、不要なスペクトルが検出されてしまいます。スペクトル解析を行う上でかなり邪魔な存在です。これを除去する方法をご存じでしょうか。いろいろと「android fft noise」等で検索したりデバイスを変えたりしましたが一向に問題は改善されません。
もし解決法をご存じでしたらご教授いただけますと幸いです。
計測対象や除去したいノイズの種類やアプリの目的次第とは思いますし、音声信号処理の専門家でもないので、あくまでご参考までに…
まず2つ大まかな方向性があるのかなと思います。
Androidの既存のライブラリでさらっとやれる範囲でやるのか、自分で頑張って処理を書くのか、、
前者の場合だと、背景雑音除去もあるみたいですね。
NoiseSuppressor | Android Developers
https://developer.android.com/reference/android/media/audiofx/NoiseSuppressor
標準のライブラリでいければ、一番楽なんですけどね。
ただまぁ特定のユースケースに用いられること前提と思われ、この記事なんかがそうですが、原始的でいいから自分で処理を決めたいということもあります。
その場合、まず音声信号処理として背景雑音除去の手法を調べて、それをAndroidで適用するにはどうしたらいいか、という流れですかね。
検索のキーワードとしては「信号処理 雑音除去」等あるでしょうか。英語ならsignal processing noise reduction
あと実際の手法として素人の自分がパッと思いつくところを書きますと、もう既に試されていたらすみませんが、まずはバンドパスフィルタなどのフィルタ処理ですかね、、
特に欲しい周波数領域や不要な周波数領域など音の特徴がわかっていれば、フィルタ処理が有効かなと。
あとは平均化でしょうか。前処理として時間領域で加算平均してからFFTしたり、逆に周波数領域で加算平均したり。あーでもこれは音声だとあんまりよくないのかな、、