Files
sunflower-girl/sound_picture_app/Audio_Player.cpp

187 lines
4.9 KiB
C++

#include "Audio_Player.h"
#include <ESP_I2S.h>
#include <algorithm>
I2SClass i2s;
TaskHandle_t audioTaskHandle = NULL;
File audioFile;
volatile bool isAudioPlaying = false;
char currentPlayPath[128];
uint16_t Wav_Count = 0;
char Wav_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;
}
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;
xTaskCreatePinnedToCore(
audioPlayTask,
"audioPlayTask",
4096,
NULL,
5,
&audioTaskHandle,
0
);
}
void stopWav() {
if (isAudioPlaying) {
isAudioPlaying = false;
while (audioTaskHandle != NULL) {
vTaskDelay(pdMS_TO_TICKS(5));
}
}
}
bool isWavPlaying() {
return isAudioPlaying;
}