
iOSデバイスで高速フーリエ変換を実装する方法を紹介します。この記事では、まずはじめに一般的な高速フーリエ変換(FFT)であるCooley-Tukey法をSwiftで検証し、その後Accelerateというライブラリによる実装方法も併せて紹介します。
こんにちは。wat(@watlablog)です。この記事ではSwiftによる高速フーリエ変換について色々まとめてみます!
この記事の目標
Cooley-Tukey法によるFFTコードを自分で書いてみる
ここしばらく筆者はiPhoneアプリ制作をしてみたいと思い、基礎文法のチェックや録音の仕方、音声再生方法や画面遷移の方法を個別の記事で取り上げました。信号処理アプリを作る目標を立てており、この分野ではかかせない周波数分析機能に着手するのがこの記事の内容です。
WATLABブログでは以下の記事で前回離散フーリエ変換(DFT)をPythonやSwiftで実装してみました。
・Pythonで検証しながらSwiftで離散フーリエ変換を実装する
そして次に高速フーリエ変換(FFT)のアルゴリズムをPythonで学びました。
・Pythonで高速フーリエ変換をCooley-Tukey法で書く
慣れもあるかもしれませんが、Pythonはこのようなコードを書くのが本当に楽です。先ほどの記事で基本的なFFT関数の作り方を学んだので、この記事ではまず同様にCooley-Tukey法をSwiftに移植してみます。
AccelerateでFFTを使ってみる
ただ、Pythonで実感したのは自分で実装したFFT関数よりもNumPy等の外部ライブラリを使った方が圧倒的に高速だということでした。基本的なアルゴリズムを学ぶために自分でコードを書いてみるのは非常に良いことですが、実用には既存のライブラリを使った方が良いと思います。
そのためこの記事では一度Cooley-Tukey法を書いてみた後に、SwiftのAccelerateというライブラリを使ってFFTを使う方法も学びます。
動作環境
この記事のコードは以下の開発環境で動作を確認しました。
Mac | OS | macOS Ventura 13.2.1 |
---|---|---|
CPU | 1.4[GHz] | |
メモリ | 8[GB] | |
Xcode | Version | 14.3(14E222b) |
Swift | Version | 5.8(swiftlang-5.8.0.124.2 clang-1403.0.22.11.100) |
iPhone SE2 | OS | iOS 16.3.1 |
SwiftでCooley-Tukey法によるFFTをフルスクラッチするコード
事前準備
まずは自作のFFTコードから書いてみますが、動作検証のためにいくつか事前準備を行います。
データ点数を指定して配列を作成する関数
FFTのアルゴリズムを効果的に使用するためにはデータ点数が2の冪乗でないとなりません。Swiftでメソッドがあるかどうか探せなかったので、まずはデータ点数を自由に指定するために以下のlinspace関数を作りました。Pythonのnp.linspace()を真似たのですが、これでFFTコードの検証がしやすくなります。
サンプル波形を生成する関数
上記データ点数を指定するlinspace関数を使って、サイン波を作るwavePoints関数も用意しておきます。サイン波は周波数や時間長を変更できます。また、コードの検証としては直流成分も確認したいため、dcで0[Hz]の時の振幅を定義できるようにしています。
複素数構造体を定義
Swiftには標準で複素数型がありません。そのためstructを使ってComplex構造体を定義します。詳細は離散フーリエ変換の記事で書いた通りです。
FFT関数(Cooley-Tukey法)
こちらがSwiftで書いたCooley-Tukey法によるFFT関数です。先ほど紹介したPythonによるFFTの記事と同じ書き方をしているので、Pythonユーザーは是非両コードを見比べてみてください。
全コード(コピペ動作OK)
UI部分を含めた全コードを以下に示します。freqLimitはなくても動作しますが、グラフに表示させる周波数を絞ることを目的としています。グラフへの表示は以下の記事で紹介したChartを使っています。
・SwiftUI:iOS16から追加されたChartsでグラフを作成
実行結果
下図が実行結果の例です。Buttonをタップすると波形が表示されます。これはwavePointsで生成された時間波形をfft関数で高速フーリエ変換した結果です。指定した通りのピークがでました。

freqLimitを外したコード、もしくはfreqLimitの上限を最大まで持っていった場合は下図になります。fft関数で計算された値は鏡像ピークまで入っている結果です。

SwiftのAccelerate/vDSPでFFTをするコード
全コード
それではAccelerateというライブラリのvDSPを使ってみます。上記のコードをvDSPを使って書いたコードを以下に示します。
先ほどもデータ数が2の冪乗数でない時の処理はしていませんが、vDSPは自動的にゼロパディングによる自由なデータ数の処理が自動的に有効になっています。しかし振幅計算時に振幅補正係数を自分で書かないといけません。今回はSwiftになれていない筆者は少し面倒だったので、2の冪乗数でないデータが渡された場合に.alert()で警告文を出し計算しないという手段をとりました。
実行結果
こちらが実行結果です。

データ点数(let result = wavePoints(N: 8191, length: 10))とNを2の冪乗数でない数を指定すると、以下の図に示すメッセージを出しています。

まとめ
今回はここまで!
Swiftを使ってフーリエ変換するコードを、まずはCooley-Tukey法をフルスクラッチすることで実装してみました。一度Pythonで検証していたといっても、やはりSwiftだと慣れていないので時間がかかりますね。
そして次にAccelerateというライブラリをimportし、vDSPメソッドでFFTを使ってみました。ライブラリを使うことで少し速度UPしたように感じました。
次回は録音再生アプリにFFTを実装する部分を書いてみたいと思います。
SwiftでもFFTをかけることができるようになりました!
Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!