diff --git a/README.md b/README.md index 2362bbd..96300fd 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ se synchroniser avec le firmware et potentiellement le mettre à jour. - [x] LCD (base) - [x] LCD (animations) - [ ] LCD (UI) - - [ ] Audio (sons de figurines) + - [x] Audio (sons de figurines) - [ ] Audio (sons d'UI) - [ ] Audio (gain ?) - [ ] Communication avec logiciel @@ -81,7 +81,7 @@ détaillés ici. ## Structure de fichiers -Les fichiers sur la carte SD sont structurés comme suit : +Les fichiers sur la carte SD sont structurés comme suit (**notez les majuscules**) : ``` / ├── RANDONNEE/ @@ -91,16 +91,16 @@ Les fichiers sur la carte SD sont structurés comme suit : │ │ ├── 02 │ │ ├── .. │ │ └── XX -│ └── audio.wav +│ └── ENTRY.WAV ├── RESTO/ │ ├── ANIM/ │ │ └── .. -│ └── audio.wav +│ └── ENTRY.WAV ├── .../ └── SYS/ ├── ANIM*/ ├── ../ - └── *.wav + └── *.WAV ``` Tous les fichiers liés à une figurine en particulier doivent être placés dans un diff --git a/include/LCD.h b/include/LCD.h index b3111a1..e84c51c 100644 --- a/include/LCD.h +++ b/include/LCD.h @@ -11,6 +11,7 @@ #include +#include "Audio.h" #include "Com.h" #include "IDs.h" #include "Pinout.h" diff --git a/include/Speaker.h b/include/Speaker.h new file mode 100644 index 0000000..722965a --- /dev/null +++ b/include/Speaker.h @@ -0,0 +1,48 @@ +// +// Created by Teo-CD on 22/10/23. +// + +#ifndef JIN_BARBAPAPA_SPEAKER_H +#define JIN_BARBAPAPA_SPEAKER_H + +#include +#include +#include +#include +#include +#include + +#include "Com.h" +#include "IDs.h" +#include "Pinout.h" + +static constexpr uint8_t audioMemReservation = 10; + +class Speaker { +public: + Speaker() : sd(SD) {}; + static void init(); + + /** Play the entry sound for a specific tag ID. */ + void playNewSound(int8_t ID); + + /** Check if a sound and playing an disable the amp if not anymore. */ + bool checkPlayingAndDisable(); +private: + SDClass &sd; + + /* + * Below are members needed to set up the audio pipeline. + * We need an input, output, and to connect them to one another. + * We also need some specially reserved memory to store the data to playback. + */ + static audio_block_t audioMemory[audioMemReservation]; + static bool memoryInitialized; + AudioPlaySdWav sdWavSource; + AudioOutputI2S i2sOut; + AudioConnection connectLeftChannels{sdWavSource, 0, i2sOut, 0}; + AudioConnection connectRightChannels{sdWavSource, 1, i2sOut, 1}; +}; + + +#endif //JIN_BARBAPAPA_SPEAKER_H diff --git a/src/LCD.cpp b/src/LCD.cpp index 0b9e478..9c08678 100644 --- a/src/LCD.cpp +++ b/src/LCD.cpp @@ -47,14 +47,23 @@ bool LCD::checkAndDisplay() { if (!animDir || millis() - lastFrameTime < frameTarget) return false; + /* + * Audio playback is controlled by interrupts, so it can happen anytime. + * Prevent a potential conflict when reading a frame of animation from the + * SD card and being interrupted by the audio library, which will try to + * read audio data from the SD card at the same time. + */ + AudioNoInterrupts(); File frame = animDir.openNextFile(); if (!frame) { /* There are no frames anymore, the animation is complete. */ animDir.close(); + AudioInterrupts(); return false; } frame.read(bitmap, 528); frame.close(); + AudioInterrupts(); lcd.clearDisplay(); lcd.drawBitmap(0, 0, bitmap, 84, 48, 1); diff --git a/src/Speaker.cpp b/src/Speaker.cpp new file mode 100644 index 0000000..d361f2e --- /dev/null +++ b/src/Speaker.cpp @@ -0,0 +1,49 @@ +// +// Created by Teo-CD on 22/10/23. +// + +#include "Speaker.h" + +DMAMEM audio_block_t Speaker::audioMemory[audioMemReservation]; +bool Speaker::memoryInitialized = false; + +void Speaker::init() { + if (memoryInitialized) + return; + AudioStream::initialize_memory(audioMemory, audioMemReservation); + memoryInitialized = true; +} + +void Speaker::playNewSound(int8_t ID) { + char filePath[20]; + uint8_t rawID = getRawId(ID); + + if (!SD.mediaPresent()) { + Com::sendComment("SD Card not present, cannot play sound"); + return; + } + if (!isValidId(ID)) { + Com::sendComment("Unknown ID for sound : %d", rawID); + return; + } + + if (sdWavSource.isPlaying()) + sdWavSource.stop(); + + snprintf(filePath, 20, "%s/ENTRY.WAV", + (isIdCharacter(ID) ? charDirs : promoDirs)[rawID]); + + if (!sd.exists(filePath)) { + Com::sendComment("File %s not present, cannot play sound.", filePath); + return; + } + digitalWrite(pin_Audio_Amp_enable, HIGH); + sdWavSource.play(filePath); +} + +bool Speaker::checkPlayingAndDisable() { + bool isPlaying = sdWavSource.isPlaying(); + if (!isPlaying) + digitalWrite(pin_Audio_Amp_enable, LOW); + return isPlaying; +} diff --git a/src/main.cpp b/src/main.cpp index 6615fef..4214db5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,6 +5,7 @@ #include "Com.h" #include "LCD.h" #include "RFID.h" +#include "Speaker.h" __attribute__((noreturn)) int main() { pinMode(pin_DBG_LED_1, OUTPUT); @@ -40,7 +41,6 @@ __attribute__((noreturn)) int main() { Serial.begin(115200); Com::sendComment("System is powered up, running set-up."); - /* TODO: Setups once module structure is up. */ RFID rfid; digitalWrite(pin_NFC1_RST, HIGH); rfid.init(); @@ -52,17 +52,28 @@ __attribute__((noreturn)) int main() { digitalWrite(pin_LCD_RST, HIGH); lcd.init(); + /* Don't enable the amp here, only do it when playing to save some power. */ + Speaker::init(); + Speaker speaker; + + Com::sendComment("All modules initialized, entering main loop."); + /* Main loop */ while (true) { int8_t tagEvent; /* Display first, in case the RFID communication delays it too much. */ lcd.checkAndDisplay(); + speaker.checkPlayingAndDisable(); tagEvent = rfid.checkTags(); if (tagEvent) { + tagEvent = 4; Com::sendFigUpdate(tagEvent); + /* Start the audio first because of the possible WAV parsing delay. */ + speaker.playNewSound(tagEvent); lcd.startNewAnim(tagEvent); + } /* TODO: Drop delay, WFE+timer interrupt(s) ? */