ESP8266とTM8211で音楽プレイヤーを作る

この投稿はNCCアドベントカレンダーの8日目の記事です。

Arduinoには[ESP8266](https://www.rs-online.com/designspark/basics-of-esp8266-jp)で使える[ESP8266Audio](https://github.com/earlephilhower/ESP8266Audio)というライブラリがあるので、これを使って音楽プレイヤーを作ろうとしました。

ESP8266Audioを使うと、内蔵フラッシュに保存されたMP3の再生だけでなく、インターネットラジオの再生、MIDIファイルの再生もできます。
詳しくは[サンプルスケッチ](https://github.com/earlephilhower/ESP8266Audio/tree/master/examples)を見て欲しいのですが、今回はこのライブラリとDACを組み合わせて音楽プレイヤーを作ります。

#なぜDAC?
ESP8266にはDACが付いていないため、普通は音を出すことはできません。なので、ESP8266Audioでは[ソフトウェアΔΣ変調方式](https://github.com/earlephilhower/ESP8266Audio#software-i2s-delta-sigma-dac-ie-playing-music-with-a-single-transistor-and-speaker)という（少し無理のある）方法で音を出すことができます。自分も詳しくは分からないのですが、0と1を高速で切り替えることでその中間を表す方法らしいです。

ですが、この仕組みは無理やり音を出しているので携帯ラジオみたいな音質であまりよくありません。もう一つ音を出す方法として外付けでDACを追加する方法があります。外付けDACといってもUSB接続ではなく、[I2S](https://toshiba.semicon-storage.com/jp/semiconductor/product/microcontrollers/articles-functions/i2s-inter-ic-sound.html)という形式で接続します。

Amazonで[I2S DAC](https://www.amazon.co.jp/s?k=I2S+DAC)と検索すると出てくる物は1つあたり1500円程度でそこまで気軽に買えるものではありません。そこで、TM8211というDACで自作しようと思いました。

#[TM8211](https://www.aitendo.com/product/12997)
TM8211は16BitのDACで、比較的なシンプルな[R-2Rラダー抵抗方式](https://www.mech.tohoku-gakuin.ac.jp/rde/contents/course/mechatronics/anadigi.html)を採用しているため1つあたり50円と単価が安いのが特徴です。それでも十分な音質が得られるのでこういった用途に向いているのですが、実はI2S対応ではないのです。
DACの接続方法にはI2S("Philips")のほかに"Japanese format"というのがあり、使用するピンは同じですが伝送方法が異なります。今回のTM8211は"Japanese format"なのでArduinoのI2Sコードを改変する必要があります。

ESP8266のArduinoコアでも[Issue](https://github.com/esp8266/Arduino/issues/6940)として出されているのですが、`C:\Users\[ユーザー名]\AppData\Local\Arduino15`から`core_esp8266_i2s.cpp`を検索して開き、その493行目を`I2SC |= I2SRF | I2SMR | I2SRMS | (div1 << I2SBD) | (div2 << I2SCD);`に変える必要があります。

#配線
TM8211はICと出力段を用意するだけで音が出せるので簡単です。[データシート](http://aitendo3.sakura.ne.jp/aitendo_data/product_img/ic/LED-driver/TM8211_V1.0.pdf)の参考回路通りに組む事で音を出す事ができます。オペアンプは[LM358N](https://akizukidenshi.com/catalog/g/gI-13213/)を使いましたが、他の単電源オペアンプでも動作すると思います。
ESP8266との接続は、GPIO15を1ピン(BCK)に、GPIO2を2ピン(WS)に、GPIO3を3ピン(DIN)に接続すれば完了です。
出力はかなり大きい音が出るので、330Ωの抵抗を入れて音量を調整しました。また、その後に47uF程度のカップリングコンデンサを追加して音が聞こえるようにします。

自分はこんな感じに作りました。

![css.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2355787/fd25c6d5-6b2c-a718-b5bc-492ae4e325c9.jpeg)

1, 2, 3の下にあるのがI2Sのピンで、6番ピン(左)か8番ピン(右)から出る音声出力をスイッチで切り替えてオペアンプに繋いでいます。
オペアンプの回路は作るのが大変なので1ch分しか実装していません。

以下の動画で音を確認できます。

<iframe width="560" height="315" src="https://www.youtube.com/embed/qDkMjgjxRL8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

#Arduino
ライブラリを導入した後、サンプルスケッチの[PlayMIDIFromLittleFS](https://github.com/earlephilhower/ESP8266Audio/tree/master/examples/PlayMIDIFromLittleFS)を改変して以下のようなスケッチを作成しました。
容量が少なくて済むMODファイルを音楽ファイルとして、3曲を順番に再生します。

```Arduino:ESP8266Player.ino
#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](http://www.bitfellas.org/e107_plugins/radio/radio.php?info&id=20075)
[separate ways.mod](https://modarchive.org/index.php?request=view_by_moduleid&query=83132)
[nocilis.mod](https://modarchive.org/index.php?request=view_by_moduleid&query=51443)

音楽ファイル自体はArduinoのプラグインである[Arduino ESP8266 LittleFS Uploader](https://github.com/earlephilhower/arduino-esp8266littlefs-plugin)を使用してフラッシュに書き込みます。
また、ボタンを短く押すと次の曲、長く押すと音量調整としました。

#最後に
一応音は出るのですが、ボリュームを最大(1.0)にするとサーと言うノイズが聞こえてしまいます。そこでDACの電圧を下げてみたのですが、ノイズは収まりませんでした。
その代わりスケッチで音量をSetGain(0.7)と指定するとよりクリアな音になりました。
音質についてはあまり分かりませんが、少なくとも無しの時よりはいい音でした。