It's working but I don't know why the sound is cracking.
Any advice ?
Code: Select all
#include "Arduino.h"
#include "Audio.h"
#include "FS.h"
#include "SPI.h"
#include "SD.h"
#include <Wire.h>
#include <vector>
#include <lvgl.h>
#include <ui.h>
#include <Arduino_GFX_Library.h>
#define TFT_BL 2
#define SD_SCK 12 // CLK
#define SD_MISO 13 // D0
#define SD_MOSI 11 // CMD
#define SD_CS 10 // CLK
#define I2S_DOUT 17
#define I2S_BCLK 0
#define I2S_LRC 18
#define GFX_BL DF_GFX_BL // default backlight pin, you may replace DF_GFX_BL to actual backlight pin
#if defined(DISPLAY_DEV_KIT)
Arduino_GFX *gfx = create_default_Arduino_GFX();
#else /* !defined(DISPLAY_DEV_KIT) */
Arduino_ESP32RGBPanel *bus = new Arduino_ESP32RGBPanel(
GFX_NOT_DEFINED /* CS */, GFX_NOT_DEFINED /* SCK */, GFX_NOT_DEFINED /* SDA */,
40 /* DE */, 41 /* VSYNC */, 39 /* HSYNC */, 42 /* PCLK */,
45 /* R0 */, 48 /* R1 */, 47 /* R2 */, 21 /* R3 */, 14 /* R4 */,
5 /* G0 */, 6 /* G1 */, 7 /* G2 */, 15 /* G3 */, 16 /* G4 */, 4 /* G5 */,
8 /* B0 */, 3 /* B1 */, 46 /* B2 */, 9 /* B3 */, 1 /* B4 */
);
// option 1:
// ST7262 IPS LCD 800x480
Arduino_RPi_DPI_RGBPanel *gfx = new Arduino_RPi_DPI_RGBPanel(
bus,
800 /* width */, 0 /* hsync_polarity */, 8 /* hsync_front_porch */, 4 /* hsync_pulse_width */, 8 /* hsync_back_porch */,
480 /* height */, 0 /* vsync_polarity */, 8 /* vsync_front_porch */, 4 /* vsync_pulse_width */, 8 /* vsync_back_porch */,
1 /* pclk_active_neg */, 12000000 /* prefer_speed */, true /* auto_flush */);
#endif /* !defined(DISPLAY_DEV_KIT) */
/*******************************************************************************
* End of Arduino_GFX setting
******************************************************************************/
/*******************************************************************************
* Please config the touch panel in touch.h
******************************************************************************/
#include "touch.h"
Audio audio;
File musical;
std::vector<String> mp3Files;
int currentSongIndex = -1;
bool isPlaying = false;
bool playbackFinished = false; // Flag to detect playback completion
struct Music_info
{
String name;
int length;
int runtime;
int volume;
int status;
int mute_volume;
} music_info = {"", 0, 0, 0, 0, 0};
// Structure for storing file information
struct FileInfo
{
String parentFolder;
String currentFolder;
String shortFileName;
String fullPath;
};
std::vector<FileInfo> fileInfoList; // Store file information
// Structure for storing folder information
struct FolderInfo
{
String parentFolder;
String currentFolder;
std::vector<FileInfo> files;
};
std::vector<FolderInfo> folderInfoList; // Store folder information
/* Change to your screen resolution */
static uint32_t screenWidth;
static uint32_t screenHeight;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t *disp_draw_buf;
static lv_disp_drv_t disp_drv;
// Add a variable to keep track of the current folder
String currentFolder = "/m"; // Start with the root folder
void setup()
{
// Hardware initialization
pin_init();
sd_init();
// Init Display
gfx->begin();
gfx->fillScreen(RED);
delay(500);
gfx->fillScreen(GREEN);
delay(500);
gfx->fillScreen(BLUE);
delay(500);
gfx->fillScreen(BLACK);
delay(500);
lv_init();
delay(10);
touch_init();
screenWidth = gfx->width();
screenHeight = gfx->height();
#ifdef ESP32
disp_draw_buf = (lv_color_t *)heap_caps_malloc(sizeof(lv_color_t) * screenWidth * screenHeight / 4, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
#else
disp_draw_buf = (lv_color_t *)malloc(sizeof(lv_color_t) * screenWidth * screenHeight / 4);
#endif
if (!disp_draw_buf)
{
Serial.println("LVGL disp_draw_buf allocate failed!");
}
else
{
lv_disp_draw_buf_init(&draw_buf, disp_draw_buf, NULL, screenWidth * screenHeight / 4);
/* Initialize the display */
lv_disp_drv_init(&disp_drv);
/* Change the following line to your display resolution */
disp_drv.hor_res = screenWidth;
disp_drv.ver_res = screenHeight;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
/* Initialize the (dummy) input device driver */
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = my_touchpad_read;
lv_indev_drv_register(&indev_drv);
// Loading interface
ui_init();
lv_obj_add_event_cb(ui_btPrevious, playPreviousSong, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(ui_btPlay, playNextSong, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(ui_btPause, tooglePlayPause, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(ui_btNext, playNextSong, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(ui_btStop, stopSong, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(ui_btHome, ui_event_btHome, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(ui_btUp, goToPreviousFolder, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(ui_btDown, goToNextFolder, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(ui_btHistory, ui_event_btHistory, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(ui_btMusical, setMusicalMode, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(ui_sliderVolume, changeVolume, LV_EVENT_VALUE_CHANGED, NULL);
// Setup MP3
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
audio.setVolume(7); // Adjust the initial volume as needed
// Set the root directory to "/m"
//setRootDirectory(currentFolder); // Set the root directory to the current folder
//printFolderInfoList();
// Add more debugging statements
//Serial.print("After setRootDirectory: ");
//Serial.println(fileInfoList.size());
// Play the first MP3 file if available
//playFirstMP3IfAvailable();
// Add a delay to ensure serial output is visible before the loop starts
//delay(1000); // You can adjust the delay time as needed
}
}
void loop()
{
// Your other main loop code can go here
audio.loop();
lv_event_t lv_event;
checkAndPlayNextSong(&lv_event);
// Check if playback is finished
if (playbackFinished && isPlaying)
{
isPlaying = false;
playbackFinished = false; // Reset the flag
}
lv_timer_handler(); /* let the GUI do its work */
delay(2);
}
// Functions
/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
#if (LV_COLOR_16_SWAP != 0)
gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
#else
gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
#endif
lv_disp_flush_ready(disp);
}
void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{
if (touch_has_signal())
{
if (touch_touched())
{
data->state = LV_INDEV_STATE_PR;
/*Set the coordinates*/
data->point.x = touch_last_x;
data->point.y = touch_last_y;
}
else if (touch_released())
{
data->state = LV_INDEV_STATE_REL;
}
}
else
{
data->state = LV_INDEV_STATE_REL;
}
}
// Function to play an MP3 file
void setMusicalMode(lv_event_t *e)
{
currentFolder = "/m"; // Set the current folder to /m
setRootDirectory(currentFolder); // Set the root directory to /m
playFirstMP3IfAvailable(); // Play the first MP3 file in the new folder if available
}
void playFirstMP3IfAvailable()
{
if (!fileInfoList.empty())
{
currentSongIndex = 0; // Start with the first MP3 file
const String &firstMP3 = fileInfoList[currentSongIndex].fullPath;
currentFolder = fileInfoList[currentSongIndex].currentFolder; // Update currentFolder
playMP3(firstMP3);
}
}
void playMP3(const String &mp3FilePath)
{
const char *mp3Path = mp3FilePath.c_str();
audio.connecttoSD(mp3Path);
updateTitleLabel();
isPlaying = true;
playbackFinished = false; // Reset the flag
}
void checkAndPlayNextSong(lv_event_t *event)
{
if (isPlaying && !audio.isRunning())
{
// Current MP3 file has finished playing, play the next one if available
currentSongIndex++;
if (currentSongIndex < fileInfoList.size())
{
const String &nextMP3 = fileInfoList[currentSongIndex].fullPath;
playMP3(nextMP3);
}
else
{
// All MP3 files in the folder have been played
isPlaying = false;
}
}
}
void playNextSong(lv_event_t *event)
{
if (currentSongIndex >= 0 && currentSongIndex < fileInfoList.size() - 1)
{
currentSongIndex++;
const String &nextMP3 = fileInfoList[currentSongIndex].fullPath;
playMP3(nextMP3);
}
else if (!fileInfoList.empty())
{
// At the last song, play the first song of the folder
currentSongIndex = 0;
const String &firstMP3 = fileInfoList[currentSongIndex].fullPath;
playMP3(firstMP3);
}
}
void playPreviousSong(lv_event_t *event)
{
if (currentSongIndex > 0)
{
currentSongIndex--;
const String &prevMP3 = fileInfoList[currentSongIndex].fullPath;
playMP3(prevMP3);
}
else
{
// Already at the first song, play it again or handle as needed
const String &firstMP3 = fileInfoList[0].fullPath;
playMP3(firstMP3);
}
}
void replayCurrentSong()
{
if (currentSongIndex >= 0 && currentSongIndex < fileInfoList.size())
{
const String ¤tMP3 = fileInfoList[currentSongIndex].fullPath;
playMP3(currentMP3);
isPlaying = true;
}
else
{
// No song is currently playing, so start playing the first one
playFirstMP3IfAvailable();
}
}
void stopSong(lv_event_t *event) {
audio.stopSong();
isPlaying = false;
}
void tooglePlayPause(lv_event_t *event) {
audio.pauseResume();
if (isPlaying) {
isPlaying = false;
} else {
isPlaying = true;
}
}
void updateTitleLabel() {
if (currentSongIndex >= 0 && currentSongIndex < fileInfoList.size()) {
String album = currentFolder; // Use the current folder as the album name
int lastSlashIndex = album.lastIndexOf('/');
if (lastSlashIndex != -1) {
album = album.substring(lastSlashIndex + 1);
}
String title = fileInfoList[currentSongIndex].shortFileName;
lv_label_set_text(ui_labelAlbum, album.c_str());
lv_label_set_text(ui_labelTitle, title.c_str());
} else {
lv_label_set_text(ui_labelAlbum, "");
lv_label_set_text(ui_labelTitle, "");
}
}
void changeVolume(lv_event_t *event) {
lv_event_code_t code = lv_event_get_code(event);
if (code == LV_EVENT_VALUE_CHANGED) {
int volume = lv_slider_get_value(ui_sliderVolume);
audio.setVolume(volume);
char volumeText[8];
snprintf(volumeText, sizeof(volumeText), "%d", volume);
lv_label_set_text(ui_labelVolume, volumeText);
}
}
void goToPreviousFolder(lv_event_t *event)
{
if (folderInfoList.empty()) {
return;
}
int currentFolderIndex = -1;
for (int i = 0; i < folderInfoList.size(); i++) {
if (folderInfoList[i].currentFolder == currentFolder) {
currentFolderIndex = i;
break;
}
}
if (currentFolderIndex <= 0) {
currentFolderIndex = folderInfoList.size() - 1;
} else {
currentFolderIndex--;
}
currentFolder = folderInfoList[currentFolderIndex].currentFolder;
fileInfoList = folderInfoList[currentFolderIndex].files;
currentSongIndex = -1;
playFirstMP3IfAvailable();
}
void goToNextFolder(lv_event_t *event)
{
if (folderInfoList.empty()) {
return;
}
int currentFolderIndex = -1;
for (int i = 0; i < folderInfoList.size(); i++) {
if (folderInfoList[i].currentFolder == currentFolder) {
currentFolderIndex = i;
break;
}
}
if (currentFolderIndex == -1) {
return;
}
if (currentFolderIndex == folderInfoList.size() - 1) {
currentFolderIndex = 0;
currentFolder = folderInfoList[currentFolderIndex].currentFolder;
fileInfoList = folderInfoList[currentFolderIndex].files;
} else {
currentFolderIndex++;
currentFolder = folderInfoList[currentFolderIndex].currentFolder;
fileInfoList = folderInfoList[currentFolderIndex].files;
}
updateTitleLabel();
currentSongIndex = -1;
playFirstMP3IfAvailable();
int currentIndex = currentFolderIndex;
}
void printFolderInfoList() {
Serial.println("Folder Information List:");
for (int i = 0; i < folderInfoList.size(); i++) {
Serial.println("Folder Index: " + String(i));
Serial.println("Parent Folder: " + folderInfoList[i].parentFolder);
Serial.println("Current Folder: " + folderInfoList[i].currentFolder);
}
}
void setRootDirectory(const String &rootDir)
{
folderInfoList.clear();
fileInfoList.clear();
File root = SD.open(rootDir);
if (!root)
{
Serial.println("Failed to open root directory.");
return;
}
collectFolderInfo(root, "", rootDir);
fileInfoList = folderInfoList[0].files; // Set fileInfoList based on the root folder
root.close();
}
void collectFolderInfo(fs::File dir, String parentFolder, String currentFolder)
{
FolderInfo folderInfo;
folderInfo.parentFolder = parentFolder;
folderInfo.currentFolder = currentFolder;
while (File entry = dir.openNextFile())
{
String entryName = entry.name();
if (entry.isDirectory())
{
String subfolder = currentFolder + "/" + entryName;
Serial.print("Entering folder: ");
Serial.println(subfolder);
collectFolderInfo(entry, currentFolder, subfolder);
}
else
{
String fileExtension = entryName.substring(entryName.lastIndexOf('.') + 1);
// Check if the file has an MP3 extension (you can add other audio file extensions if needed)
if (fileExtension.equalsIgnoreCase("mp3"))
{
FileInfo fileInfo;
fileInfo.parentFolder = parentFolder;
fileInfo.currentFolder = currentFolder;
fileInfo.shortFileName = entryName;
fileInfo.fullPath = currentFolder + "/" + entryName;
fileInfoList.push_back(fileInfo);
}
}
entry.close();
}
if (currentFolder != "/m" && currentFolder != "/h") {
folderInfo.files = fileInfoList;
folderInfoList.push_back(folderInfo);
}
fileInfoList.clear();
Serial.print("File count in ");
Serial.print(currentFolder);
Serial.print(": ");
Serial.println(folderInfo.files.size());
}
void pin_init()
{
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
}
void sd_init()
{
pinMode(SD_CS, OUTPUT);
digitalWrite(SD_CS, HIGH);
SPI.begin(SD_SCK, SD_MISO, SD_MOSI);
SPI.setFrequency(400000);
if (!SD.begin(SD_CS, SPI))
{
Serial.println("Card Mount Failed");
while (1)
;
}
else
{
Serial.println("SD OK");
}
}