ESP32S3とV4220Mでデジタル音声処理デバイスを作る
ふとデジタル信号処理で電気・電子楽器用エフェクターを作ろうと思い立ち、ESP32S3というマイコンとV4220MというAD/DA変換ICを組み合わせて作ってみた話です。
Expand
デジタルで音声信号を処理する機器を作ろうとする場合、アナログ信号をデジタル信号に変換する処理(AD変換)と、デジタル信号をアナログ信号に変換する処理(DA変換)が必要になります。AD・DA変換はオーディオに限らずさまざまな分野で使われているため、これらを実現するためのデバイスは沢山あります。その1つが、秋月電子通商で購入できるCOOLAUDIOの「V4220M」というICです。このV4220MはAD変換とDA変換の両方の機能を備えるICで、販売価格は240円と、この手のデバイスとしてはかなりリーズナブルなお値段です。
V4220Mは同じくCOOLAUDIOの「V1000」というデジタルエフェクトICと組み合わせて利用することが想定されているようなのですが、「I2S」と呼ばれる音声向けの汎用データ送受信方式に対応しているため、ほかの信号処理プロセッサやマイコンと組み合わせることが可能です。最近ではデジタル音声処理を実行するには十分な性能を持ち、かつ小型で安価なマイコンも多く出回っています。今回はその1つである「ESP32S3」というプロセッサを搭載したSeeed Studioの小型マイコンボード「XIAO ESP32S3 Plus」とV4220Mを組み合わせて、AD変換およびDA変換処理を実装してみました。
まず今回紹介する回路を理解するための前提知識として、使用するICやマイコン、そしてこれらの間の通信で使われる「I2S」というインターフェイスについて解説します。あくまで簡単な説明なので、より詳しい知識が知りたい方はそれぞれの単語でWeb検索してみてください。
なお、デジタルエフェクターのようなデジタル信号処理を行うプログラムを作成するにはデジタル信号処理の知識が必要となりますが、さすがにそれをここで解説するのは大変なのでそちらについては割愛します。
V4220Mは前述のとおりAD変換とDA変換の両方の機能を備えたICです。デジタル化を行う装置は「コーダー(coder)」、それをアナログ信号に戻す装置は「デコーター(decoder)」とも呼ばれており、その両方の機能を持つことからこれらの単語を組み合わせた「codec IC」と呼ばれています。AD変換・DA変換ともに2チャンネルの同時処理(つまりステレオでの処理)が可能で、対応量子化ビット数は最大24ビット、サンプリング周波数は4~48kHzです。音楽CDで使われている圧縮無しデジタル音声は16ビット/44.1kHzなので、スペック上はこれらと同等以上の品質でのAD変換やDA変換が期待できます。
COOLAUDIOは昔(80~90年代ごろ)の音響機器で使われていたICを現代に復刻させて製造・販売していることで知られるメーカーで、このV4220MもCirrus Logicという信号処理ICメーカーがかつて製造・販売していた「CS4220」というICを復刻させたもののようです。そのため、型番こそ異なるものの、V4220Mの基本的な特性はCS4220と同等で、CS4220を使った回路はそのままV4220Mでも利用できるようです。
ちなみに、Cirrus LogicはCS4220の姉妹製品として、CS4221~CS4224というICも発売していたそうです。基本的なピン配置等はほとんど同じですが(CS4222のみ若干違う)、データシートを見る限りCS4220とこれら姉妹製品では下記のような違いがあるようです。
CS4221:SPIおよびI2Cインターフェイス機能を内蔵しており、マイコンから動作の設定が可能
CS4222:CS4221と同様にSPIおよびI2Cインターフェイスを内蔵するが分解能は最大20ビット固定、スレーブ動作のみ(外部クロックが必須)。廉価版ということ?
CS4223:CS4220と仕様的にはほぼ同じだが音質が強化されている?
CS4224:CS4221と仕様的にはほぼ同じだが音質が強化されている?
V4220MはCS4220のクローンですが、CS4223とも互換性はあるようで、CS4223を使用しているLINE6の「DL4」というディレイエフェクターでCS4223をV4220Mに置き換えても動作するというレポートがありました。
This is the first DL4 I ever got, bought broken almost 5 years ago. Despite working on DL4s for other people and my own MM4 repair I have
なお、V4220MのデータシートはこのCS4220/4221のデータシートを参考に作成されたように見えるのですが、以下のような違いがあります。
クロックの設定などに関する情報がCS4220とは異なっている
本来V4220Mには搭載されていない(CS4221専用の)I2C・SPIインターフェイスを使った設定機能の話が記載されている
そのため、DigikeyのWebサイトなどで入手できるCS4220/4221のデータシートも一緒に確認することをおすすめします。
ESP32シリーズはEspressif Systemsという半導体メーカーが通信機能を持つデジタル機器(いわゆるIoTデバイス)向けに開発したマイコンです。Wi-FiやBluetooth機能を内蔵しているため、ネットワーク接続機能を持つデジタル機器でよく使われているようです。国内では秋月電子通商や千石電商、マルツなどの電子部品を取り扱っているお店でESP32シリーズを搭載する小型マイコンボードを購入できます。
これらのマイコンボードは入手性が良く、価格も1,000~2,000円前後と安価なのですが、特にSeeed StudioのXIAO ESP32-S3シリーズはおよそ2cm四方の小型サイズながら最大240MHz動作のデュアルコアプロセッサを搭載し、かつ「PSRAM」と呼ばれる、SPI接続のRAM(容量8MB)も搭載されている点が特徴です。このPSRAMは通常のメモリと同様に利用できるため、大容量のメモリが必要な処理も余裕で実行できます。
また、ESP32シリーズ向けには「FreeRTOS」と呼ばれる、並列処理を考慮したOSをベースとするシステムが提供されており、(知識さえあれば)簡単にマルチコアを活用した並列処理プログラムを実装できます。さらにハードウェアレベルでI2Sインターフェイスをサポートしており、低い負荷でオーディオ入出力処理が可能です。
ESP32シリーズでの開発にはArduino IDE、もしくはESP-IDFという環境環境が利用できます。Arduino IDEではハードウェアの細かい部分を気にせず開発ができるので、マイコンに詳しくない人でも開発しやすい点もメリットだと思います。
V4220MとESP32S3マイコンの間では、「I2S」という形式でデータのやり取りを行います。I2Sでは4本の信号線を使用してデータの送受信やタイミング制御を行います。簡単に言ってしまうと、2つのデバイス間で次の4本の端子を接続し、適切に設定を行うだけでデジタル化されたオーディオ信号をやり取りできる、というものです。
データ入力(DIN):アナログ信号に変換するデジタル信号を入力する端子
データ出力(DOUT):アナログ信号から変換されたデジタル信号を出力する端子
ビットクロック(BCLK/CSLK):DIN・DOUT端子でのデータ入出力タイミングを制御するための端子
ワードセレクト(WS/LRCK):DIN・DOUT端子でやり取りしている信号がステレオの右チャネルの信号なのか、それとも左チャネルの信号なのかを示すための端子
なお、ビットクロックやワードセレクトはV4220Mのデータシートではそれぞれ「シリアルデータクロック(SCLK)」や「レフト/ライトクロック(LRCK)」と呼ばれていますが、以下ではESP32側での呼び方である「BCLK」や「WS」で表記しています。また、ここでいう「クロック」は(雑に説明すると)一定の決まった周波数でHIGH/LOWを繰り返す信号のことです。
DIN端子は接続相手のDOUT端子に、DOUT端子は接続相手のDIN端子に接続します。また、BCLK端子は接続相手のBCLK端子に、WS端子も接続相手のWS端子に接続します。このとき、CSLKおよびLRCK端子から信号を出力する側のデバイスを「マスター」、信号を受け取る側のデバイスを「スレーブ」と呼びます。今回使用するV4220MやESP32S3マイコンはマスター/スレーブのどちらで動作しますが、マスターもしくはスレーブでしか動作しないデバイスもあります。
I2Sでは上記の4つの端子とは別に、各デバイスで「マスタークロック(MCLK)」と呼ばれる、AD変換やDA変換処理も含めたシステム全体の処理タイミングを同期させるためのクロックが必要です。V4220Mは外部からマスタークロックを入力して使用することもできますし、発振子(いわゆるクリスタルなど)を接続してそこから自前でマスタークロックを生成することもできます。また、ESP32Sでは外部からマスタークロックを入力するか、もしくはマイコン全体で使用しているクロックからマスタークロックを生成することができます(この設定については後述)。
ESP32S3とV4220Mを使用したオーディオ信号のデジタル処理回路
続いては実際に作成した回路について紹介していきます。回路の全体像はこのような感じになっています。
マイコンボードにはSeeed Studioの「XIAO ESP32-S3-PLUS」を使用しました。ESP32-S3-PLUSは基板の縁に1.27mmピッチのパッドを設置することで、小型ながら20個の入出力ポートを利用できるマイコンボードです。さらに国内で無線機能を利用するための技術基準適合証明(いわゆる技適)を取得しているため、合法的に無線機能を利用できます。エフェクターで無線機能を使うことはあまりないですが、たとえばBluetooth経由でスマートフォンと連携して設定をカスタマイズする、といったことが実現できます。
ちなみにXIAO ESP32-S3シリーズは複数のモデルがありますが、出力インターフェイスが異なるだけで、搭載するプロセッサや機能はすべて同一です。そのため、同シリーズのほかのマイコンでも、基本的には同じような回路が利用可能です。
XIAOシリーズのマイコンボードはUSB Type-Cコネクタもしくは14番ピン(VBUSピン)から供給された5Vの電源で動作します。今回は一般的なエフェクターで使用する9V電源で動作させたかったので、3端子レギュレータを使って9Vから5Vを生成します。さらに、アナログ側の電源供給(+5VA)とデジタル側の電源供給(+5VD)は分離し、+5VAと+5VDはノイズを遮断する効果があるインダクタを介して接続しています。
マイコンボードのVBUSピンは内部的にはUSBコネクタのVBUS端子(+5V供給端子)に接続されているため、USBケーブルを接続した状態で外部から電源を供給しても問題が発生しないよう、ショットキーダイオードを介して+5VDに接続します。
マイコンボードの17番ピン(3V3ピン)は、VBUSピンもしくはUSBケーブルから供給された+5Vから生成した+3.3Vを供給するピンです。ESP32シリーズマイコンはインターフェイス電圧が3.3V、つまり3.3VがHIGH電圧となるため、マイコンに接続する抵抗器やスイッチ、各種インターフェイスなどにはここから3.3Vを供給します。この3.3Vはマイコンに直結されているため、ノイズが多い点には注意が必要です。
前述のとおり、ESP32S3とV4220Mとのデータのやり取りにはI2Sという仕組みを使用します。V4220MではそのためにDIN、DOUT、LRCK、SCLK端子が用意されています。一方、ESP32S3のI2S機能では任意のGPIO端子をDINやDOUT、WS、BCLK端子として使用できます。今回はGPIO3端子をDOUTに、GPIO4をDINに、GPIO5をBCLKに、GPIO6をWSとして使用することにしました。ちなみにどの端子にどの機能を割り当てるかはソフトウェア側で設定できるのですが、ESP32S3の一部の機能(たとえばADコンバータ)は一部のGPIO端子にしか割り当てできないため、使用したい機能が被らないように割り当てを考える必要があります。
また、V4220MではDIF0およびDIF1端子で入出力動作モードを、DEM0およびDEM1端子でフィルタ機能の設定を切り替えられるようになっています(詳細についてはデータシートを参照)。これらは固定値でも良いのですが、今回はマイコン側から設定できるよう、それぞれGPIO38、GPIO39、GPIO41、GPIO40端子に接続しています。これに加えて、マイコン側からV4220Mをリセットできるよう、GPIO1端子をV4220MのRSTN端子に接続しています。
それ以外の端子は、スイッチや可変抵抗器(POT)、LEDなどを接続することを想定してピンヘッダに接続しています。
V4220Mにはアナログ用の電源端子(VA)とデジタル用の電源端子(VD)があり、それぞれに5Vの電源を接続する必要があります。V4220MのデータシートではVAに+5Vを入力し、かつVDは2Ωの抵抗を介してVAと接続する例が記載されていますが、これはデジタル用の電源端子とアナログ用の電源端子を分離することでノイズの影響を抑える意図だと思われます。ただ、今回はデジタル側電源にマイコンボードも接続する設計にしたため、2Ωの抵抗を接続するとそこでの電圧降下が気になります(たとえばマイコン側で100mAの電力を消費した場合、200mV、つまり0.2Vの電圧効果が発生する)。そのため、前述のように今回は抵抗ではなくインダクタでアナログ側電源とデジタル側電源を分離しています。ぞれぞれの電源端子はノイズ対策および安定性向上のため、10uおよび0.1uのセラミックコンデンサを接続しています(いわゆるパスコンデンサ)。
ESP32マイコンは3.3V電源で動作するため、その入出力もすべて3.3Vで行われます。つまり、やり取りするデジタル信号では「1」を3.3V、「0」を0Vで表すことになります。V4220Mではデジタル信号の電圧を設定するためのVL端子があり、そこに3.3Vを入力することで、0V/3.3Vで信号の入出力を行えるようになります。こちらにも同様にパスコンデンサを接続しています。
V4220Mは外部からマスタークロックを入力することもできますし、発振子を接続してそこからマスタークロックを生成することもできます。ESP32のI2S機能はマスタークロック生成機能も提供しているので、そこで生成したマスタークロックをV4220MのXTI端子に供給すれば良さそうなのですが、試したところその場合どうやっても正常に動作させることができませんでした(動作はするがデータ送受信のタイミングがずれるようで、データを正しく送受信できない)。そのため、水晶発振子とコンデンサをXTOおよびXTI端子に接続して、V4220M側でマスタークロックを生成させています。
なお、V4220Mのサンプリング周波数は供給するマスタークロックもしくは発振子の周波数で決まり、V4220Mをマスターとして使用する場合はこの周波数の256分の1がサンプリング周波数になります。また、V4220Mをスレーブとして使用する場合はマスタークロックをサンプリング周波数の256もしくは384、512倍にしなければなりません。今回はV4220Mをマスターとして使用し、かつサンプリング周波数を48kHzに設定したかったので、水晶振動子の周波数として48kHz×256=12,288,000Hz(12.288MHz)を選択しました(この周波数の水晶振動子は千石電商で購入可能です
ちなみに、I2Sではマスタークロックをデバイス間で共有する必要はなく、それぞれのデバイス内で独立したマスタークロックを利用できます。ESP32のI2S機能ではマイコン内部でI2S用のマスタークロックを生成できるので、V4220M側で生成したマスタークロックをESP32S3側に供給する必要はありません(念のため、XTIもしくはXTO端子をGPIO43端子に接続できるようジャンパーは用意しています)。
デジタル信号に変換するアナログ信号は、V4220MのLIN+、LIN-、RIN+、RIN-端子に入力します。LIN+およびLIN-が左チャネル、RIN+およびRIN-が右チャネルで、それぞれ差動動作する構成です。LIN-とRIN-をGNDに接続して使用することもできますが、差動構成にしたほうがノイズ耐性は高くなるため、今回はオペアンプを使って入力信号にバイアス電圧を加えるとともに、反転した信号も生成してそれぞれを+および-端子に入力する構成にしています。バイアス電圧はCS4220データシート上の「Common Mode Input Voltage」(2.3V)に近い値(約2.27V)に設定しています。
この部分の回路はデータシートに記載されている参考回路ものとほぼ同じですが、+側出力は入力信号と同じ振幅に、-側出力は入力信号を振幅はそのままに反転させたものになるよう抵抗値を調整しています。V4220Mの入力は差動回路になっているので、その結果V4220Mが受け取る信号の振幅はオペアンプに入力された信号の2倍になります。V4220Mに入力できる信号は2.0Vrmsですが、一般的なエレキギターやエレキベースの出力はおよそ1Vrms未満なので、入力信号を2倍に増幅することでより高い解像度でのAD変換が期待できます。また、オペアンプの入力段の前にはトランジスタを使ったバッファ回路を入れています。そのため、この回路のL_INPUTおよびR_INPUTにはバイアス電圧が付与された信号を入力する必要があります。
なお、オペアンプは単電源で動作させている(-電源端子をGNDに接続している)ため、オペアンプによってはバイアス電圧(約2.27V)が低すぎて正常に動作しない可能性があります。そのため、今回はこの部分で使用するオペアンプとしてTIのTLV2365を選択しました。電源電圧は最大5.5Vと低めですが、RAIL-to-RAIL、つまり入出力ともに0Vから電源電圧までの範囲で正常動作し、かつスペック上はノイズも少なめの2回路入りオペアンプです。あえて2回路入りオペアンプを2つ使用する構成にすることで、右チャネルと左チャネルを完全に分離することでクロストーク(片方のチャネルの信号がもう片方のチャネルに混入すること)の低減を期待しています。
V4220Mのアナログ信号出力は、アナログ信号入力と同様に差動動作となっています。LOUT+とLOUT-が左チャネル、ROUT+とROUT-が右チャネルです。
一般的なDA変換では、その過程で本来の信号には含まれていない高周波ノイズが含まれてしまいます。そのため、出力信号にはローパスフィルタを入れてこれらのノイズをカットします。今回はデータシートに掲載されている2-Pole Butterworthフィルタ回路を使用しました。ただし、コンデンサや抵抗器の値は(特性は変わらないようにしつつ)入手しやすいものに変更し、また単電源動作用にGNDをバイアス電圧源に入れ替えています。さらに、入力部と同様にクロストークの低減を図るため、1回路入りのオペアンプを使用して右チャネルと左チャネルを完全に分離させています。こちらはバイアス電圧を十分に高く設定できるので(今回は4.5Vに設定)、オペアンプには入手しやすいOP07を選択しています。
この回路の出力信号はバイアス電圧を含んでいるため、最終的な出力段で直列にコンデンサを入れてバイアス電圧を除去する必要があります。
V4220Mとの接続に使用していないマイコンのGPIOピンやアナログ信号入力・出力、電源端子はピンヘッダに接続し、このピンヘッダ経由で外部とやり取りするようにしています。
また、この回路はマイコンとしてXIAO ESP32-S3-PLUSを使用する想定で設計していますが、これ以外のXIAOシリーズマイコンでの使用も考慮しています。その場合、利用できるピンは(電源ピンも含めて)1~14までの14本のみになり、V4220MのDIF0、DIF1、DEM0、DEM1ピンはマイコンから操作できなくなります。そのため、これらのピンに加える電圧(0Vもしくは3.3V)を基板上で設定できるよう、これらのピンにはDIPスイッチやジャンパーも接続しています。また、V4220MからMCLKを受け取ったり、逆にMCLKを送り込むためのジャンパーも用意しておきました。
今回の基板はノイズ対策のため、下記のような方針で設計しました。
4層基板を使い、内側の2層は全面をGNDとすることで(いわゆるベタGND)、デジタル部分の回路で発生するノイズの伝搬を抑える
デジタル回路とアナログ回路を基板上で混在させず、できるだけ同じブロックにまとめる
4層基板というと高価なイメージがありますが、最近では4層基板も安価に製造できるようになっています。ただ、JLCPCBで製造する場合、50mm×50mmを超えるサイズになると一気に製造費用が上昇します(それでも一昔前に比べれば安いですが)。そのため、基板サイズは50mm四方以下にするという方針で設計を行いました。実際にCAD上で部品を配置したパターンはこのような感じになります(最初期バージョンなので、一部回路図とは異なる部分があります)。
基板の上側にはアナログ回路、下側にはマイコンを含むデジタル回路を実装しています。また、何らかのトラブルでマイコンやV4220Mを破損させてしまっても交換しやすいよう、両者ともにピンヘッダ経由で接続することを想定しています(V4220Mについては直接基板上にはんだ付けできるよう表面実装のパターンも用意しています)。ただ、XIAO ESP32S3-PLUSの入出力端子をフルに活用しようとした場合、マイコン基板の縁にはんだ付けを行う必要がありるのですが、片側は1.27mmピッチの端子が13個、その反対側は1.27mmピッチの端子7個と2.54mmピッチの端子3個という変則的な構成のため、基板側も2.54mmピッチのピンヘッダと1.27mmピッチのピンヘッダを併用する構成としました。そのため、はんだ付けがかなり面倒なことになっています。
バイアス電圧生成部分(C1とC29)には電解コンデンサを使用しています。C29は一般的な円筒形の電解コンデンサですが、C1はスペースがギリギリだったので長方形型の表面実装電解コンデンサを採用しました。
また、アナログ信号の入力段と出力段のローパスフィルタ部分にはフィルムコンデンサを使用しています。今回使用する容量のコンデンサはあまり入手性が良くないのでスルーホール部品で実装していますが、コンデンサ自体のサイズ(特に高さ)が大きめなので、表面実装品を使ったほうが良かったかもしれません。これら以外のコンデンサはすべて積層セラミックコンデンサです。
今回の基板は表面実装部品を多用しているため、JLCPCBに基板製造とともに表面実装部品の実装も発注しました。安価に済ませるため、基本的に使用する部品は追加料金がかからない一般的なものを選択していますが、オペアンプのTLV2365だけは追加料金(約3ドル)を支払って実装しています。
届いた基板に、追加で必要な部品を手元で実装したものが最初に紹介した写真です。V4220Mは直接はんだ付けするのではなく、秋月電子のDIP変換基板を使ってピンヘッダ経由で取り付けています。
この基板を使ってデジタル信号処理を行うには、当然ながらESP32S3側でプログラムを作成する必要があります。こちらについて詳しくは別途紹介する予定なので、ここでは設定すべきことに絞って簡単に紹介します。
今回はV4220Mをマスター、ESP32S3をスレーブとして動作させます。この場合、ESP32S3側でI2Sで使用するピンのうちDOUTを出力用に、DIN、BCLK、WSを入力用に設定します。また、V4220Mはその電源投入時もしくはリセットからの復帰時にDOUT端子(つまりESP32S3のDIN端子)が47kΩでプルダウンされているとマスター、そうでない場合はスレーブとして動作します。ESP32S3の各GPIO端子には45kΩのプルダウン抵抗が内蔵されているので、別途プルダウン抵抗を接続する必要はなく、プログラム側で内蔵プルダウン抵抗の有効/無効を切り替えてV4220MをリセットするだけでV4220Mをマスターとして動作させることができます(後述)。
これに加えて、V4220MのRSTN、DIF0、DIF1、DEM0、DEM1に接続されている端子は出力に設定します。
V4220MのRSTN端子に接続されているGPIO端子の値0に設定すると、V4220Mはリセット状態になって動作を停止します。この状態でDIF0、DIF1、DEM0、DEM1端子に接続されているGPIO端子を希望する値(今回はDIF0とDIF1を0、DEM0とDEM1を1)に設定し、その後にRSTN端子に接続されているGPIO端子の値を1に設定することで、V4220Mが再起動されて指定した値が反映されます。
ちなみに、V4220MのRSTN端子が0になってからリセット状態になるまでは10ミリ秒、1になってからリスタートまでは50ミリ秒かかるそうなので、RSTN端子に接続しているGPIO端子の値を変えたあとはその時間以上待機する必要があります。
I2Sにはさまざまな方言(?)があり、デバイスによって微妙にフォーマットが異なるようです。ESP32のI2S機能はさまざまなデバイスに対応できるようになっているため、設定がやや複雑です。V4220Mも複数のフォーマットに対応しており、DIF0/DIF1ピンでどのフォーマットを使用するか選択できるのですが、今回は最もスタンダードとされる「DIF0=0、DIF1=0」(Philipsフォーマット)を使用します。これに対応する設定は以下のようになります。
ESP32S3側マスタークロックの生成源(SCLK):PLL240M
マスタークロックの倍率:サンプリング周波数の512倍
BCLKの周波数:マスタークロックの8分の1(サンプリング周波数の32倍)
data_bit_width(データビット幅):32ビット
slot_bit_width(スロットビット幅):32ビット
ESP32シリーズでI2Sを使うに当たっては、個人的にはこのパラメータ設定が一番難しいと感じました(自分も紆余曲折を経てこの設定値に辿り着きました)。それぞれの値は次のようにして決定しています。
接続するデバイス(今回はV4220M)側のサンプリング周波数と同じ値を指定します。
ESP32S3側マスタークロックの生成源(SCLK)
ここでの「SCLK」はV4220MのSCLKではなく、ESP32S3内部のクロック源(Source Clock)を意味しています。下記のいずれかを選択できます。
ESP32S3に接続された(マイコンボード上に設置されている)40MHzの水晶振動子(XTAL)
上記の水晶振動子で生成したクロックを4分割して生成した160MHzクロック
上記の水晶振動子で生成したクロックを6分割して生成した240MHzクロック
外部クロック(ESP32S3外で生成してESP32S3のいずれかのピンに入力されたクロック)
デフォルトでは160MHzからマスタークロックを生成しますが、より高い周波数のクロック源を使ったほうがより正確なクロック生成が期待できるため、今回は240MHzのクロックソースであるPLL240Mを指定しています。ただ、今回はESP32S3とV4220Mとでマスタークロックを同期させていないため、この値を変更したとしても動作や音質への影響はないとは思います。
マスタークロックの倍率(mclk_multiple)
ESP32S3側で使用するI2Sのマスタークロック周波数を指定します。サンプリング周波数の128/192/256/384/512/576/768/1024/1152倍のいずれかで選択します。今回はV4220MとESP32S3とでマスタークロックを同期させていないため、このマスタークロック周波数をV4220Mのマスタークロック周波数(今回のハードウェア構成の場合ではサンプリング周波数の256倍)と一致させる必要はありません。
V4220Mをマスターとして動作させる場合、次のBCLKの周波数によって選択できる値が決まります。
BCLK(V4220M側で言うSCLK)の周波数を「マスタークロックのX分の1」(Xは整数)という形で指定します。Xは8以上である必要があります。逆に言うと、マスタークロックはBCLKの(8倍以上の)整数倍でなければならない、いうことです。
V4220Mをマスターとして動作させる場合、BCLKはサンプリング周波数の64倍(64Fs)になります。そのため、ここで選択できる値とマスタークロック倍率の関係は下記のいずれかになります。
bclk_div=8、mclk_multiple=512倍(64×8)、マスタークロック24.576MHz
bclk_div=9、mclk_multiple=576倍(64×9)、マスタークロック27.648MHz
bclk_div=12、mclk_multiple=768倍(64×12)、マスタークロック36.864MHz
bclk_div=16、mclk_multiple=1024倍(64×16)、マスタークロック49.152MHz
bclk_div=18、mclk_multiple=1152倍(64×18)、マスタークロック55.296MHz
マスタークロックはクロックソース(40MHzもしくは160MHz、240MHz)から生成する都合上、その周波数がクロックソース周波数の整数分の1になるのが最適です(たとえばクロックソース240MHzの場合120MHzや80MHz、60MHzなど)。しかし、サンプリング周波数が48kHzの場合どうやってもそれは無理なので、この場合はできるだけ小さい値にすることでクロック生成時の誤差を減らすことが期待できます。つまり、この場合はbclk_div=8、mclk_multiple=512倍が最適値となります(この場合、240MHzの10分の1=24MHzと、9分の1=26.666…Hzのクロックを49:15の割合で組み合わせることで疑似的に24.576MHzのマスタークロックを生成する形になる)。
入出力するデータのビット数を指定します。一般的には使用する量子化ビット数(V4220Mの場合24)を指定しますが、次のslot_bit_width以下の値であれば必ずしも量子化ビット数に一致させる必要はありません。
ただ、ESP32S3のI2S機能では、データビット幅として24ビットを指定した場合、1サンプル分のデータを8ビット型(int8_t)の変数3つ単位でやり取りすることになります。いっぽうで24ビットのデータを処理する場合は32ビット型(int32_t)の変数を使用するのが一般的なので、受け取った3つの8ビットデータを組み合わせてint32_t型の変数に格納する処理が追加で必要になります。
また、ESP32S3のI2S機能ではこの値を24に設定した場合、マスタークロック倍率の設定に制約が発生します。これらの問題を回避するため、今回はslot_bit_withと同じ32に設定しています。この設定の場合、I2Sでやり取りするデータは32ビット単位(int32_t)になりますが、受信したデータの下位8ビットには0が格納され、また送信したデータの下位8ビットは無視されます。
「BCLK周波数(V4220MでいうSCLK)/(2 * サンプリング周波数)」を指定します。V4220Mをマスターとして動作させる場合、BCLK(SCLK)はサンプリング周波数の64倍(64Fs)なので、その半分の32を指定します。(64*Fs/(2*Fs) = 32)。
今回は2チャンネル(ステレオ)で入出力を行うのでステレオに指定します。
ステレオ入出力なので左と右両方を使用するよう指定します。
slot_bit_widthと同じ値を指定します。
PhilipsフォーマットではWS(LRCK)の値が変わってから1クロック後にデータの最初のビットが送信されます。この1クロックの遅延がビットシフトです(つまりビットシフトあり)
ビットシフト後、すぐにデータ送信を行うので「左詰め」を指定します。
I2Sではビッグエンディアン(最上位バイトが左側に送信)でデータのやり取りを行いますが、ESP32は内部的にリトルエンディアンでデータを処理します。そのため、ここではリトルエンディアンを指定してエンディアンを自動変換してもらいます。
Philipsフォーマットでは高位ビットを先に送信します。
これら以外の設定でも動作する可能性はありますので、もし細かく挙動をチューニングしたい場合は試行錯誤してみると良いかもしれません。
ESP32S3のI2S機能では、プログラム側で用意したバッファを介してデータのやり取りを行います。データの送受信はリアルタイムで行われるわけではなく、あらかじめ指定した単位(=バッファサイズ単位)でまとめて行われます。また、今回の設定では1サンプル当たり8バイト(1チャンネル当たり32ビット=4バイト、ステレオなのでその2倍)のバッファが必要になります。つまり、たとえばバッファサイズを3840バイトに設定した場合、480サンプル(=3840/8)分のデータをまとめて入出力する形になります。サンプリング周波数が48kHzの場合、480サンプルは10ミリ秒分のデータに相当するので、入力されたデータを読み取って処理して出力するまでに、最低でも10ミリ秒の時間が必要になります。バッファサイズを小さくすればこの遅延時間は短くできますが、その分エフェクト処理に掛けられる時間は短くなる点には注意が必要です。
また、入出力バッファには右チャネルと左チャネルのデータを交互に格納します。そのため、畳み込み処理など時間軸に対して連続した処理を行う場合は、インデックス指定の際に注意が必要です。
実際に今回作成したデバイスでエフェクトペダルを作ってみたのですが、こちらについてはまた長くなりそうなので次の機会に紹介します。しばしお待ちください!