この投稿はNCCアドベントカレンダーの8日目の記事です。
ArduinoにはESP8266で使えるESP8266Audioというライブラリがあるので、これを使って音楽プレイヤーを作ろうとしました。
ESP8266Audioを使うと、内蔵フラッシュに保存されたMP3の再生だけでなく、インターネットラジオの再生、MIDIファイルの再生もできます。
詳しくはサンプルスケッチを見て欲しいのですが、今回はこのライブラリとDACを組み合わせて音楽プレイヤーを作ります。
#なぜDAC?
ESP8266にはDACが付いていないため、普通は音を出すことはできません。なので、ESP8266AudioではソフトウェアΔΣ変調方式という(少し無理のある)方法で音を出すことができます。自分も詳しくは分からないのですが、0と1を高速で切り替えることでその中間を表す方法らしいです。
ですが、この仕組みは無理やり音を出しているので携帯ラジオみたいな音質であまりよくありません。もう一つ音を出す方法として外付けでDACを追加する方法があります。外付けDACといってもUSB接続ではなく、I2Sという形式で接続します。
AmazonでI2S DACと検索すると出てくる物は1つあたり1500円程度でそこまで気軽に買えるものではありません。そこで、TM8211というDACで自作しようと思いました。
#TM8211
TM8211は16BitのDACで、比較的なシンプルなR-2Rラダー抵抗方式を採用しているため1つあたり50円と単価が安いのが特徴です。それでも十分な音質が得られるのでこういった用途に向いているのですが、実はI2S対応ではないのです。
DACの接続方法にはI2S("Philips")のほかに"Japanese format"というのがあり、使用するピンは同じですが伝送方法が異なります。今回のTM8211は"Japanese format"なのでArduinoのI2Sコードを改変する必要があります。
ESP8266のArduinoコアでもIssueとして出されているのですが、C:\Users\[ユーザー名]\AppData\Local\Arduino15
からcore_esp8266_i2s.cpp
を検索して開き、その493行目をI2SC |= I2SRF | I2SMR | I2SRMS | (div1 << I2SBD) | (div2 << I2SCD);
に変える必要があります。
#配線
TM8211はICと出力段を用意するだけで音が出せるので簡単です。データシートの参考回路通りに組む事で音を出す事ができます。オペアンプはLM358Nを使いましたが、他の単電源オペアンプでも動作すると思います。
ESP8266との接続は、GPIO15を1ピン(BCK)に、GPIO2を2ピン(WS)に、GPIO3を3ピン(DIN)に接続すれば完了です。
出力はかなり大きい音が出るので、330Ωの抵抗を入れて音量を調整しました。また、その後に47uF程度のカップリングコンデンサを追加して音が聞こえるようにします。
自分はこんな感じに作りました。
1, 2, 3の下にあるのがI2Sのピンで、6番ピン(左)か8番ピン(右)から出る音声出力をスイッチで切り替えてオペアンプに繋いでいます。
オペアンプの回路は作るのが大変なので1ch分しか実装していません。
以下の動画で音を確認できます。
#Arduino
ライブラリを導入した後、サンプルスケッチのPlayMIDIFromLittleFSを改変して以下のようなスケッチを作成しました。
容量が少なくて済むMODファイルを音楽ファイルとして、3曲を順番に再生します。
#include <Arduino.h>
#ifdef ESP32
void setup() {
Serial.begin(115200);
Serial.printf("ERROR - ESP32 does not support LittleFS\n");
}
void loop() {}
#else
#include <ESP8266WiFi.h>
#include <AudioOutputI2S.h>
#include <AudioGeneratorMOD.h>
#include <AudioFileSourceLittleFS.h>
AudioFileSourceLittleFS *file;
AudioOutputI2S *dac;
AudioGeneratorMOD *mod;
int ct = 0;
int rlock = 0;
unsigned long tim = 0;
unsigned long diff = 0;
int gain = 1;
int button = 14;
void setup()
{
const char *filename = "/street life.mod";
pinMode(button, INPUT_PULLUP);
WiFi.mode(WIFI_OFF);
Serial.begin(115200);
Serial.println("Starting up...\n");
audioLogger = &Serial;
file = new AudioFileSourceLittleFS("/street life.mod");
dac = new AudioOutputI2S();
//dac->SetLsbJustified(true); // works only in ESP32
mod = new AudioGeneratorMOD();
mod->SetBufferSize(3*1024);
mod->SetSampleRate(44100);
mod->SetStereoSeparation(32);
mod->begin(file, dac);
dac->SetGain(0.7);
Serial.printf("BEGIN...\n");
}
void closefile()
{
file->close();
delete file;
}
void newmod(char* str) //track reload
{
closefile();
file = new AudioFileSourceLittleFS(str);
mod->begin(file, dac);
}
void volChange()
{
if (gain == 0) {
gain = 2;
dac->SetGain(1.0);
Serial.println("SetGain: 1.0");
} else if (gain == 1) {
gain = 0;
dac->SetGain(0.3);
Serial.println("SetGain: 0.3");
} else {
gain = 1;
dac->SetGain(0.6);
Serial.println("SetGain: 0.6");
}
}
void loop()
{
if (mod->isRunning()) {
if (digitalRead(button) == 0) { //if pressed
rlock = 1;
if (tim <= 0) {
tim = millis();
}
}
if (digitalRead(button) == 1) { //if depressed
if (rlock == 1) {
diff = millis() - tim;
Serial.println(diff); //please comment
if (diff > 10 && diff < 500) { //if timer < 500 stop
mod->stop();
} else if (diff >= 500) { //else change volume
volChange();
}
tim = 0;
}
rlock = 0;
}
if (!mod->loop()) {
uint32_t e = millis();
mod->stop();
}
} else {
Serial.printf("MOD done\n");
delay(10);
if (ct >= 2) {
ct = 0;
} else {
ct++;
}
if (ct == 0) { //track list, short loop samples makes noise
newmod("/street life.mod");
} else if (ct == 1) {
newmod("/separate ways.mod");
} else if (ct == 2) {
newmod("/nocilis.mod");
}
}
}
サンプルファイル:
street life.mod
separate ways.mod
nocilis.mod
音楽ファイル自体はArduinoのプラグインであるArduino ESP8266 LittleFS Uploaderを使用してフラッシュに書き込みます。
また、ボタンを短く押すと次の曲、長く押すと音量調整としました。
#最後に
一応音は出るのですが、ボリュームを最大(1.0)にするとサーと言うノイズが聞こえてしまいます。そこでDACの電圧を下げてみたのですが、ノイズは収まりませんでした。
その代わりスケッチで音量をSetGain(0.7)と指定するとよりクリアな音になりました。
音質についてはあまり分かりませんが、少なくとも無しの時よりはいい音でした。