RPI-Vocoder PCB

概要

Raspberry Piをbaremetal(OSなし)環境で使用してヴォコーダを作りました.

ソース・回路図はGitHubをご覧ください.

ヴォコーダがどんなものかについては「FPGAで作るヴォコーダ」を見ていただければと思います.

動機

いろいろとRaspberry Piは触ってきていていたのですが,直接のきっかけとしては,「BareMetalで遊ぶ Raspberry Pi」という電子書籍の発売が挙げられます.

最初にも書きましたが,baremetal(ベアメタル)というのは,本来OSを動作させるような環境をOS無しで使用することを指します.上に書いた本はRaspberry Piをbaremetal環境で動かすための基本的な手順が紹介されています.

一通り読んで遊んでみたのですが,せっかくFPU付きの700MHzで動くCPUを自由に使える環境が手に入ったわけですから,計算パワーを必要とする作例を,と考えて,いつもながらのオーディオネタからヴォコーダを取り上げてみました.

実装

ハードウェア

ヴォコーダを実装するにはオーディオの入出力の双方が必要となります.残念ながら,Raspberry Piには標準ではオーディオ出力のみが存在し,入力が存在しません.さらに,Raspberry Piの標準オーディオ出力はPWMで,音質もそれほど良いものではありません.

しかし,Raspberry PiのGPIOピンヘッダには,搭載しているSoCのI2S(Inter-IC Sound)入出力端子が引き出されているので,これを使うことができます.このI2S出力を利用して,Raspberry Piをハイレゾ・ネットワークオーディオプレーヤーとして使用するする事例がよく見られます.今回は,I2S出力だけでなく,入力についても活用します.

Raspberry PiのI2S入出力には,

  • オーディオデータ出力
  • オーディオデータ入力
  • ビットクロック
  • ワードセレクト(L/R選択)クロック

が用意されています.さて,ここで問題となるのが,近年一般的なオーディオDAC/ADCで必要な,ビットクロックの256倍といった周波数のシステムクロックがRaspberry Piの出力に存在しないことです.さらに,ビットクロックとシステムクロックの位相関係が一定である,すなわちバラバラの信号源から得られる信号ではなく,何らかの方法で双方の間で同期が取れていることを要求するICが多いという問題もあります.また,ワードセレクトクロックについても,入出力で同じものを使えるようにする,ということが要求されます.

このシステムクロックの問題は結構厄介で,よくある対策としては,

  1. PLLでビットクロックからシステムクロックを逓倍して生成するDAC(PCM510xなど)を使う
  2. システムクロックとビットクロックの位相関係を問わないIC(Cirrus社の一部のICなど)を使う
  3. IC側でシステムクロックに同期したビットクロックを生成する

といったものが挙げられます.今回はDACだけでなくADCも使用したいことと,ワードセレクトクロックの同期を取りたい,ということから,3.のIC側でシステムクロックに同期したビットクロックを生成するという方法を取り,さらにDACとADCが一体化しているコーデックICである,TI社のTLV320AIC23Bを使用しました.

TLV320AIC23Bは水晶振動子用の発振回路を内蔵しているため,IC側でビットクロックやワードセレクトクロックなどを生成することが可能です.

厳密には,TLV320AIC23Bのワードセレクトクロックとビットクロックは,ADC用とDAC用の2つの端子が存在し,それぞれが別のサンプリング周波数で使用された場合にも対応できるようになっているのですが,手元の環境でテストした所,ADCとDACで同じサンプリング周波数を使用している場合にはADCとDACの各々のクロックは完全に同期しているようなので,これを利用して,ADC用のクロックのみをRaspberry Piに供給し,Raspberry PiとTLV320AIC23Bの間の通信を行っています.(このような動作についてはデータシートに記述がないため,条件によっては上手く動かない可能性があります.)

また,TLV320AIC23Bは設定レジスタにSPIかI2Cでアクセスする必要がありますが,今回はRaspberry PiのI2Cを使って設定を行うよう配線しました.

回路図に関してはGitHubに中心部だけの回路図を用意してあります.

実際にはトップの写真のようにTLV320AIC23Bの部分はプリント基板化したモジュールを,その他にもマイクアンプや楽器入力用のステレオ入力をモノラルにまとめるミキサー回路等を追加してあります.

ソフトウェア

処理の流れ自体はFPGA版と同じなので,MUSIC-DSP Source Code Archive等を参照しながら実装するだけなのですが,一つデバッグに苦労したところがありました.

最初は音声が出るのですが,数秒後に音声が止まってしまうというバグでした.また,常にマイクの前でアーと喋っていると出力が止まらず正常に動く,ということも判明しました.

いろいろと追っていくと,どうやら例外が発生して例外ハンドラに飛んで止まっている,ということのようで,しばらく原因が分からなかったのですが,最終的に原因はVFP(ARM11のFPU)の非正規化数の扱いにありました.

非正規化数の話をするためにはまずIEEE 754浮動小数点フォーマットの話をしなければいけないかと思いますが,要点だけかいつまむと,

  • ごくごく小さい値については通常とは違う数値表現で表す必要がある
  • 通常とは違う数値表現なので計算時の扱いも異なる

のが非正規化数です.非正規化数の計算は通常の浮動小数点値と異なるので,現在のx86 CPU等でも通常の浮動小数点値の計算に比べてかなり時間がかかります.

ARMコアにおいては,非正規化数の演算等の一部の演算については,すべてをVFPのハードウェアで処理するのではなく,例外を発生させて,ソフトウェアでそれをハンドルして処理する,という仕様になっています.従って,この条件に合致してしまう演算を例外ハンドラの用意なしに行うと,プログラムが正しく動作しない,ということになってしまうわけです.

運悪く,ヴォコーダのエンベロープディテクタは段々と減衰し0へと漸近するような挙動を示すために,ちょうど非正規化数が発生するような処理となってしまっていたようです.また,声をずっと出していると非正規化数が発生しないため,プログラムは動き続けていた,ということのようでした.

これに関して全く対策が無いわけではなく,VFPの設定によっては,例外を発生させずすべてハードウェアで処理することが出来ます.これをRunFastモードと呼び,

  • VFPをflush-to-zeroモード(非正規化数を0と変換するモード)にする
  • VFPをdefault NaN(NaNをすべてdefault NaNとして扱う)モードとする
  • すべてのVFP関連例外bitを0とする

という条件を満たすことで,非正規化数の扱いは若干変わりますが,例外を発生させずに処理を進めることが出来ます.今回のアプリケーションは厳密な数値計算ではないので,(実際に非正規化数対策として適当なところで0に丸めるのはよくある話です)この設定で問題ないはずです.

スタートアップルーチンにRunFastモードに設定する処理を追加した所,正常に動作し続けるようになりました.baremetal環境でVFPを使う場合には気をつけたほうがよいかもしれません.

今後の課題

まだマイク入力に対してコンプレッサーやリミッタが無いため,オーバーフローが発生するとバリバリと派手に音が割れるわりにマイクと口の位置関係がかなりクリティカルなので,その辺の機能を追加することを考えています.

その他にも昔のヴォコーダだとコーラスが付いていたりするようなので,せっかくRaspberry Piの広大なメモリが使えるのでコーラスに限らず,リバーブなど,ディレイをベースにしたエフェクタをいろいろと実装したいと考えています.

また,常に使うためにはケースの作成もする必要があるので,UIを考えるとともに,ケースの設計もやっていかねば,と思っています.