公開日:2019/10/19
title.png

7th Season


#7-10 WAVファイルを再生する(回収編)

 前回の記事で、おおよそ必要な検討は終わっているので、今回は成果を回収して行きます。

回路の設計

 まずは、SDカードI/Fの回路図を示します(下図)。以前、「#6-5 microSDカードI/Fを自作する」で作った回路の使い回しです。

ar96_schem.png


 今回追加になるMCP4725モジュール(12ビットDA変換器)と、音声信号出力部の回路図は以下の通りです。

ar7a_schem.png

 MCP4725モジュールは、3.3Vで使いますので、Arduinoと通信するためのI2C信号線には、レベルシフタが必要です。

※前回検索して見つけたMCP4725のデータシートより、各端子に印可可能な電圧の絶対最大定格がVCC+0.3Vと規定されているので、VCC=3.3Vで使用する時に、SCL,SDA端子に5V信号を接続すると、定格違反です。

 MCP4725モジュールから先の回路は、アナログの音声信号が通るので、Arduino等デジタルノイズを発生する回路とは、電源(Vcc,Vdd,GND)を分離します。上の回路図中、青破線で引いた境界線で、アナログ部とデジタル部を分離しています。
 アナログ側には、以下の様な電源モジュールから給電します。

ar26_acpow.jpg

 MCP4725には、電源端子が1組しかない(アナログ/デジタル共通)ので、完全に分離することは無理でしたが、これは止むを得ません。

 MCP4725モジュールの出力電圧は、最大3.3Vですので、5V電源を使うLM358であっても、十分フルスケール(3.3V)まで信号を通すことができます。

※オペアンプLM358は、Rail-To-Railではないので、5V電源で使う場合、3.3Vまでしか出力することができません(スペックより。実力では約3.9V程度)。入力信号が3.3V(最大)なので、ぎりぎりフルスケールまで出力できるということです。詳しくは「#6-4 オペアンプを使って増幅回路を作る」参照。

制御プログラム

 キーとなる部分の解説は、前回記事で済んでいますので、以下に完成版プログラムをリンクしておきます。

  完成版プログラム:play_wav.ino

  コマンド仕様:

WAVファイルを鳴らしてみよう

 Windowsのカードリーダなどを使って、SDカードへ適切なWAVファイルを転送しておいてください。演奏可能なWAVファイルは、以下の通りです。

 以下、実験中の様子です。

ar7a_expr.jpg

 だいぶ大掛かりな回路になりました(1^^;)。
 ただ、SDカードI/F回路、音声データ出力回路、に関しては、別記事で過去に作っておいた、ミニブレッドボードの回路を崩さずに置いてあったお陰で、筆者の環境では、見た目ほど大変ではありませんでした。
 使い回し可能な回路が手元に無くて、0から起こそうとすると、そこそこ大変かと……。

 大きなブレッドボードには、アナログ系の信号を扱う回路を集めて、Arduinoおよびロジック系回路とは電源を分離してあります。

  実験サンプル:【 】内はシリアルモニタからの送信データ
  【who】
  OK: play_wav ver.1.00 (12 bit dac)

  【pwav,ar7a_spl.wav】
  DBG: fmt=1, 1, 22050, 22050, 1, 8
  OK: play_wav: 2856128[us]/12845[smpl]=222[us/smpl](4497[Hz])

  WAVファイル:

 何とか、聞き取れました\(1^o^)/。ちょっと、ノイズがうるさいな……。
 再生後に出力される情報は、再生したWAVファイルのフォーマット情報と、再生に当たってDA変換器にデータを出力できた時間間隔(出力レート)、その逆数である出力周波数です。

 音声データのビット幅が8ビット/16ビットのケースと、ステレオ/モノラル(チャンネル数)のケースの組み合わせで4通りのデータを準備して再生してみたので、以下表に出力レートと周波数の計測結果をまとめておきます。

WAVファイル 出力レート
[μs]
出力周波数
[Hz]
備考
ビット チャンネル
8 1 248 4,025 ノイズに混じって、何とか聞き取れる
8 2 357 2,803 原音を知っていると、それらしい
音は聞き取れるが、厳しい。
16 1 369 2,708
16 2 1,050 952 ほぼ、ノイズ。聞き取れず。

 以下、雑感です。

WAVデータのフォーマット変換方法

 実は、Windowsとかで再生させると、8ビット1チャンネルのデータは、相当音質が悪いのですよ……。なので、恐らく、皆様のお手元にあるWAVファイルは、大抵、16ビットなのではないでしょうか?8ビット1チャンネルデータにしないと、Arduinoでは使い物にならないので、適切な変換ソフトウェアを使って変換してもらえば良いのですが、拙作sPeachで可能なので、説明しておきます。
 インストーラは、ダウンロードコーナから入手可能です(あるいは、ベクターのライブラリからどうぞ)。

出力レート(処理時間)の考察

 8ビット1チャンネルWAVファイルの再生時、出力レート=248μsでしたので、この数値からスタートします。以下の通り、I2C通信速度の考察が必要となります。

 I2Cの通信に掛かる物理的な時間は、「#7-9 WAVファイルを再生する」で示した図(以下再掲)より、Fastモード400kHzの場合で、約80μsと推定しました。

ar79_mcpi2c.png

 しかし、この他に、実行されるWireライブラリの処理時間が見積もられていません。そこで、I2Cへデータを送信するdac12_write()関数の、Wire.beginTransmission()〜Wire.endTransmission()までをコメントして、【pwav】コマンドを実行してみたところ、出力レートが248μsから95μsに短縮されました。勿論、MCP4725へデータは送信されていないので、音は鳴りません。
 この短縮した時間248μs−95μs=153μsがWireライブラリ(I2C通信時間含む)の処理時間だ、と言うことです。物理的な送信時間80μsに対して、73μsものオーバヘッドが付いていました(意外と大きいな……(1^^;))。

 SDカードの読み出し速度については、前回考察済みで、約35μs/バイトと見積もってあります。

 以上より、出力レート248μsの内訳は以下の通りと推定できます。

 どこを潰すと高速化できるでしょうね……。もうだいぶ叩いた後なので、これ以上は、ライブラリの内部に踏み込まないと、ちょっと無理かもしれません。もっと高速なMCUを使うとか、一番確実で好きですけど……(1^−^)/。

4ビットDA変換器(手作り)を使う

 「#7-6 DA変換器を作る」で手作りしたDA変換器(下図)も使用できる様に、プログラムを組んでおきました。

ar76_dac4.jpg

 プログラムの先頭付近に、以下の様な#defineがコメントされていますので、コメントを削除してコンパイルすると、4ビットDA変換器(手作り)を使うようなプログラムになります。

  //#define USE_DAC4BIT

 4ビットDA変換器を使う様にコンパイルすると、MCP4725モジュールへはデータを送らなくなるので、回路を変更する必要があります(以下図)。

ar7a_schem2.png

 4ビットDA変換器(手作り)では、デジタル電源とアナログ電源が別々なので、綺麗に電源を分離することができました。
 MCP4725モジュールは、電圧軸方向の解像度(12ビット)に余裕があったので、3.3Vフルスケールに対してデジタルボリュームでVP-Pを調整して使いましたが、4ビットDA変換器では、デジタルボリュームにするには解像度が低すぎます。そこで、デジタルボリュームは無効(初期値を×1倍)にしておき、代わりにVREFとAGNDに抵抗を挟んで出力電圧範囲を狭める設計にしました。

※「#7-6 DA変換器を作る」で作った4ビットDA変換器では、220Ω×16個の直列抵抗ですので、合計約3.5kΩです。VREFとAGNDに10kΩづつ抵抗を挿入したので、出力電圧範囲は、およそ1.4〜1.9[V]で、VP-P≒0.5Vとなります。これが音量になるので、抵抗値R1=R2で調整してください。

 4ビットのパラレルI/Oで制御できるお陰で、DA変換器へのデータ出力は、ほんの数命令(一瞬)で終わってしまうため、一気に出力レートが上がります。

  実験サンプル:【 】内はシリアルモニタからの送信データ
  【who】
  OK: play_wav ver.1.00 (4 bit dac)

  【pwav,ar7a_spl.wav】
  DBG: fmt=1, 1, 22050, 22050, 1, 8
  OK: play_wav: 2855900[us]/26882[smpl]=106[us/smpl](9413[Hz])

  録音データ:

 MCP4725(12ビット)に比べると、かなり粗い4ビットDA変換器なのに、12ビットのMCP4725と比べても、そん色無いほどの音で聞こえます(ノイズはさておき)。このレベル(Arduinoによる再生)だと、電圧軸方向の解像度より、時間軸方向の解像度の方が、支配的なのかもしれませんね。

ノイズの考察

 注意深く聞いてみると、MCP4725の録音データの方には、「リリリリリリリ……」と、聞こえるノイズが混じっています。喋り終わった直後の無音部分に耳を澄ませてみてください。4ビットDA変換器の録音データの方では、それが聞こえません。
 喋り終わった直後の無音部分では、DA変換器の出力はほとんど変化しないはずです。つまり、「階段状のカクカクした波形」は出力されていません。にもかかわらず、「リリリリリリリ……」と言うノイズは聞こえているので、I2C通信に起因するデジタル的なノイズではないか?と推察しています。
 4ビットDA変換器の方はというと、無音部分ではデジタル値も固定値となり、パラレルI/Oの信号変化もありませんので、無音部分のデジタルノイズが発生しないのだと思われます。

 逆に、4ビットDA変換器の方は、音が鳴っている間の雑音が大きく聞こえます。DA変換器に送る信号が4ビットパラレルであること、信号変化の周波数が出力周波数(約9.4kHz、可聴域)程度であることから、I2C(400kHz、可聴域外)に比べて、可聴域内のノイズが大きくなっているのだと考えられます。

 ここで、もう一度、オリジナル/MCP4725版/手作り版を聞き比べられるように、3つのWAVファイルを並べておきます。

 階段状の波形によって発生するノイズを警戒して、LPFを通してみたりなど、対策したのですが、結果的には、DA変換器に対するデジタル値の通信に関する、デジタルノイズが支配的な様で、全く効果がありませんでした。デジタル信号は、VP-Pが大きい(3.3Vまたは5V)上に、信号の変化速度(立ち上がり/立ち下がり時間)も速いので、ノイズもたくさん出します。手強いです。
 そもそも、DA変換器も、バカ高い(←値段が)オーディオ用のDA変換器を使わないとだめかもしれないですね。

STM32での実行

 何にせよ、ATmega328ベースのArduinoでは、パフォーマンス不足ですね。と言って、Pi ZeroWでは、軽く、ミリ秒オーダでOSにCPUタイムを取って行かれるので、多分、Arduinoよりひどい結果になるでしょう。

 と、なると、手元の機体では、STM32ボードなのですが、あんまり使いたくないんですよ。何故?って、この機体は、Logiscopeを使用するデバッグ用の機体なので。STM32をアプリケーションに使っている間、UNO等を使った他のアプリケーションのデバッグに支障をきたしてしまうから……。

ar41_stm32.jpg

 そうも言ってられなくなってきたので、STM32を使ってみましょう。
 Arduino IDEで、ターゲットを「Generic STM32F103C series」に変更して、コンパイルします。いくつか変更が必要だった部分は、「__STM32F1__」マクロで条件付きに切り替えてあります。先輩方に「感謝」なのは、SDライブラリも、Wireライブラリも、何の変更も要らず、コンパイルが通ってしまったことです。これはありがたい。
 STM32ボードのピンアサイン表は、こちらの記事にまとめてあります。この表の中で、SDカードI/Fに使用するSPI端子はSPI1(PA4〜PA7;3.3V I/Oポート)、MCP4725と通信するためのI2C端子はI2C1(PB6,PB7;5Vトレラント)でした。

 なお、回路図は、上で示したArduino用の回路図をそのまま使うことはできないので、各自、再設計をお願いします。

※「#8-6 音声合成、の、マネ事」にて作成したSTM32用の回路が、本ページのプログラム(play_wav.ino)でも使用可能です。ご参考まで。

 いつもの如く、深追いしていないのですが、さすがは、Cortex-M3 @72MHz!出力レートは相当早くなりました。と、言っても、I2C(400kHz)の物理的データ転送時間(約80μs)を超越することはできないので、出力レートで100数10μs程度までの向上です。
 SDカードへのアクセス速度も、相当向上している印象を受けます。余裕があれば、お試しください。

 個人的には、「STM32でも、WireライブラリやSDライブラリが、そのまま使える」という事が判ったのが収穫でした。再生音質の方は、Arduino UNOと比べて大差なく、「STM32を使えば解決します」みたいな結論には至りませんでしたので、この程度でサラッと流しておきます。


 総括としては、Arduino UNOによるWAVファイルの再生は、ノイズはすごいことになっているけれど、「聞こえなくはない」レベルでした。う〜ん、ザンネン。もう少し綺麗に再生できる様なら、「人の声(WAVファイル)で起きる目覚まし時計のアラーム」とか、応用が広がったところなのですが……。「実用レベルか?」と、問われると、「否」かな……。
 残念ながら筆者の能力では、これ以上のノイズ対策は降参です。得意な何方かにお任せいたしまする。


btn_arduino.png

btn_prev.png btn_next.png

home