2021年11月28日

LaVoixski@I2Sを使用せずにMCUとDACを接続する方法について

dacHandlerは、MCUに内装されているI2Sを使用せずに、オーディオ用のDACと通信を行う目的で開発したシステムで、MCUの外部に32bit✕32stepのレジスタ群を設置することで、諸々の制約が発生するライブラリやDMAに頼らずに、独自のタイミングで通信を行うことができる。 取扱うデータのフォーマットは出力先のDACに依存するために、予め2の補数にオーディオデータを変換しておく必要がある。

”2の補数”でオーディオデータを扱う記事を検索すると、対 Decimal / Binary 変換に関心が集中しているようで、Binaryから2の補数に加工する際のオーディオの出力制御に絡んだトピックを見つけることは難しい。

これは自分の検索能力(特に英語)が拙いことを踏まえても明らかな傾向だと思うのだが、流通しているオーディオ用ADCの出力が概ね2の補数フォーマットなので、それを踏まえた常識的な反応なのかもしれない。


さらに検索を行った結果、I2S/Raspberryに絡む記事が散見されたものの、ライブラリを介さずにデバイスを直結する場合に必要とされるプロセスに関して書かれた記事を発見することは遂に出来なかった。

さて、2の補数フォーマットのオーディオ・データを扱う勘所は、負極側の扱いとなるのだが、概念的に-1が-128より大きいと解っていても、最初は2の補数によって示される 0xff と 0x80 といった数値に実感が沸かないために、ゼロクロス時の不連続な表現に戸惑わされることになった。

つまり、-1と0の間には概念上の「壁」が存在するのだが、ここで実際にデータを掛け合わせてレベルを設定するタスクが絡んだ場合、まず概念上のゼロポイントをフルスイングする波形データの振幅の中点に設定して、波形データを極性で二分することが最初の工程となる。

重要なのは、出力の最終的なデータ幅(分解能)にデータが纏められるまでは、MSBに添付される極性を表す「符号」を決定してはいけないことで、この事に理解が及ばなかったために、相応な廻り道を強いられることになった。

実験の結果到達した加工のプロセスを以下に示す。

Screen Shot 2021-12-02 at 8.41.21.png

1)まず、音声データの極性を決定するためのゼロクロス・ポイントを規定し、ゼロポイントの上下でデータを分離する。

2)正極側のデータからゼロクロス・ポイントに規定した値を引いて、最小値を0に設定する。

3)出力レベルを決定するデータを正負それぞれのデータと掛け合わせる。この際に念の為桁上りを防ぐためのリミッターを添付してもよい。

4)負側のデータにはオフセットが生じるので、レベルデータのフルビットから設定する値を引いたものをオーディオデータのフルビット値に掛けることでオフセット値を生成し、これを負側のデータに加算して、嵩上げを行う。

5)11bit✕12bit=23bitの音声データが確定した後、DACの仕様で求められる32bit幅に合わせてデータを8bit左にシフトする。

6)最後に負側のMSBに極性のフラグを立てて、2の補数フォーマットへの変換が完了する。

後から振り返ってみれば、何ということはない常識的な手順を踏んでいるだけなのだが、工程6)でMSBに添付する「符号」の意味を読みきれず、24bit環境で極性を決定しようとしたことが、錯綜の原因となっていた。

以上、「解っている人達」にとっては自明のことをつらつらと書き綴って来たのだが、抽象表現に弱い自分は、実験を繰り返すことで、やっと理解を進めることができた。

追記:

2022年7月に、Transition をコントロールするデータ幅を12bitに拡張している。

http://audiohologram.sblo.jp/article/189696350.html
posted by Yasuski at 10:11| LaVoixski

LaVoixski@PCM5102Aから出力された波形の記録

まず、波形の選択を行ってみたが、何故かPitchがチューン出来ない場合があるのと、サイン波以外の波形が正確に再現できない状態が観測された。 青は、TransitionMode時の出力波形。



ArpeggiatorとLFOによって、オシレーターをドライヴした結果。
こちらは、サイン波を選択している為に、表示に問題は認められなかった。



試しに、Arpeggiator経由のPitchDataで、オシレーター(単体)をドライヴしてみたが、こちらは元の波形がよく判る結果となった。

(疑似)アンテナ入力よりも波形が安定しているのはデータに含まれるリップル成分が皆無なためと思われるが、この結果から「GPTによるPitchの読み出し機構」に問題がある可能性が出てきた。



MixOutを出力に接続した。波形を観測する限り、概ね良好な内容。 フェードインを繰り返す毎に、プリセットされた波形グループを切り替えている。

Transitionによるオシレータの乗り換えに伴って、シームレスに波形が変化していくことが判る。

懸案だったリミッターは動作不良が発生したために機能をカットしているが、「半波整流」的なエフェクトが不足していると感じられた場合は、定数を再検討した後の復活もあり得る。



実際に出力した音声を確かめたところ、基音が異常に高い。 いまのところ原因は不明だが、修正を行わなければならない。

Volume関連の動作は問題なさそうで、強制Fade/In機能とLFOによるトレモロ効果を確認できた。



オーディオフォーマットの変更によって破綻していた波形表示を復旧した。 若干の問題が残されているが、これでほぼOKだろう。



音声に関しては、chordEditModeのコード編集ページで検証したところ、固定周波数モードのオシレーターの挙動に問題はみられなかった。



強制Fade/In環境で、Transitionの推移を記録したテイク。



いい感じに音が暴れている。

LCD側の表示もスムーズに行われているようだ。



これは、ノイズの波形。



残念ながら、今回新規に導入したGPTによるPitchDetectorに問題があるようなので、これから検証を進めていく。
posted by Yasuski at 07:58| LaVoixski

LaVoixski@DACの実装を開始する

9月発注のDACが11月9日になってやっと届いたので、早速脚を取り付けた。 実装した端子は、基盤上面に直接配線用のpinを差し込めるようになっている。

IMG_20211109_233133126.jpg

が、例によって暫くの間放置状態に陥っていた。 月蝕後に到着したDACユニットへの配線を始めたものの、FPGAのファームを試行錯誤で書き換え続けていた影響で、配線すべきポートの位置が混乱してしまっている。

もちろん、配線の変更は記録してあるが、

WS002128.JPG

DacHandlerの出力は、タスクの軽減を鑑みた結果、6chに削ることになった。 他の変更点は、DACを直接ドライヴするLRCKの追加、入力データのタイミング・クロック/CKWをpin21にリプレイスしている。

混乱を避けるために、一旦FPGA周りの全ての配線を取り除いたついでに、

IMG_20211121_152034198.jpg

DAC基板をFPGA基板に寄せて配線長を短縮、EMI対策としてFPGAをMCU側に隣接させる配置を行った。

IMG_20211121_182628453.jpg

FPGAに至る白い配線は、クロック生成用に配置したMCUから供給される256FSのクロックラインで、DAC の"SCK"を経由させている。

IMG_20211121_225839743.jpg

DACの動作を確認する作業の前に、とっちらかっていた測定器の配線をDIPソケットに接続する形にまとめて、ポートの引き出しがより明確になるように対策を行っている。

IMG_20211120_135302452.jpg

とりあえず、音声出力を視認できているが、Rch(青)の挙動がおかしく、レベル設定周りのコードを調査しなければならない。

Screen Shot 2021-11-23 at 6.26.57.png

挙動が甚だ怪しいものの接続を確認できたので、試しにPITを波形ジェネレーターとしてリニアな波形を生成してみた。

Screen Shot 2021-11-24 at 16.44.40.png

当初発覚したDACの不調は、MCU側の接続端子を間違えていたことが原因だったが、何れにしても特定の閾値を超えた時点からの出力波形の挙動が怪しい。 これがプログラム由来のものなのか、FPGAの性能限界に依るものなのかハッキリとは判らず、今一度プログラムの構成を考え直さなければならなくなってしまった。

Screen Shot 2021-11-26 at 10.49.10.png

例示している波形は、典型的な符号の読み損ないによるエラーだが、これを修正することから始めなければならない。

ここで、2'Sコンプリに出力をデータ変換する手順に関しておさらいをすると、、、

12bit幅のWaveTableのレベルを12bit分解能のVolumeでコントロールする場合、まずWaveTableに0ポイントの"しきい値"を設け、閾値以上の場合は波形データから「ゼロクロス」に設定した値を引いた結果を、未満の場合はXORを掛けて全bitを反転させた後に1を加えた結果を出力する。 この場合に問題となるのは、レベル値を乗算した後にMSBの符号の位置がズレることで、これを防止するために、閾値未満の分岐後にMSBをマスク、乗算後にMSBの符号を復活する方法が考えられる。

現行ヴァージョンのプログラムが抱える問題点は、この極性を表すMSBの符号のズレを失念していたことにあり、この点を修正してゼロクロス信号を生成することを考えているのだが、FPGAのスピードがMCUから出力されるクロックのスピードに追いつけていない可能性もあり、その場合はDAC←MCUの直結を検討することになる。

Screen Shot 2021-11-27 at 4.19.24.png

問題は、レベル設定時に発生するオフセットで、これを解消する方法を考えなければならない。 この時点で、音声出力にアサインしているデータはオシレータの出力を合算したものだったが、より単純な波形にアサインを変更することで、明確な観測を行える筈だ。

DACチップに供給するシステムクロックをデバイスに依る自励に切り替えてみたが、波形に変化は見られない。本来はサイン波の筈が、何故かこうなってしまう。

Screen Shot 2021-11-27 at 11.52.54.png

ここで初心に帰って、タイマーによってカウントアップした数値を波形として使用する環境で実験を行ったところ、やはりFPGAとのハンドリングに問題があることが判明した。 このように、ゼロクロス・ポイント付近で、先頭の符号を読み落とす現象が観測されている。

Screen Shot 2021-11-27 at 14.01.59.png

FPGAとのハンドリングを行うクロックの幅は”dsb”の数で調整するのだが、今回の実験ではLATCH後の遅延時間をdsbを12発分、各クロックの振幅を、dsbを4発分に調整している。

フォーマットが「2の補数」なオーディオデータの扱いは、24bitのオーディオデータをローカルで32bit化した後に、MSBの符号を添付することで解決することが出来たが、このままではMix時の信号の扱いに問題が生じてくる可能性がある。

Screen Shot 2021-11-27 at 14.15.34.png

実験では、まずカウンタのLSBから12bitを音声データとして取り扱うことにした。

データは11bitにあたる2048をゼロクロスポイントに設定し、それ以上を正の値、以下を負の値として取り扱う。 正負の極性は分岐によって振り分けられる。 正極の値はオフセット値を引いた11bit幅に、負極には加工を行わず(+1は不要か)フルビット状態が−1となり、ゼロは最小値として扱う。

音声データ(実機ではウエーブテーブル)にレベル値を掛けた結果は、23bitのオーディオデータにMSBの符号が追加された24bit幅となるが、DACのデータフォーマットは32bitなので、まずデータに8bit分のbitShiftを行った後、負極側のMSBのみにbitSetを行って、負極を表す符号を添付している。

Screen Shot 2021-11-27 at 14.16.06.png

負極側の波形との段差を解消するために、オフセットを付加する方法を色々と試してみたものの、レベル設定値による変動を解消できず、良い策を思いつけない。 11bitフルの場合は巧く繋がるのだが、それ以外はどうやっても段差が生じてしまう。

Screen Shot 2021-11-27 at 14.58.20.png

画像は、アウトプットをフルスケールでスイングさせた状態とレベルを半分にした状態で、何れもオフセットの修正を行っていないが、レベルを減衰させた方の波形はオフセット分だけ不連続な部分が出来ている。

また、極限値付近で発生しているリンギングが気になる。

Screen Shot 2021-11-27 at 16.51.44.png

上の波形は0x6ff、下が0x7ffのレベル設定値を基本波に掛けた状態。 数値が割り切れる0x7ffの方は綺麗に繋がっているが、0x6ffの方は段差が気になる。 段差は中央値0x7ffから離れるにつれて大きくなるが、中央値を超えた場合には差分が閾値を超えて、下方にオーヴァーシュートしてしまう。

Screen Shot 2021-11-27 at 16.57.47.png

レベルを0x6ffに設定した時の段差をクローズアップしたもの。 実測で約4bit分のズレだが、当然ながら減衰率によってオフセット値は変動する。

Screen Shot 2021-11-27 at 22.16.12.png

いろいろと試行錯誤を行った結果、オフセットの解消は12bitの段階ではなく24bitモードで行うのが正解だった。 画像は、レベル設定を極端に変えた2波を記録している。

Screen Shot 2021-11-27 at 22.29.22.png

プログラムの16進数の部分(0xaff/0x2ff)に関数を割り当てることで、動的制御が可能になる。

Screen Shot 2021-11-27 at 22.38.00.png

ゼロ・クロスポイントを拡大した図。 20us/20mvのスケールで観察しているが、段差は確認できない。

Screen Shot 2021-11-27 at 22.44.09.png

綺麗に直線が出ている。

Screen Shot 2021-11-27 at 22.49.27.png

更に波形を拡大しても、ゼロクロスポイント周辺の波形の段付きは見られなかった。

Screen Shot 2021-11-27 at 22.59.53.png

オフセット設定周りの構造を整理したので、これを他のパートにも応用していく。

Screen Shot 2021-12-02 at 8.41.21.png
posted by Yasuski at 06:37| LaVoixski