ArduinoでSTEM教育​ 応用編:WAV音源をデータ変換して圧電スピーカから出力する

ArduinoでSTEM教育​ 応用編:WAV音源をデータ変換して圧電スピーカから出力する

効果音のような短い音源であれば、SDカードやシールド等を使用しなくても、音源をソースコードに埋め込んで、圧電スピーカから出力することができます。プログラムサイズが容量オーバーしないように、音質を落とす必要がありますが、利用できる機会は多いと思います。

WAV音源を準備する

まずは音源を準備する必要があります。ネットで購入するのが、一番お手軽だと思います。国内サイトだとAudioStock、海外サイトだとAudioJungleがメジャーです。先日、AudioJungleで効果音を2ドルで購入しました。支払いはPayPalで行いました。購入前に音を再生することができますので、色々聴き比べて選ぶことができます。購入したファイルはMP3形式だと思うので、フリーソフト等でWAV形式に変換してください。このあたりの情報は、検索するとたくさん出てきます。

WAV音源からデータ配列に変換する

RAW形式に変換

音声データをArduinoのプログラムに埋め込む必要があるため、多くの場合はステレオからモノラルに変換したり、音質(ビットレート)を落として、データサイズを小さくする必要があります。これは、Audacityというフリーソフトで出来ます。手順は次のとおりです。

  1. AudacityでWAVファイルを開いて、ステレオからモノラルに変換する。
    [トラック] -> [Mix] -> [Mix Stereo down to Mono]
  2. ビットレートを8000Hzにする。
    画面左下の[Project Rate]で8000を選択する。
  3. ファイル形式を指定して出力する。
    [ファイル] -> [Expoprt] -> [選択したオーディオの書き出し] をクリック
    ファイルの種類:その他の非圧縮ファイル
    ヘッダ:RAW(header-less)
    エンコーディング:Unsigned 8-bit PCM

データ配列に変換

作成したRAW形式のファイルをArduinoで取り扱えるデータ配列に変換します。これは、「放課後マイコンクラブ」の「PROGMEM作蔵さん」というHTMLスクリプトを使用すると簡単です。 「PROGMEM作蔵さん」 で変換すると、「 const uint8_t bindata[] PROGMEM = { … };」という形式で出力されます。 他の方法としては、「 xxd -i 」というLinuxコマンドでもできるようですが、Windowsを使用されている方は環境を整えることが難しそうなので、先述の方法をお勧めします。

プログラム

音源ヘッダファイル

先述の方法で出力したデータをヘッダファイル(*.h)で保存します。メインプログラムと分離しておくことで、コードの管理がしやすくなります。PROGMEMはFlashメモリにデータを格納する記述です。プログラムを実行するためのメモリはSRAMですが、容量が2kBしかありません。そこで、比較的容量の大きいFlashメモリ(32kB)に音源を保管します。必要なときに、必要な分だけSRAMに読み込みます。

メインプログラム

次にメインのプログラムです。今回は、3つの音源を使用しました。それぞれ冒頭のincludeで読み込みます。圧電スピーカは3番ピン(PWM)で出力します。以下の記述は今まで見たこと無い方が多いと思いますが、これはATmega328Pのデータシートを見ると記載があります。

TCCR2A = _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
TCCR2B = _BV(CS20);

TCCR2A/TCCR2Bは、タイマ/カウンタ2制御レジスタです。「_BV」は、ビットセットで、カッコ内のビットを1にセットします。すなわち、下記のCOM2B1、WGM21、WGM20を1にセットします。

WGM2(波形生成種別)を2’b11にセットすることで、高速PWM動作モードになります。そして、COM2B(比較B出力選択)を2’b10にセットすることで、タイマとOCR2Bとの非反転比較をOC2Bピン(ArduinoのD3ピン)へ出力します。CS2(クロック選択)ビットは、3’b001なので分周無しです。

TCCR2A
TCCR2B

次に、「OCR2B = pgm_read_byte_near(&soundArray[i]);」について説明します。 pgm_read_byte_near は、Flashメモリから1バイトのデータをSRAMに読み出します。引数はアドレスです。ヘッダファイルでPROGMEMで音源をメモリに入れているので、そのアドレスを引き渡します。OCR2B(タイマ/カウンタ2比較B値)は、継続的にTCNT2( タイマ/カウンタ2計数値値)と比較される8ビットの値です。

OCR2B

そして、「delayMicroseconds(125);」についてですが、音源データを作成する際に、8000Hzを選択しました。なので、1秒÷8000=125マイクロ秒のdelayを設けます。

回路

スピーカのプラス端子を3番ピンに接続し、マイナス端子をGNDに接続します。

接続図

まとめ

ファイルサイズを小さくするために、音質をかなり落としましたが、意外とまともな音を再生できました。効果音や短い音声であれば、十分使える方法だと思います。ATmega328Pのレジスタ設定について、いろいろ書きましたが、なかなか理解することは難しいと思います。まずは、ソースコードをコピーして、とにかく動く状態を確認し、そこからソースコードを自分で少し変えながら理解を深めていくのが良いと思います。

Arduino応用編カテゴリの最新記事