Need help to optimise + debug because sound is cracking

headquaker
Posts: 20
Joined: Thu Sep 21, 2023 6:28 am

Need help to optimise + debug because sound is cracking

Postby headquaker » Sun Oct 08, 2023 9:28 am

Here the full code of my project to play mp3.
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 &currentMP3 = 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");
    }
}
Last edited by headquaker on Mon Oct 09, 2023 8:41 am, edited 1 time in total.

lbernstone
Posts: 831
Joined: Mon Jul 22, 2019 3:20 pm

Re: Need help top optimise + debug because sound is cracking

Postby lbernstone » Sun Oct 08, 2023 5:25 pm

Don't update the screen every time you go through the loop. 250ms is barely noticeable for a human, but will free up a lot of processing time for the device.

headquaker
Posts: 20
Joined: Thu Sep 21, 2023 6:28 am

Re: Need help top optimise + debug because sound is cracking

Postby headquaker » Mon Oct 09, 2023 7:36 am

lbernstone wrote:
Sun Oct 08, 2023 5:25 pm
Don't update the screen every time you go through the loop. 250ms is barely noticeable for a human, but will free up a lot of processing time for the device.
Hello, what do you mean ? I can't see 250ms somewhere.

lbernstone
Posts: 831
Joined: Mon Jul 22, 2019 3:20 pm

Re: Need help to optimise + debug because sound is cracking

Postby lbernstone » Mon Oct 09, 2023 5:02 pm

250ms is the frequency I am recommending for everything in the loop except for the audio processing. Use a Ticker, or a simple millis() counter.

headquaker
Posts: 20
Joined: Thu Sep 21, 2023 6:28 am

Re: Need help to optimise + debug because sound is cracking

Postby headquaker » Tue Oct 10, 2023 6:04 am

Hello thanks, could you explain please? I understand what you mean but not really how to use it.
I think i should put the ticker in the loop() but how to use it ?

headquaker
Posts: 20
Joined: Thu Sep 21, 2023 6:28 am

Re: Need help to optimise + debug because sound is cracking

Postby headquaker » Thu Oct 12, 2023 6:54 am

any help please ?

headquaker
Posts: 20
Joined: Thu Sep 21, 2023 6:28 am

Re: Need help to optimise + debug because sound is cracking

Postby headquaker » Thu Oct 12, 2023 5:50 pm

lbernstone wrote:
Mon Oct 09, 2023 5:02 pm
250ms is the frequency I am recommending for everything in the loop except for the audio processing. Use a Ticker, or a simple millis() counter.
Hello thanks, could you explain please? I understand what you mean but not really how to use it.
I think i should put the ticker in the loop() but how to use it ?


headquaker
Posts: 20
Joined: Thu Sep 21, 2023 6:28 am

Re: Need help to optimise + debug because sound is cracking

Postby headquaker » Mon Oct 16, 2023 7:48 am

Hello
okay... but it does not explain in what it could help me... and how should I use it

Who is online

Users browsing this forum: lbernstone and 65 guests