新年、明けましておめでとうございます 🐎

昨年2月の「ESP32 2432S028R (CYD)にサーモグラフィカメラを移植しました」以来、1年に1本しかマトモなソフトをリリース出来ず、情けない限りです。しかも投稿した記事のほぼ全てが CYD 関連でした。

まぁ、本年もユルユルとやっていきますので、よろしくお願い致します 🥱

さて新年の一発目も CYD ネタで恐縮ですが、LVGL の習熟を目標とし、LCD・タッチパネル・SD カード・アンプ IC の全機能を活かすことに拘って制作を進めて来た表題の件です。

CYD MP3 Music Player
CYD MP3 Music Player

まずはハードウェアから…

未登録の技適マーク付き CYD を引いた件

詐称された技適マーク付き CYD
詐称された技適マーク付き CYD

ESP32 2432S028R (CYD)でLVGL - I2SとInternal DACでMP3 Player - 基本動作編」で CYD の音質改善について報告しました。これをやり直すべく、昨年ちゃんとした技適マーク付きが購入できた AliExpress のショップから再購入したところ、写真のようなモノが届きました。

総務省のサイトで検索 すると「検索条件に該当するデータが存在しません」でした。さすが中国、やってくれます。生産元が色々と変わる様なので、もう正規の技適マーク付きは入手が難しいのかもしれませんネ。

音質改善、再び

さて気を取り直し、昨年購入のストック品を引っ張り出し、アンプ IC(SC8002B)周りの回路定数を決め直しました。

ILI9341版とST7789版
ILI9341版とST7789版

まずは回路図と R7、R8、R9 のおさらいです。

出回っている2種類の 2.8インチ CYD のうち、ILI9341版は回路図と同じモノが付いている一方、ST7789版は R7 と R8 に 0Ω が付けられ、全く酷い音質でした。

音量端子の調整
音量端子の調整

この問題を改善するため、hexeguitar/ESP32_TFT_PIO では R7、R8 を回路図通りに、R9 は並列に接続した 47K〜100KΩ で調整し音質改善を図っています。

一方私は、依然ゲインが高く、高域が潰れた様に感じたので、JPOP 系を何曲か試聴し、R9 に並列な抵抗は 2KΩ を選びました。これ以上低いと音量が物足りず、高いとザラつき始めるギリギリの値です(個人の感想デス)。

440Hz サイン波のテスト
440Hz サイン波のテスト

さて今回、CYD 由来の派生品や類似品の回路図を調査し、また試行錯誤した結果、R7 は 0Ωのまま、R8 を回路図通りの 22kΩに、R9 を 10KΩにすることで、ほぼ前回同様の試聴感が得られました。付け替える 0603 抵抗が1つ減ったワケです。

高い周波数の再現性
高い周波数の再現性

ただ再生する周波数を上げていくと改修版でも再現性は悪く、ゲインが下がった分、目立たなくなっただけの様で、そもそも楽曲の再生には適さない IC であることが分かりました。

ということで、やはり音質にこだわるなら PCM5102A DACmacsbug さんの記事 のように、DAC とアンプは外付けが良さそうなので、そのうちトライしてみたいと思います。

ソフトウェアの解説

リポジトリの紹介

Github リポジトリ embedded-kiddie/CYD_MP3Player には以下を収載しました。

  • CYD_Audio
    PlatformIO 用に作られたオリジナルの ESP32 I2S audio libraryフォーク し、コンパイル時の警告やエラーを修正、スケッチブックフォルダの libraries にインストール可能な Arduino IDE 用ライブラリとしたバージョンです。

  • CYD_MP3Player_Basic
    CYD_Audio ライブラリを簡単に扱えるようにしたヘルパー関数の使用例です。詳しくは 前回記事 を参照ください。

  • CYD_MP3Player_Simple
    MP3Player という CYD_Audio ラッパークラスで、SD カード上のオーディオファイルをスキャンし、プレイリストを作成し、設定された順序で連続再生を制御する例です。

  • CYD_MP3Player_LVGL
    本記事で紹介する LVGL 版です。WiFi 機能を無効にし、SdFat 専用にカスタマイズした CYD_Audio を内包しています。

LVGL 版の構築方法

プラットフォームパッケージ

名称 バージョン
esp32 by Espressif Systems 2.0.17

バージョン 3.x では内蔵の I2S DAC が非推奨となり、耳障りなクリック音が出るため、2.0.17 が必須です。

またコンパイル時の設定は以下の通りです。

項目 設定
Partition Scheme “Huge App (3MB No OTA/1MB SPIFFS)”
Upload Speed “460800” (Mac), “921600” (Win)

ライブラリ

名称 バージョン
LVGL by kisvegabor 9.2.2 以降
LovyanGFX by lovyan03 1.2.7
SdFat by Bill Greiman 2.3.0
ArduinoJson by Benoit Blanchon 7.4.2
  • LVGL の設定
    まず、公式資料の Configure LVGL を参考に、スケッチブックフォルダの librarieslv_conf.h を 配置します。同ファイルの設定については README.md と各バージョンのサンプルを参考にして下さい。

  • SdFat の設定
    長いファイル名と日本語ファイル名を有効化するため、SdFat のインストールフォルダから SdFatConfig.h を開き、コメントアウトされているシンボル USE_UTF8_LONG_NAMES の定義を有効にして下さい。

アプリケーションの設定

IDE で config.h を開き、1. 〜 8. のコメントに沿って設定を編集して下さい。

  1. TFT Screen configuration
    残念ながら UI 画面はレスポンシブではなく、240x320 の設定は変更不可です 😞

  2. Graphic library configuration
    新し目の CYD 派生モデルでは、LovyanGFX の自動設定機能が働かないモノがあるようです。その場合は USE_AUTODETECTfalse に設定し、お持ちの LCD を設定して下さい。

  3. Touch panel calibration
    タッチ位置にズレがある場合は、一旦 USE_CALIBRATEDfalse を設定し、再コンパイル後に LovyanGFX のキャリブレーションを実行し、シリアルモニタに表示されるコードを CYD_MP3Player_LVGL.ino にコピペして下さい。その後 USE_CALIBRATED に戻すことを忘れずにネ。

  4. Custom fonts configuration
    既に色々な記号や JIS 第1水準と第2水準が入ってます。変更の必要がないことを祈ります。

  5. Path to the folder where MP3 audio files are saved
    MP3_ROOT_PATH に楽曲ファイルを格納する SD カードのルートパスを設定します。またよく使う楽曲ファイルの拡張子を MP3_FILE_EXT の先頭に移して下さい。SD カードの検索が少し早くなります。

  6. File that stores settings related to the operation of this player
    アプリケーションの設定を保存するファイル名を設定します。変更する場合は、ファイル名には @ から始まる文字列を設定して下さい。

  7. Partitions under “MP3_ROOT_PATH”
    SRAM 容量の制限により、1つのプレイリストに入る曲数を 600 程度に制限していますが、複数のサブフォルダに分けることで全体の収容数を増やせる仕様としています。本アプリではこのサブフォルダのことを「パーティション」と呼び、PARTITION_PATH にサブフォルダ名のテンプレートを設定します。デフォルトは単なる「数字」です。

  8. Album list configuration under “PARTITION_PATH”
    後述するアルバムリストの保存用設定です。特に変更する必要はないと思います。

使い方

SD カード内のファイル構成

本アプリは、CD をリッピングしたイメージ(アーティスト名/アルバム名/曲名で構成される階層構造)がそのまま SD に保存されることを想定しています。

[パーティションなし]              [パーティションあり]
MP3_ROOT_PATH/                 MP3_ROOT_PATH/
├── Artist1/                   ├── 1/
   ├── Arbum1.1/                 ├── Artist1/
      ├── 01 title01.mp3           ├── Arbum1.1/
      ├── 02 title02.mp3              ├── 01 title01.mp3
      ├── ...                         ├── 02 title02.mp3
      └── ...                         ├── ...
   ├── Arbum1.2/                       └── ...
      └── ...                      ├── Arbum1.2/
   └── ...                             └── ...
├── Artist2/                         └── ...
   ├── Arbum2.1                  ├── Artist2/
   └──...                           ├── Arbum2.1/
└── ...                              └── ...
└── ...                           └── ...
                               ├── 2/
                                  ├── Artist3/
                                     ├── ...

画面構成

全部で4つの画面からなり、それぞれスワイプ(または矢印ボタン)で遷移します。

全体の画面構成
全体の画面構成
全体の画面構成
  • メイン画面
    デザインセンスがゼロの MP3 プレイヤー画面です。Bluetooth ボタンは現在機能しません 😊

  • プレイリスト
    楽曲の一覧で、再生する曲を選択します。❤️ボタンで「お気に入り」の登録ができます。また演奏時間は最初「0:00」ですが、再生終了時に更新され SD カードに保存されます。

  • アルバムリスト
    プレイリストに入れるアルバムを選択します。ドロップダウンリストには “All” 以外に任意のリストが登録/編集/削除ができます。リスト名に日本語を使いたい場合は、前章 8. の ALBUM_LIST_FILE で設定した .txt ファイルを編集して下さい。

    アルバムリスト
    アルバムリスト
  • 設定画面
    パーティションの切り替え、LCD 消灯までの時間、及びスリープタイマーを設定します。

アルバムのカバー写真について

本アプリでは、予め埋め込まれた10枚の画像のうち1枚をランダムに選び、楽曲の再生中にメイン画面に表示します。この埋め込み画像を変えるには、各アルバムフォルダにお好みの画像を @photo.jpg として保存して下さい。

ただしスワイプのアニメーション中も オンザフライで JPEG 画像をデコードするので、サイズは 96x96 程度に、サイズは 6KB 以下に抑えて下さい。圧縮率は 50 〜 75% がお勧めです。

既知の問題点

コンパイル時の警告

ESP32 のコア 2.0.17 でコンパイルすると、次の警告が出ます。

/Users/xxxx/Documents/Arduino/libraries/SdFat/src/FsLib/FsNew.h:44:48: warning: optimization attribute on 'void* operator new(size_t, newalign_t*)' follows definition but the attribute doesn't match [-Wattributes]
 void* operator new(size_t size, newalign_t* ptr);
                                                ^
/Users/xxxx/Library/Arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/esp-2021r2-patch5-8.4.0/xtensa-esp32-elf/include/c++/8.4.0/new:168:14: note: previous definition of 'void* operator new(std::size_t, void*)' was here
 inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
              ^~~~~~~~

これは、new 演算子の例外に関する取り扱い方が SdFat と Espressif の C++ 標準ライブラリとで異なることが原因ですが、アプリケーション側でトラップしており、通常動作には影響ないので無視しておいて大丈夫です。

コンパイル時のリセット問題

私の環境は Mac なので、Win ユーザーには当てはまらないかもしれませんが… ESP32 のコア 2.0.17 でコンパイル中にリセットが掛かる事象を経験しています。そのまま再コンパイルしてもリンク時点でハングし、IDE を終了させることも出来なくなります。ターミナル APP で次のコマンドを実行し、IDE の全タスクを kill した後、IDE を再起動してみて下さい。

% ps -axc|grep -i arduino|awk '{print $1}'|xargs -I@ kill -9 @

演奏時間について

I2S オーディオライブラリによる推定値で、実際の演奏時間とはズレます。UI 演出上のギミックとお考え下さい。

アルバムのカバー写真が表示されない

JPEG 画像のデコードには LVGL に添付された Tiny JPEG Decoder をカスタマイズして使っています。画像の圧縮ツールによっては、まれに表示されない事象が発生しているので、画像の編集ソフトは GIMP を使うことをお勧めします。

SD カード内のドットファイル

Mac ユーザー特有の問題ですが、SD カードにドラッグ&ドロップでファイルをコピーすると . で始まるファイルが作成されます。特に大量の楽曲ファイルをコピーし続けると、. ファイルも同数生成されることになり、本アプリが SD カードをスキャンするときに余計な時間がかかることになります。

. で始まるファイルを一気に削除するには、ターミナル APP から SD カードの該当フォルダに移動し、以下を参考に find コマンドを実行して下さい。

% cd /Volumes/NO\ NAME/MP3/
% find . -name ".???*" -print -exec /bin/rm -rf {} \;

雑記

メモリ消費量と収容曲数について

曲の再生はコア0に生成した audioplay タスクの役割ですが、再生中にコア1の UI ループ中で SD カードを読み書きすると例外エラーが発生します。この競合問題の解消は難しいため、プレイリストに表示するデータは全てメモリ上に保持する仕様で逃げています。

またアルバムのカバー写真用に JPEG 画像のデコード用バッファ(4KB)とキャッシュ(6KB)も必要でした。

このため、あちらこちらから少しずつメモリをかき集めています。

コンポーネント 元のサイズ 縮小後のサイズ 稼いだサイズ
I2S 用オーディオタスクのスタック 約 10K 4K 約 6K
I2S 用 WiFi 機能 約 22K 0K 22K
LVGL 用描画バッファ 15KB 10KB 5K
LVGL ウィジェット用メモリ 48KB 40KB 8KB

他にも LVGL プログラミングの工夫により(「習熟」の目標はかなり達成できた気がします)、計算上はプレイリストに 750 曲余りを収容可能ですが、今後の Bluetooth 化に備え、今回は 600 曲程に抑えることとしました。

LVGL のプログラミング上の工夫については、別途まとめて報告したいと思います 😉

シャッフルモードについて

アルバムリストの画面で “All” を選んだ時の動作は次の通りです。

  • シャッフルが OFF の時
    ランダムに1つアルバムを選び、全曲をプレイリストに加えます。これをプレイリストの曲数が 600 を超えるまで繰り返します。アルバムをランダムに選ぶ理由は、プレイリストの偏りをなくす為です。

  • シャッフルが ON の時
    OFF と同じロジックでプレイリストを作成し、最後にプレイリスト全体をランダムに並べ替えます。1曲ずつランダムに選んでいるとプレイリストの作成に 10 倍の時間がかかるため、この様な仕様にしています。

最後に、お願い

試してみよう!という奇特な方、問題を見つけたり、改善点やご要望があれば、是非 Github やココに書き込みをお願いします 🙇‍♂️