ESP32でI2S+DACを使う

# 概要

ESP32にはI2Sインターフェースが内蔵されており、音声の入出力が可能です。ESP32のI2SはDMA経由で動作し、低いCPUの負荷で動作します。8bitのDACも内蔵されており、I2Sに接続して使用できます。
I2Sのライブラリ、サンプルプログラムは esp-idf開発環境[1]で提供されています。使用方法もProgramming Guide[2]に説明してありますが、そこに書いてある方法では、内蔵DACから音声を出力することができませんでした。試行錯誤の結果、若干修正することで出力できるようなったので、その修正内容を紹介します。　
esp-idfのドキュメントの更新が追い付いていないだけで、いずれ解決される問題ではないかと思いますが、現時点（2017年5月）では、この情報は、提供されていないようなので、紹介します。

# ドキュメントにあるI2S+DACの使い方

ドキュメント、Programming GuideのI2Sのページ[3]にあるI2SとDACを組み合わせて使う方法を以下に示します。大変簡単です。

```c
#include "driver/i2s.h"
#include "freertos/queue.h"

static const int i2s_num = 0; // i2s port number

static const i2s_config_t i2s_config = {
     .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN,
     .sample_rate = 44100,
     .bits_per_sample = 8, /* must be 8 for built-in DAC */
     .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
     .communication_format = I2S_COMM_FORMAT_I2S_MSB,
     .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // high interrupt priority
     .dma_buf_count = 8,
     .dma_buf_len = 64
};

...

    i2s_driver_install(i2s_num, &i2s_config, 0, NULL);   //install and start i2s driver

    i2s_set_pin(i2s_num, NULL); //for internal DAC

    i2s_set_sample_rates(i2s_num, 22050); //set sample rates

    i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver
```

補足すると、

- i2s_driver_install()で設定した値と違わなければ、i2s_set_sample_rates()は不要。
- i2s_driver_uninstall()も、特にメモリーを解放したいというようなことが無ければ不要。
- データの書き込みは、1サンプルづつ i2s_push_sample() か、まとめてi2s_write_bytes()で行う。

# その問題点

ドキュメントどおり作成したプログラムでは、DACの出力から、ちゃんとした音声が出力されません。
私は、次のような現象に遭遇しました。

- i2s_set_sample_rates()で、エラーになります。メッセージは"**Invalid bits per sample**"
- esp-idf/components/driver/i2s.cを改造し、上記のエラーが出ないようにしても、
 - DAC1から、なんか波形は出るが、いろいろ変
 - DAC2からは、ノイズしか出力されない

# 対策

試行錯誤の結果、わかった対策は、以下の通りです。

- **.bits_per_sample は 16**, "must be 8 for built-in DAC" と書いてあるけど16
- i2s_push_sample(), i2s_write_bytes()でデータを書き込む場合も1サンプルあたり 16bit x 2　= 32bitづつ書き込む。但しDACは8bitなので、下位8bitは無視される
- .dma_buf_countは大目に取る。参考にしたプログラム[4]では14

当初、 .dma_bufの数など2つあれば十分、余裕を持たせて4程度にし、.dma_buf_lenを1024で試したが、うまくいきませんでした。参考にしたプログラム[4]で14となっていたので、同じにしたところ急にうまくいくようになりました。14がマジックナンバーというわけでもなく16でも問題無く動作しました。

# デモ・プログラム

実際に、I2S+内蔵DACで動作するプログラムの例として、bluetooth speakerを作成しました。Tw_Mhageさんの「[ESP32でBluetoothイヤホンを作ってみた](http://qiita.com/Tw_Mhage/items/0ef09c9a8bcf5f15c41b "ESP32でBluetoothイヤホンを作ってみた")」に触発され、esp-idfのサンプルプログラム a2dp_sinkを改造しています。
サンプリングレート44.1kHz, 12bit stereoに決め打ちです。環境によっては、うまく動かないかもしれませんが、サンプルプログラムとして参考になれば幸いです。

動作の様子をYouTube, ソースプログラムをgithubで公開しています。

- デモ・プログラム・動画 (YouTube)
 - https://youtu.be/7-ZSAkkyPiY
- デモ・プログラム・ソース (GitHub)
 - https://github.com/h-nari/ESP32_bt_speaker

必要な回路は、下の図程度です。GPIO25,GPIO26にDAC1,DAC2の信号が出力されます。これは固定されていて変更できません。

<img src="http://www.narimatsu.net/blog/wp-content/uploads/2017/05/sc170503a1-300x262.png" width="400"/>

# 最後に

ESP32での音声出力は、前からやりたかったことなので、出来て嬉しいです。次は、音声入力に挑戦したいと考えています。音声入出力の機能がArduinoライブラリなどに、まとめられて、簡単に使えるようになるのが理想ですが、現状のIDFやesp32-arduinoの状態を見ると、それには、まだまだ時間がかかりそうな気がします。

# 参照

- [1]esp-idf 開発環境
 - https://github.com/espressif/esp-idf
- [2]ESP-IDF Programming Guide:
 - http://esp-idf.readthedocs.io/en/latest/#
- [3]ESP-IDF Programming Guide I2Sのページ:
 -  http://esp-idf.readthedocs.io/en/latest/api-reference/peripherals/i2s.html
- [4]参考にしたプログラム、ESP32_MP3_Decoder
 - https://github.com/MrBuddyCasino/ESP32_MP3_Decoder