TadaoYamaokaの開発日記

個人開発しているスマホアプリや将棋AIの開発ネタを中心に書いていきます。

ピッチ検出モデルのSwiftF0を試す(その2:Windows MLでリアルタイム推論)

前回Pythonで試したSwiftF0の推論を、WindowsC++プログラムでリアルタイムに行う方法を検討した。

SwiftF0のモデル

SwiftF0のモデルは、ONNXで公開されているので、それをそのまま使うことを検討する。

Netronで、ONNXモデルを確認すると、STFTもONNXで処理されるようになっている。
STFTオペレータはopset 17から追加されている。

窓関数もこのSTFTオペレータで適用される。

そのため、音声の前処理は必要なく、そのままモデルに渡せばよい。

WindowsでのONNXの推論

Windows App SDK に含まれる Windows ML は、ONNXRuntimeを内蔵している。

そのため、ヘッダーをincludeするだけで追加のライブラリなしで、ONNXRuntimeを使って推論処理を実装できる。

#include <winml/onnxruntime_cxx_api.h>

実装

GPT-Codex-5.3を使って、リアルタイムピッチ解析を実装した。

SwiftF0でリアルタイムにピッチ検出するプログラムを、WinPitchDetectionフォルダにあるC++/WinRT(WinUI3)のプロジェクトに実装してください。

### 条件
- WASAPI Capture のイベント駆動で音声入力する(WASAPI共有モード)
- サンプリングレート16kHz、モノラル、ビット深度24 or 16
- SwiftF0のONNXモデルはAsstesに追加済み(ms-appx:///model.onnx)
- Windows App SDK に含まれる Windows MLでONNXの推論を行う
- 推論処理はPythonの実装を参考にする
- 横軸を時間、縦軸をピッチ(音階スケール)としてグラフを描画
- 録音中グラフを横スクロールする
- SwapChainPanelで描画領域を作る。文字の描画にはDirectWriteを使用
- ディスプレイのDPIのスケールを正しく処理すること
- D2D COM オブジェクトを確実に解放すること(winrt::com_ptr<>を使用)
- ピッチ検出処理は再利用可能なようにソースを分割して実装

### 参考
- samples/swift_f0: SwiftF0の推論のPython実装
- SwiftF0のリポジトリ: https://github.com/lars76/swift-f0

はじめ、弦をミュートした後に、オクターブエラーが発生した。
Pythonの推論処理でも試したが、同様に発生する。

無音区間はモデルに渡さない方がよいことがわかったので、音量しきい値を追加することにした。
-50dBでカットするとオクターブエラーが発生しなくなった。

実行結果

ギターの各弦のピッチを検出した結果は以下の通り。

安定して検出できている。

まとめ

Windowsで、SwiftF0のONNXモデルを推論する処理を実装した。
Windows App SDK内蔵のWindows ML(ONNXRuntime)を使うことで追加ライブラリなしで実装できた。

次は、チューナーアプリのアルゴリズムをSwiftF0に置き換えたい。

追記

その後、チューナーアプリに組み込んでみたが、キーボードのピアノの音を入力したところ、音階により20cent近くの誤差があることがわかった。
誤差が大きくチューナー用途では実用的とは言えない。
SwiftF0は教師データ作成に活用して、チューナーに直接組み込むのは見送ることにする。