Arduino Waveシールドを使ってみる

th_wave_shield_1

ちょっとArduinoで音声ファイルを再生したくなったので、Adafruit Waveシールドキットを買ってきました。

キットなので全部自分で半田付けしないといけないですが、そんなに難しくはないです。SDカードのスロットのところの半田付けがちょっと細かいぐらい。

(注. WaveシールドをArduino UNOで使うことが確定している人はそのまま組み立てればOKですが、Arduino Megaで使う可能性がある方は、こちらの記事もご参照いただいた方が良いと思います)

th_wave_shield_2

出来上がりはこんな感じです。ボリュームのダイヤルがちょっとデカいですが、まあこれはこれでレトロな雰囲気で良い感じです。

後でいろいろ接続しやすいように、プラスでいくつかピンソケットを追加しています。うっかり11〜13番ピンのところにもソケットをつけてしまいましたが、このピンはSDカードが使用しているの利用できません。

th_wave_shield_1

スピーカーを直接繋いだWaveシールドをArduino Uno R3に乗せて、2つのタクトスイッチを配線した状態です。動作イメージとしては、

  • 片方のスイッチを押すと再生&停止
  • もう一方のスイッチを押すと次の曲に移動
  • 曲が終わると次の曲を自動的に再生開始

というものを想定しています。

ではでは、プログラムしていきます。まずは、WaveHCライブラリをここからダウンロードしてきて、Arduinoのlibrariesフォルダに突っ込みます。

Waveシールドの基本的な使い方については、以下の本にわかりやすいサンプルプログラムが載っています。

この本のソースをベースに、ライブラリに含まれているサンプルスケッチの要素をいくつか追加して作り上げたのが以下です。

#include <WaveHC.h>
#include <WaveUtil.h>

#define PLAY_PIN   6
#define SELECT_PIN 7

#define error(msg) SerialPrint_P(PSTR(msg))

SdReader  cardReader;
FatVolume volume;
FatReader root;
FatReader playFile;
WaveHC    waveFile;

const char game_0[] PROGMEM = "maX.wav";
const char* const games[] PROGMEM = {game_0};
const PROGMEM char number[] = {'1','2','3'};

char name[8];
uint8_t index = 1;
uint8_t lastPlayState   = HIGH;
uint8_t lastSelectState = HIGH;
boolean isPlaying = false;

void setup(){
  Serial.begin(9600);
  pinMode(PLAY_PIN, INPUT_PULLUP);
  pinMode(SELECT_PIN, INPUT_PULLUP);

  // カードリーダの初期化
  if(!cardReader.init()){
    error("Can't init the card.");
  }

  // FATパーティションを検索
  uint8_t partition = 0;
  for(partition=0; partition<5;partition++){
    if(volume.init(cardReader, partition)){
      break;  
    }  
  }

  // FATパーティションが見つからなければエラー
  if(partition == 5){
    error("Can't find a FAT partition.");
  }

  // ボリュームのルートディレクトリをオープン
  if(!root.openRoot(volume)){
    error("Can't open the root.");
  }
}

void loop(){
  uint8_t selectState = digitalRead(SELECT_PIN);
  if(lastSelectState == HIGH && selectState == LOW){
    soundSelect();
    if(waveFile.isplaying){
      soundStop();
      soundPlay();
    }
  }
  lastSelectState = selectState;
  uint8_t playState = digitalRead(PLAY_PIN);
  if(lastPlayState == HIGH && playState == LOW){
    if(waveFile.isplaying){
      soundStop();
    }else{
      soundPlay();
    }    
  }
  lastPlayState = playState;

  if(isPlaying  && !waveFile.isplaying){
    // 曲の再生が終わったら自動的に次の曲を再生する
    soundSelect();
    soundPlay();
  }

  delay(20);  
}

void soundPlay(){
  // ファイル名のセット
  strcpy_P(name, (char*)pgm_read_word(&(games[0])));
  name[2] = pgm_read_byte(&number[index]); 
  // ルートディレクトリにあるファイルを名前指定でオープン
  if(!playFile.open(root, name)){
    error("Can't open the file to play.");
  }  
  // プレーヤーにファイルをセット
  if(!waveFile.create(playFile)){
    error("Not a valid WAV file.");
  }
  // 再生開始
  waveFile.play();
  // 再生用にオープンしたファイルをクローズ
  playFile.close();

  isPlaying = true;
}

void soundStop(){
  waveFile.stop();
  isPlaying = false;
}

void soundSelect(){
  index == 3 ? index = 1: index++;
}

さて、ソースとしてはこれだけなのですが、ここからがちょっと面倒です。

このWaveシールド、名前の通りで、wavファイルしか再生してくれません。しかも、サンプルレート22KHz、サンプルサイズ16bit、モノラル限定です。よって、これに合うように手持ちの音楽ファイルを変換してやる必要があります。

変換方法については、こちらに記載があります。自分はiTunesを使って変換しました。

無事にwavファイルが用意できたら、ファイルをSDカードのトップのフォルダにコピーします。ついでに言うと、ファイル名も長さ制限とかあるみたいなので、英数字で8文字以下ぐらいに収まる名前にしときましょう。

ちなみにSDカードも、事前にFAT16かFAT32にフォーマットしておく必要があり、その手順もこちらに記載されています。SDFormatterで上書きフォーマットするのが一番確実なようなので、それでやりました。

ではでは、動作確認です。

 

 

うん、とりあえずはOKです。再生中に停止した場合、停止した場所からではなく、必ず最初から再生が始まるようになっていますが、自分の要件としてはこれでOKです。

 

ちなみにこのWaveシールド、ここに書かれている通り、基本的にはそのまま組み立てるとArduino MegaとかLeonardoでは動きません。が、ちょっと配線を修正すれば、少なくともArduino Megaでは動かすことができます。これについては、次の記事で紹介します。

小細工なしでMegaで動かしたい、ということであれば、Seeed製のミュージックシールドを使うのもアリかもしれません。

こっちなら確実にMegaに対応しているようですし、サポートフォーマットも豊富です。ただ、アンプ関係の実装がよくわからなかったので、今回はWaveシールドを選びました。