From 26c1bf9ed1ee6ca4ae4b9b5eee0bb2594c0f5baf Mon Sep 17 00:00:00 2001 From: GOB Date: Thu, 23 May 2024 11:54:03 +0900 Subject: [PATCH 1/2] Add speak_sd example for issue #149 --- examples/Basics/speak_sd/speak_sd.ino | 134 ++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 examples/Basics/speak_sd/speak_sd.ino diff --git a/examples/Basics/speak_sd/speak_sd.ino b/examples/Basics/speak_sd/speak_sd.ino new file mode 100644 index 00000000..52a4e75e --- /dev/null +++ b/examples/Basics/speak_sd/speak_sd.ino @@ -0,0 +1,134 @@ +/* + Play wav file from SD card + + SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + SPDX-License-Identifier: MIT + + **************************************************************** + Prepare a file named "speak_sd.wav" in the root of your SD card + Wav file format : 44100 16bit mono + + Example of conversion using ffmpeg + ffmpeg -i input.wav -vn -ac 1 -ar 44100 -acodec pcm_s16le -f wav speak_sd.wav + **************************************************************** +*/ +#include + +#define WAV_FILE_NAME "/speak_sd.wav" +File wavFile; +uint8_t buffer[1024]; +bool play{}; + +struct __attribute__((packed)) wav_header_t { + char RIFF[4]; + uint32_t chunk_size; + char WAVEfmt[8]; + uint32_t fmt_chunk_size; + uint16_t audiofmt; + uint16_t channel; + uint32_t sample_rate; + uint32_t byte_per_sec; + uint16_t block_size; + uint16_t bit_per_sample; +}; +struct __attribute__((packed)) sub_chunk_t { + char identifier[4]; + uint32_t chunk_size; +}; + +// Seek to the beginning of the audio data +bool seek_file(File& f) { + f.seek(0); + + // Read header + wav_header_t wheader{}; + if (f.read((uint8_t*)&wheader, sizeof(wheader)) != sizeof(wheader)) { + return false; + } + +#if 0 + ESP_LOGI("wav", "RIFF : %.4s" , wheader.RIFF ); + ESP_LOGI("wav", "chunk_size : %d" , wheader.chunk_size ); + ESP_LOGI("wav", "WAVEfmt : %.8s" , wheader.WAVEfmt ); + ESP_LOGI("wav", "fmt_chunk_size : %d" , wheader.fmt_chunk_size); + ESP_LOGI("wav", "audiofmt : %d" , wheader.audiofmt ); + ESP_LOGI("wav", "channel : %d" , wheader.channel ); + ESP_LOGI("wav", "sample_rate : %d" , wheader.sample_rate ); + ESP_LOGI("wav", "byte_per_sec : %d" , wheader.byte_per_sec ); + ESP_LOGI("wav", "block_size : %d" , wheader.block_size ); + ESP_LOGI("wav", "bit_per_sample : %d" , wheader.bit_per_sample); +#endif + + // Check format + // 44100 16bit mono linear PCM + if (memcmp(wheader.RIFF, "RIFF", 4) || + memcmp(wheader.WAVEfmt, "WAVEfmt ", 8) || wheader.audiofmt != 1 || + wheader.sample_rate != 44100 || wheader.bit_per_sample != 16 || + wheader.channel != 1) { + Serial.printf("Illegal format"); + return false; + } + + // Find data chunk + sub_chunk_t c{}; + while (true) { + if (f.read((uint8_t*)&c, sizeof(c)) != sizeof(c)) { + return false; + } + if (memcmp(c.identifier, "data", 4) == 0) { + break; + } + f.seek(f.position() + c.chunk_size); + } + return true; +} + +void streaming(File& f) { + // No more read + if (!f.available()) { + return; + } + // Read and play + auto readed = f.read(buffer, sizeof(buffer)); + // I2S write is blocking until the end of write + M5.Spk.PlaySound(buffer, readed); +} + +void setup() { + // LCDEnable,SDEnable,SerialEnable, I2CEnable, mbus_mode_t,SpeakerEnable + M5.begin(true, true, true, true, mbus_mode_t::kMBusModeOutput, true); + + M5.Lcd.setTextColor(WHITE); + M5.Lcd.setCursor(10, 10); + M5.Lcd.printf("Speak Test!"); + + wavFile = SD.open(WAV_FILE_NAME); + if (!wavFile) { + M5.Lcd.clear(TFT_RED); + M5.Lcd.setCursor(10, 10); + M5.Lcd.printf("Failed to open"); + while (true) { + delay(1000); + } + } + + play = seek_file(wavFile); + if (!play) { + M5.Lcd.setCursor(10, 10); + M5.Lcd.printf("Illegal format"); + } + M5.Lcd.clear(play ? TFT_DARKGREEN : TFT_PURPLE); +} + +void loop() { + if (play) { + streaming(wavFile); + + auto pos = M5.Touch.getPressPoint(); + if (pos.x >= 0 && pos.x < M5.Lcd.width() && pos.y >= 0 && + pos.y < M5.Lcd.height()) { + Serial.println("Repeat"); + seek_file(wavFile); + } + } +} From c35a052ad886027e0257c2f6456fc069fa16267e Mon Sep 17 00:00:00 2001 From: GOB Date: Mon, 1 Jul 2024 19:49:02 +0900 Subject: [PATCH 2/2] Fixes assume that the resource files on the GitHub wiki are downloaded and used --- examples/Basics/speak_sd/speak_sd.ino | 36 ++++++++++++++++----------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/examples/Basics/speak_sd/speak_sd.ino b/examples/Basics/speak_sd/speak_sd.ino index 52a4e75e..e41c8c80 100644 --- a/examples/Basics/speak_sd/speak_sd.ino +++ b/examples/Basics/speak_sd/speak_sd.ino @@ -1,23 +1,27 @@ /* - Play wav file from SD card - - SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD - SPDX-License-Identifier: MIT - - **************************************************************** - Prepare a file named "speak_sd.wav" in the root of your SD card - Wav file format : 44100 16bit mono - - Example of conversion using ffmpeg - ffmpeg -i input.wav -vn -ac 1 -ar 44100 -acodec pcm_s16le -f wav speak_sd.wav - **************************************************************** + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + * + * Play wav file from SD card + * + **************************************************************** + * Prepare a file named "resources_speak_sd.wav" in the root of your SD card + * Wav file format : 44100Hz 16bit mono + * + * Download it from https://github.com/m5stack/M5Core2/wiki + * or you can make it. + * Example of conversion using ffmpeg + * ffmpeg -i input.wav -vn -ac 1 -ar 44100 -acodec pcm_s16le -f wav resources_speak_sd.wav + **************************************************************** */ #include -#define WAV_FILE_NAME "/speak_sd.wav" +#define WAV_FILE_NAME "/resources_speak_sd.wav" + File wavFile; uint8_t buffer[1024]; -bool play{}; +bool play{}, play_loop{true}; struct __attribute__((packed)) wav_header_t { char RIFF[4]; @@ -86,6 +90,10 @@ bool seek_file(File& f) { void streaming(File& f) { // No more read if (!f.available()) { + // rewind to top (playback loop) + if (play_loop) { + seek_file(f); + } return; } // Read and play