LVGL の練習にシンプルな GUI で MP3 プレイヤーを作ろうと思い立ち、色々と情報を漁ってました。CYD の情報は豊富だし、音を鳴らす方法なんて簡単に見つかるだろうとタカを括ってましたが、コレが中々どうして、結構な調べ甲斐のあるテーマでした。

今回ようやく .mp3.m4a.wav といったオーディオファイルを再生することができたので、まずは基本動作編としてお披露目したいと思います。

1.CYD で音を鳴らすには…

Internal DAC を利用する

ボードタイプが ESP32 Dev Module で設定される標準的な ESP32 開発ボードでは、GPIO25 と GPIO26 が DAC の出力ピンに割り当てられています。

static const uint8_t DAC1 = 25;
static const uint8_t DAC2 = 26;
スピーカー端子と回路図
スピーカー端子と回路図

一方 CYD では、GPIO25 はタッチパネルの CLK に、GPIO26 はオーディオアンプ IC の SC8002B にそれぞれ内部的に接続されていてます。

これらの GPIO は、ボードタイプに ESP32-2432S028R CYD を選択することで、次のシンボルが利用可能となります。

#define CYD_TP_CLK    25
#define CYD_AUDIO_OUT 26

試しに「SPEAK」端子にスピーカーを接続し、IDE から「ファイル > スケッチ例 > 02.Digital > toneMelody」を開き、tone()noTone() に指定された 826 に変更すれば、お馴染みのメロディを聞くことが出来ます。

外付けの DAC やアンプを利用する

以下の2つのサイトには、標準的な ESP32 開発ボードの GPIO25(DAC1)、GPIO26(DAC2)の代わりに拡張 I/O 端子の 22 番ピンと RGB LED の一部のピンを流用し、オーディオアンプのブレイクアウトボードを外付けする方法が紹介されています。

音質が良くなりそうだし、ステレオ化も魅力的ですが、今回は見送りです 😓

2.音の歪み改善

ESP32 2432S028R (CYD)+任天堂コントローラー=懐かしのアーケードゲームマシン」では放置してましたが、スピーカー端子から出る音は歪みが酷く、音楽を聴くには耐え難いほどです。まずはこれを何とかしなくちゃです。

手元の2つのタイプで、オーディオアンプ IC SC8002B 周りの回路定数を確認すると、回路図では本来 47KΩ と 22KΩ の R7 と R8 が、2 USB タイプでは何と 0Ω になってます 😳

アンプ周りの R, C(1USBタイプ)
アンプ周りの R, C(1USBタイプ)
アンプ周りの R, C(2USBタイプ)
アンプ周りの R, C(2USBタイプ)

Piotr Zapart さんの「Audio amp gain mod」では R7、R8 の付け替えが推奨されていますが、0603 は小さ過ぎて換装できる気がしません。そこで試しに R9(これは回路図通り 68KΩ)と並列に半固定抵抗を付けて調整したところ、アンプ IC に 2KΩ を直付けすることで何とか聞くに耐えられる程度には改善しました 😮‍💨

出典:hexeguitar/ESP32_TFT_PIO
出典:hexeguitar/ESP32_TFT_PIO
アンプゲインの調整
アンプゲインの調整
0603 SMD抵抗器キット
一生使いきれない0603のセット

結局 R7、R8 の換装にチャレンジしました。47KΩ と 22KΩ だけとはいかず、Amazon で 0603 のセット(¥1,399) を購入。歪みは改善しましたが、逆にノイズが気になる様に。そこで 68KΩ の R9 と並列に 47KΩ をアンプ IC に直付けし再びアンプゲインを下げたところ、満足できる音質になりました(注:インピーダンス 4Ω のスピーカーを接続した時の個人的感覚です)。メデタシ、メデタシ 😇

3.I2S オーディオライブラリ

CYD の Internal DAC で音を鳴らせるライブラリーを探しました。

Espressif の Arduino ESP32 ドキュメント “Migration from 2.x to 3.0” によると、ボードパッケージ 3.0.0(ESP-IDF 5.1) で I2S ドライバが「完全に再設計されリファクタリングされた」とあります。

そのため 2.0.17(ESP-IDF v4.4.7) の前後でコンパイルに問題が出たり、動作が変わったりということが起きている様です。

ESP32-audioI2S

ESP32-audioI2Sの混乱っぷり
ESP32-audioI2Sの混乱っぷり

ESP32-audioI2S は、ESP32 用の I2S ライブラリとして最も利用されていると思われます。ただし、library.properties の記述に不整合があり、IDE のライブラリマネージャーと GitHub 上にホストされたリリースバージョンが乖離しています。

ライブラリマネージャー上の 3.0.13(GitHub 上は 3.1.0) では、リリースノートの「no longer supports an internal DAC」の通り Audio クラスのコンストラクタから Internal DAC の指定が削除されています。

class Audio : private AudioBuffer {
    AudioBuffer InBuff; // instance of input buffer
  public:
    Audio(uint8_t i2sPort = I2S_NUM_0);
    ~Audio();
    ...
}

一方 2.0.0(GitHub 上は 3.0.12)では Internal DAC がサポートされていていますが、ESP32 by Espressif のボードパッケージ 2.0.17(ESP-IDF v4.4.7)が必要です。

class Audio : private AudioBuffer {
    AudioBuffer InBuff; // instance of input buffer
  public:
    Audio(bool internalDAC = false, uint8_t channelEnabled = 3, uint8_t i2sPort = I2S_NUM_0); // #99
    ~Audio();
    ...
}

farmsoft さんの記事 にサンプルスケッチが示されていたので試してみましたが、オーディオファイルの情報は正しく読み取るものの、残念ながら本記事の執筆時点では音が出ませんでした。次のコードで GIPI26 を有効化 しているハズなのですが…

Audio audio(true, I2S_DAC_CHANNEL_LEFT_EN);

I2S の使い方をもっと研究しないとダメですね。とりあえずこのライブラリの使用は断念です 😩

ESP32 I2S audio library

音の歪み改善」でも紹介した Piotr Zapart さんの ESP32 I2S audio library は、前述の ESP32-audioI2S をベースに CYD 用に改修したライブラリです。

動作条件は限られますが、いい感じで音が出たので、その利用方法を順に説明します。

動作条件

ソフトウェア 動作条件
Arduino IDE 2.3.4 〜 2.3.6 で確認済み
ESP32 by Espressif ボードバッケージ 2.0.17 を推奨 1
ボードタイプ ESP32 Dev Module

インストール方法

  1. Piotr Zapart さんのリポジトリから ESP32_TFT_PIO-main.zip を落とし解凍します。

  2. 解凍したフォルダから ESP32_TFT_PIO-main/Examples/CYD28_BaseProject/lib/ 中の CYD_Audioライブラリフォルダにインストール します。

  3. CYD_AudioPlatformIO 用に作られているので、Arduino 環境で利用できるよう以下の library.properties をインストールしたフォルダに保存します。

name=CYD_Audio
version=1.0.0
author=Piotr Zapart
maintainer=hexeguitar www.hexefx.com
sentence=Audio library for the CYD series display+ESP32 board, based on work of schreibfaul1
paragraph=Audio library for the CYD series display+ESP32 board, based on work of schreibfaul1
category=Signal Input/Output
url=https://github.com/hexeguitar/ESP32_TFT_PIO/tree/main/Examples/CYD28_BaseProject/lib/CYD_Audio
architectures=*

CYD_Audio フォルダの構成は以下のようになります。

CYD_Audio
├── keywords.txt
├── library.json
├── library.properties    <-- Arduino環境用に追加
├── readme.md
└── src
    ├── CYD_Audio.cpp
    ├── CYD_Audio.h
    ├── CYD_DSP.cpp
    ├── CYD_DSP.h
    ├── CYD_audioCustom.cpp
    ├── aac_decoder/
    ├── flac_decoder/
    ├── mp3_decoder/
    ├── opus_decoder/
    └── vorbis_decoder/

ライブラリの動作仕様

このライブラリがどの様に動作するかを次の図に描いてみました。

ユーザー I/F 用のコードをコア1で、オーディオ再生用のタスク audioTask() をコア0で動作させるのが、このライブラリの典型的な利用方法です。ちょうど「リモコンと CD プレイヤー」をイメージすると分かり易いかもしれません。

ライブラリの動作イメージ
ライブラリの動作イメージ

コア間はメッセージキューで結ばれ、コア1からコマンドを送り、コア0の audioTask() がレスポンスを返します。また同タスクは1回に1つのコマンドだけを処理するよう、セマフォで自身を排他制御しています。

コマンドの送受信を担当するのが Examples 中の CYD28_audio.hCYD28_audio.cpp で、次のようなインターフェースが用意されています。

インターフェース関数 動作
void audioInit(void) コア0でタスク audioTask() を起動します
bool audioConnecttoSD(…) microSD 内のオーディオファイルを再生します
bool audioConnecttohost(…) Web ラジオをストリーミング再生します 2
bool audioConnecttoSpeech(…) Google サービスでテキスト to スピーチを実行します
bool audioIsPlaying(void) 再生中か否かを問い合わせます
void audioStopSong(void) 再生を停止します
void audioSetVolume(uint8_t vol) ボリュームを指定します(0〜21)
uint8_t audioGetVolumePerCent(void) 現在のボリュームを取得します(0〜100%)
uint32_t audioGetRMS(void) 信号の実行値(Root Mean Square)を取得します

RMS については コチラ を参照してください。VU メーター などのギミックを実装するときに使います。

続いてこれらの関数を使ったサンプルスケッチを説明します。

サンプルスケッチの説明

最もシンプルな例を CYD_MP3Player_Basic に作ってみました。

CYD_MP3Player_Basic
├── CYD28_audio.cpp           <-- オリジナルのExamplesからコピー
├── CYD28_audio.h             <-- オリジナルのExamplesからコピー
└── CYD_MP3Player_Basic.ino

注意すべきは audioInit() の実行後、コア0側の準備が整った頃合いを見計らってからコマンドを発行する必要があるという点です。例では 10msec の遅延を入れています。

#include "CYD28_audio.h"

void setup() {
  Serial.begin(115200);
  while (millis() < 1000);

  if (!SD.begin()) {
    Serial.println("Cannot begin SD.");
    while (1);
  }

  audioInit();
  delay(10); // Wait until the task on Core 1 is ready to receive a command
  audioConnecttoSD("/sample.mp3");
}

void loop() {}

また LVGL 化に向けてもう少し実用的な例題を CYD_MP3Player に作りつつあるので、良かったら参考にしてください。

入出力デバイスの設定

  • 出力バイスの設定
    外付けの DAC やアンプを利用する場合は、CYD28_audio.h または CYD28_audio.cpp のどちらかに #define USE_I2S_DAC を追加します。

  • SdFat の設定
    ESP32 標準の SD ライブラリ に替えて SdFat を利用する場合は、CYD_Audio.h#define SDFATFS_USED を有効化する必要があります。またファイル名に日本語を使うには SdFatConfig.h の編集も必要です(ESP32 標準の SD ライブラリでは不要)。

既知の問題点

CYD_Audio ライブラリの動作条件を ESP32 ボードパッケージの 2.0.17(ESP-IDF v4.4.7)としているのは、3.1.3(ESP-IDF v5.3)または本記事執筆時点で最新の 3.2.0(ESP-IDF v5.3)(おそらく 3.0.0 以上)で以下の問題が生じるためです。

コンパイル時のエラー

次のシンボルが未定義となります。

error: 'FUNC_GPIO0_CLK_OUT1' was not declared in this scope
error: 'FUNC_U0RXD_CLK_OUT2' was not declared in this scope
error: 'FUNC_U0TXD_CLK_OUT3' was not declared in this scope
error: 'PERIPHS_IO_MUX_GPIO0_U' was not declared in this scope
error: 'PERIPHS_IO_MUX_U0RXD_U' was not declared in this scope
error: 'PIN_CTRL' was not declared in this scope
error: 'PIN_FUNC_SELECT' was not declared in this scope

このエラーは CYD には必要ない i2s_mclk_pin_select() を無効化すれば解消しますが、さらに関数引数の型の不一致で怒られます。

error: invalid conversion from 'int32_t*' {aka 'long int*'} to 'int*'
error: invalid conversion from 'int32_t*' {aka 'long int*'} to 'const int*'

実際の修正については コチラ を参照ください。

コンパイル時の警告

非推奨となった I2S 関連の警告が出ます。

#warning "legacy adc driver is deprecated, please migrate to use esp_adc/adc_oneshot.h and esp_adc/adc_continuous.h for oneshot mode and continuous mode drivers respectively"
#warning "This set of I2S APIs has been deprecated, please include 'driver/i2s_std.h', 'driver/i2s_pdm.h' or 'driver/i2s_tdm.h' instead. if you want to keep using the old APIs and ignore this warning, you can enable 'Suppress legacy driver deprecated warning' option under 'I2S Configuration' menu in Kconfig"
ランタイム時の警告

スケッチの起動時に次の様な警告がシリアルモニタに出力されます。

i2s(legacy): i2s_calculate_adc_dac_clock(764): sample rate is too small, the mclk division exceed the maximum value 255
i2s(legacy): i2s_calculate_clock(859): ADC/DAC clock calculate failed
再生開始時の異音

曲の再生開始時に耳障りな「ボツッ」という異音が出ます。この対策としてフェードイン処理が施されていますが、機能するのは 2.0.17 までです。

4.「だんすぴ 🔊」で試聴

何とか音を鳴らすことが出来たので、今回のために「だんすぴ」(¥680)と 2.5 インチ 4Ω 15W フルレンジ(¥550/個)を Amazon でセット購入、机上に置いて聴いています。

「だんすぴ」自体はそう悪くないのですが、最大音量にすると明らかに歪みが残っているのが分かりますし、 (← 歪みはかなり改善しました)静かな曲だとノイズが気になります。

まぁ、オーディオマニアじゃないので、何かしながら聴き流す BGM 程度なら十分です。

ということで、これで心置きなく LVGL で GUI の作成にチャレンジできると思います 🎉


  1. 2.0.17 を推奨としている理由については「既知の問題点」を参照願います。 

  2. Web ラジオのストリーミング再生には PSRAM の増設が必要です、残念!