Implement advanced gestures (1=next, 2=pause, 3=prev, 4=vol) and ESP32-audioI2S MP3/WAV playback
This commit is contained in:
@@ -1,186 +1,94 @@
|
||||
#include "Audio_Player.h"
|
||||
#include <ESP_I2S.h>
|
||||
#include <algorithm>
|
||||
#include "Audio.h"
|
||||
|
||||
I2SClass i2s;
|
||||
Audio audio;
|
||||
TaskHandle_t audioTaskHandle = NULL;
|
||||
File audioFile;
|
||||
volatile bool isAudioPlaying = false;
|
||||
char currentPlayPath[128];
|
||||
volatile bool isAudioTaskRunning = false;
|
||||
uint8_t currentVolume = 12; // 0 to 21 range
|
||||
|
||||
uint16_t Wav_Count = 0;
|
||||
char Wav_Names[100][100];
|
||||
uint16_t Audio_Count = 0;
|
||||
char Audio_Names[100][100];
|
||||
|
||||
void Search_Wavs(const char* directory) {
|
||||
Wav_Count = Folder_retrieval(directory, ".wav", Wav_Names, 100);
|
||||
}
|
||||
|
||||
|
||||
// Parse WAV header and skip meta chunks (like LIST) to find the "fmt " and "data" segments
|
||||
bool parseWavHeader(File& file, uint32_t& sampleRate, uint16_t& channels, uint16_t& bitsPerSample, uint32_t& dataStartOffset, uint32_t& dataSize) {
|
||||
char id[4];
|
||||
|
||||
// Read RIFF header
|
||||
file.seek(0);
|
||||
if (file.readBytes(id, 4) != 4 || memcmp(id, "RIFF", 4) != 0) {
|
||||
printf("WAV Header Error: RIFF signature not found!\r\n");
|
||||
return false;
|
||||
// FreeRTOS background task on Core 0 to run ESP32-audioI2S decode loop
|
||||
void audioLoopTask(void* parameter) {
|
||||
isAudioTaskRunning = true;
|
||||
while (isAudioTaskRunning) {
|
||||
audio.loop();
|
||||
vTaskDelay(pdMS_TO_TICKS(1)); // Yield to let other RTOS tasks run
|
||||
}
|
||||
|
||||
file.seek(8);
|
||||
if (file.readBytes(id, 4) != 4 || memcmp(id, "WAVE", 4) != 0) {
|
||||
printf("WAV Header Error: WAVE signature not found!\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t offset = 12;
|
||||
bool foundFmt = false;
|
||||
bool foundData = false;
|
||||
|
||||
while (offset < file.size() && (!foundFmt || !foundData)) {
|
||||
file.seek(offset);
|
||||
if (file.readBytes(id, 4) != 4) break;
|
||||
|
||||
uint32_t chunkSize;
|
||||
if (file.read((uint8_t*)&chunkSize, 4) != 4) break;
|
||||
|
||||
offset += 8; // move past chunk ID and Size field
|
||||
|
||||
if (memcmp(id, "fmt ", 4) == 0) {
|
||||
uint16_t audioFormat;
|
||||
file.read((uint8_t*)&audioFormat, 2);
|
||||
if (audioFormat != 1) { // 1 = PCM (uncompressed)
|
||||
printf("WAV Header Error: Only uncompressed PCM WAV is supported!\r\n");
|
||||
return false;
|
||||
}
|
||||
file.read((uint8_t*)&channels, 2);
|
||||
file.read((uint8_t*)&sampleRate, 4);
|
||||
file.seek(offset + 8); // skip byte rate and block align
|
||||
file.read((uint8_t*)&bitsPerSample, 2);
|
||||
foundFmt = true;
|
||||
}
|
||||
else if (memcmp(id, "data", 4) == 0) {
|
||||
dataStartOffset = offset;
|
||||
dataSize = chunkSize;
|
||||
foundData = true;
|
||||
}
|
||||
|
||||
offset += chunkSize;
|
||||
}
|
||||
|
||||
return foundFmt && foundData;
|
||||
}
|
||||
|
||||
void audioPlayTask(void* parameter) {
|
||||
uint32_t sampleRate = 44100;
|
||||
uint16_t channels = 2;
|
||||
uint16_t bitsPerSample = 16;
|
||||
uint32_t dataStartOffset = 44;
|
||||
uint32_t dataSize = 0;
|
||||
|
||||
audioFile = SD_MMC.open(currentPlayPath, "r");
|
||||
if (!audioFile) {
|
||||
printf("Failed to open audio file: %s\r\n", currentPlayPath);
|
||||
isAudioPlaying = false;
|
||||
audioTaskHandle = NULL;
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!parseWavHeader(audioFile, sampleRate, channels, bitsPerSample, dataStartOffset, dataSize)) {
|
||||
printf("Failed to parse WAV header\r\n");
|
||||
audioFile.close();
|
||||
isAudioPlaying = false;
|
||||
audioTaskHandle = NULL;
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("WAV File: Rate=%d Hz, Channels=%d, Bits=%d, Size=%d bytes\r\n",
|
||||
sampleRate, channels, bitsPerSample, dataSize);
|
||||
|
||||
// Configure I2S based on WAV parameters
|
||||
i2s_data_bit_width_t bitWidth = I2S_DATA_BIT_WIDTH_16BIT;
|
||||
if (bitsPerSample == 8) bitWidth = I2S_DATA_BIT_WIDTH_8BIT;
|
||||
else if (bitsPerSample == 24) bitWidth = I2S_DATA_BIT_WIDTH_24BIT;
|
||||
else if (bitsPerSample == 32) bitWidth = I2S_DATA_BIT_WIDTH_32BIT;
|
||||
|
||||
i2s_slot_mode_t slotMode = (channels == 1) ? I2S_SLOT_MODE_MONO : I2S_SLOT_MODE_STEREO;
|
||||
|
||||
// Set standard pins
|
||||
i2s.setPins(I2S_BCLK, I2S_LRCK, I2S_DIN, -1, -1);
|
||||
|
||||
// Stop any active driver configuration
|
||||
i2s.end();
|
||||
|
||||
if (!i2s.begin(I2S_MODE_STD, sampleRate, bitWidth, slotMode)) {
|
||||
printf("Failed to initialize I2S!\r\n");
|
||||
audioFile.close();
|
||||
isAudioPlaying = false;
|
||||
audioTaskHandle = NULL;
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Seek past the parsed headers to raw data
|
||||
audioFile.seek(dataStartOffset);
|
||||
|
||||
const size_t bufferSize = 1024;
|
||||
uint8_t buffer[bufferSize];
|
||||
uint32_t bytesRead = 0;
|
||||
uint32_t totalBytesWritten = 0;
|
||||
|
||||
while (isAudioPlaying && totalBytesWritten < dataSize) {
|
||||
size_t toRead = std::min(bufferSize, (size_t)(dataSize - totalBytesWritten));
|
||||
bytesRead = audioFile.read(buffer, toRead);
|
||||
if (bytesRead == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
size_t written = i2s.write(buffer, bytesRead);
|
||||
if (written == 0) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
continue;
|
||||
}
|
||||
totalBytesWritten += written;
|
||||
}
|
||||
|
||||
printf("Playback done. Written %d bytes.\r\n", totalBytesWritten);
|
||||
audioFile.close();
|
||||
i2s.end();
|
||||
isAudioPlaying = false;
|
||||
audioTaskHandle = NULL;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void playWav(const char* filePath) {
|
||||
if (isAudioPlaying) {
|
||||
stopWav();
|
||||
}
|
||||
strncpy(currentPlayPath, filePath, sizeof(currentPlayPath) - 1);
|
||||
currentPlayPath[sizeof(currentPlayPath) - 1] = '\0';
|
||||
isAudioPlaying = true;
|
||||
void initAudioPlayer() {
|
||||
audio.setPinout(I2S_BCLK, I2S_LRCK, I2S_DIN);
|
||||
audio.setVolume(currentVolume);
|
||||
|
||||
xTaskCreatePinnedToCore(
|
||||
audioPlayTask,
|
||||
"audioPlayTask",
|
||||
4096,
|
||||
NULL,
|
||||
5,
|
||||
&audioTaskHandle,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
void stopWav() {
|
||||
if (isAudioPlaying) {
|
||||
isAudioPlaying = false;
|
||||
while (audioTaskHandle != NULL) {
|
||||
vTaskDelay(pdMS_TO_TICKS(5));
|
||||
}
|
||||
// Start the background loop task on Core 0
|
||||
if (audioTaskHandle == NULL) {
|
||||
xTaskCreatePinnedToCore(
|
||||
audioLoopTask,
|
||||
"audioLoopTask",
|
||||
8192, // Stack size (ESP32-audioI2S requires a larger stack size for decoding)
|
||||
NULL,
|
||||
5, // Priority
|
||||
&audioTaskHandle,
|
||||
0 // Core 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bool isWavPlaying() {
|
||||
return isAudioPlaying;
|
||||
void playAudio(const char* filePath) {
|
||||
printf("AudioPlayer: Connecting to FS for file: %s\r\n", filePath);
|
||||
audio.connecttoFS(SD_MMC, filePath);
|
||||
}
|
||||
|
||||
void stopAudio() {
|
||||
audio.stopSong();
|
||||
}
|
||||
|
||||
void pauseResumeAudio() {
|
||||
audio.pauseResume();
|
||||
}
|
||||
|
||||
bool isAudioPlaying() {
|
||||
return audio.isRunning();
|
||||
}
|
||||
|
||||
void setPlayerVolume(uint8_t volume) {
|
||||
if (volume > 21) volume = 21;
|
||||
currentVolume = volume;
|
||||
audio.setVolume(currentVolume);
|
||||
printf("AudioPlayer: Volume set to %d / 21\r\n", currentVolume);
|
||||
}
|
||||
|
||||
uint8_t getPlayerVolume() {
|
||||
return currentVolume;
|
||||
}
|
||||
|
||||
void Search_Audio(const char* directory) {
|
||||
File Path = SD_MMC.open(directory);
|
||||
if (!Path) {
|
||||
printf("Path: <%s> does not exist\r\n", directory);
|
||||
Audio_Count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t fileCount = 0;
|
||||
File file = Path.openNextFile();
|
||||
while (file && fileCount < 100) {
|
||||
if (!file.isDirectory()) {
|
||||
const char* name = file.name();
|
||||
// Match WAV or MP3 files
|
||||
if (strstr(name, ".wav") || strstr(name, ".mp3") || strstr(name, ".WAV") || strstr(name, ".MP3")) {
|
||||
strncpy(Audio_Names[fileCount], name, sizeof(Audio_Names[fileCount]) - 1);
|
||||
Audio_Names[fileCount][sizeof(Audio_Names[fileCount]) - 1] = '\0';
|
||||
fileCount++;
|
||||
}
|
||||
}
|
||||
file = Path.openNextFile();
|
||||
}
|
||||
Path.close();
|
||||
Audio_Count = fileCount;
|
||||
printf("Found %d audio files (.mp3/.wav)\r\n", Audio_Count);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user