From 17f7254483f8f4d9beff98d3ae183e1d4fd937a5 Mon Sep 17 00:00:00 2001 From: bernd Date: Sat, 30 Oct 2021 16:39:42 +0200 Subject: [PATCH 01/32] Refactor and bug fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor all into classes * use enums for better readable code * remove global variables * fix issue #37 (Rückwärtsspulen im Spezialmodus (Album)) * fix issue #87 (mute because 3 button pressed when adminmenü locked) * fix issue #70 (Don't leak memory when removing modifiers) * add startup jingle --- .gitignore | 9 + Tonuino.ino | 1778 +-------------------------------------- sd-card/advert/0262.mp3 | Bin 0 -> 6407 bytes sd-card/mp3/0262.mp3 | Bin 0 -> 6407 bytes src/array.hpp | 46 + src/buttons.cpp | 176 ++++ src/buttons.hpp | 55 ++ src/chip_card.cpp | 244 ++++++ src/chip_card.hpp | 67 ++ src/modifier.cpp | 103 +++ src/modifier.hpp | 125 +++ src/mp3.cpp | 112 +++ src/mp3.hpp | 144 ++++ src/settings.cpp | 110 +++ src/settings.hpp | 38 + src/tonuino.cpp | 886 +++++++++++++++++++ src/tonuino.hpp | 106 +++ 17 files changed, 2234 insertions(+), 1765 deletions(-) create mode 100644 sd-card/advert/0262.mp3 create mode 100644 sd-card/mp3/0262.mp3 create mode 100644 src/array.hpp create mode 100644 src/buttons.cpp create mode 100644 src/buttons.hpp create mode 100644 src/chip_card.cpp create mode 100644 src/chip_card.hpp create mode 100644 src/modifier.cpp create mode 100644 src/modifier.hpp create mode 100644 src/mp3.cpp create mode 100644 src/mp3.hpp create mode 100644 src/settings.cpp create mode 100644 src/settings.hpp create mode 100644 src/tonuino.cpp create mode 100644 src/tonuino.hpp diff --git a/.gitignore b/.gitignore index 0aab59b2..a8eeaf96 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,12 @@ Temporary Items # TonUINO /tools/*.pyc +/Release/ +/sc-card_priv/ +/sloeber.ino.cpp +/spec.d +/.cproject +/.project +/.pydevproject +/.sproject +/.settings diff --git a/Tonuino.ino b/Tonuino.ino index c3ce9c9b..94f656e1 100644 --- a/Tonuino.ino +++ b/Tonuino.ino @@ -1,10 +1,9 @@ -#include -#include -#include -#include -#include -#include -#include +#include "src/tonuino.hpp" + +#include "src/settings.hpp" +#include "src/mp3.hpp" +#include "src/buttons.hpp" + /* _____ _____ _____ _____ _____ @@ -17,714 +16,20 @@ Information and contribution at https://tonuino.de. */ -// uncomment the below line to enable five button support -//#define FIVEBUTTONS - -static const uint32_t cardCookie = 322417479; - -// DFPlayer Mini -SoftwareSerial mySoftwareSerial(2, 3); // RX, TX -uint16_t numTracksInFolder; -uint16_t currentTrack; -uint16_t firstTrack; -uint8_t queue[255]; -uint8_t volume; - -struct folderSettings { - uint8_t folder; - uint8_t mode; - uint8_t special; - uint8_t special2; -}; - -// this object stores nfc tag data -struct nfcTagObject { - uint32_t cookie; - uint8_t version; - folderSettings nfcFolderSettings; - // uint8_t folder; - // uint8_t mode; - // uint8_t special; - // uint8_t special2; -}; - -// admin settings stored in eeprom -struct adminSettings { - uint32_t cookie; - byte version; - uint8_t maxVolume; - uint8_t minVolume; - uint8_t initVolume; - uint8_t eq; - bool locked; - long standbyTimer; - bool invertVolumeButtons; - folderSettings shortCuts[4]; - uint8_t adminMenuLocked; - uint8_t adminMenuPin[4]; -}; - -adminSettings mySettings; -nfcTagObject myCard; -folderSettings *myFolder; -unsigned long sleepAtMillis = 0; -static uint16_t _lastTrackFinished; - -static void nextTrack(uint16_t track); -uint8_t voiceMenu(int numberOfOptions, int startMessage, int messageOffset, - bool preview = false, int previewFromFolder = 0, int defaultValue = 0, bool exitWithLongPress = false); -bool isPlaying(); -bool checkTwo ( uint8_t a[], uint8_t b[] ); -void writeCard(nfcTagObject nfcTag); -void dump_byte_array(byte * buffer, byte bufferSize); -void adminMenu(bool fromCard = false); -bool knownCard = false; - -// implement a notification class, -// its member methods will get called -// -class Mp3Notify { - public: - static void OnError(uint16_t errorCode) { - // see DfMp3_Error for code meaning - Serial.println(); - Serial.print("Com Error "); - Serial.println(errorCode); - } - static void PrintlnSourceAction(DfMp3_PlaySources source, const char* action) { - if (source & DfMp3_PlaySources_Sd) Serial.print("SD Karte "); - if (source & DfMp3_PlaySources_Usb) Serial.print("USB "); - if (source & DfMp3_PlaySources_Flash) Serial.print("Flash "); - Serial.println(action); - } - static void OnPlayFinished(DfMp3_PlaySources source, uint16_t track) { - // Serial.print("Track beendet"); - // Serial.println(track); - // delay(100); - nextTrack(track); - } - static void OnPlaySourceOnline(DfMp3_PlaySources source) { - PrintlnSourceAction(source, "online"); - } - static void OnPlaySourceInserted(DfMp3_PlaySources source) { - PrintlnSourceAction(source, "bereit"); - } - static void OnPlaySourceRemoved(DfMp3_PlaySources source) { - PrintlnSourceAction(source, "entfernt"); - } -}; - -static DFMiniMp3 mp3(mySoftwareSerial); - -void shuffleQueue() { - // Queue für die Zufallswiedergabe erstellen - for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1; x++) - queue[x] = x + firstTrack; - // Rest mit 0 auffüllen - for (uint8_t x = numTracksInFolder - firstTrack + 1; x < 255; x++) - queue[x] = 0; - // Queue mischen - for (uint8_t i = 0; i < numTracksInFolder - firstTrack + 1; i++) - { - uint8_t j = random (0, numTracksInFolder - firstTrack + 1); - uint8_t t = queue[i]; - queue[i] = queue[j]; - queue[j] = t; - } - /* Serial.println(F("Queue :")); - for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1 ; x++) - Serial.println(queue[x]); - */ -} - -void writeSettingsToFlash() { - Serial.println(F("=== writeSettingsToFlash()")); - int address = sizeof(myFolder->folder) * 100; - EEPROM.put(address, mySettings); -} - -void resetSettings() { - Serial.println(F("=== resetSettings()")); - mySettings.cookie = cardCookie; - mySettings.version = 2; - mySettings.maxVolume = 25; - mySettings.minVolume = 5; - mySettings.initVolume = 15; - mySettings.eq = 1; - mySettings.locked = false; - mySettings.standbyTimer = 0; - mySettings.invertVolumeButtons = true; - mySettings.shortCuts[0].folder = 0; - mySettings.shortCuts[1].folder = 0; - mySettings.shortCuts[2].folder = 0; - mySettings.shortCuts[3].folder = 0; - mySettings.adminMenuLocked = 0; - mySettings.adminMenuPin[0] = 1; - mySettings.adminMenuPin[1] = 1; - mySettings.adminMenuPin[2] = 1; - mySettings.adminMenuPin[3] = 1; - - writeSettingsToFlash(); -} - -void migrateSettings(int oldVersion) { - if (oldVersion == 1) { - Serial.println(F("=== resetSettings()")); - Serial.println(F("1 -> 2")); - mySettings.version = 2; - mySettings.adminMenuLocked = 0; - mySettings.adminMenuPin[0] = 1; - mySettings.adminMenuPin[1] = 1; - mySettings.adminMenuPin[2] = 1; - mySettings.adminMenuPin[3] = 1; - writeSettingsToFlash(); - } -} - -void loadSettingsFromFlash() { - Serial.println(F("=== loadSettingsFromFlash()")); - int address = sizeof(myFolder->folder) * 100; - EEPROM.get(address, mySettings); - if (mySettings.cookie != cardCookie) - resetSettings(); - migrateSettings(mySettings.version); - - Serial.print(F("Version: ")); - Serial.println(mySettings.version); - - Serial.print(F("Maximal Volume: ")); - Serial.println(mySettings.maxVolume); - - Serial.print(F("Minimal Volume: ")); - Serial.println(mySettings.minVolume); - - Serial.print(F("Initial Volume: ")); - Serial.println(mySettings.initVolume); - - Serial.print(F("EQ: ")); - Serial.println(mySettings.eq); - - Serial.print(F("Locked: ")); - Serial.println(mySettings.locked); - - Serial.print(F("Sleep Timer: ")); - Serial.println(mySettings.standbyTimer); - - Serial.print(F("Inverted Volume Buttons: ")); - Serial.println(mySettings.invertVolumeButtons); - - Serial.print(F("Admin Menu locked: ")); - Serial.println(mySettings.adminMenuLocked); - - Serial.print(F("Admin Menu Pin: ")); - Serial.print(mySettings.adminMenuPin[0]); - Serial.print(mySettings.adminMenuPin[1]); - Serial.print(mySettings.adminMenuPin[2]); - Serial.println(mySettings.adminMenuPin[3]); -} - -class Modifier { - public: - virtual void loop() {} - virtual bool handlePause() { - return false; - } - virtual bool handleNext() { - return false; - } - virtual bool handlePrevious() { - return false; - } - virtual bool handleNextButton() { - return false; - } - virtual bool handlePreviousButton() { - return false; - } - virtual bool handleVolumeUp() { - return false; - } - virtual bool handleVolumeDown() { - return false; - } - virtual bool handleRFID(nfcTagObject *newCard) { - return false; - } - virtual uint8_t getActive() { - return 0; - } - Modifier() { - - } -}; - -Modifier *activeModifier = NULL; - -class SleepTimer: public Modifier { - private: - unsigned long sleepAtMillis = 0; - - public: - void loop() { - if (this->sleepAtMillis != 0 && millis() > this->sleepAtMillis) { - Serial.println(F("=== SleepTimer::loop() -> SLEEP!")); - mp3.pause(); - setstandbyTimer(); - activeModifier = NULL; - delete this; - } - } - - SleepTimer(uint8_t minutes) { - Serial.println(F("=== SleepTimer()")); - Serial.println(minutes); - this->sleepAtMillis = millis() + minutes * 60000; - // if (isPlaying()) - // mp3.playAdvertisement(302); - // delay(500); - } - uint8_t getActive() { - Serial.println(F("== SleepTimer::getActive()")); - return 1; - } -}; - -class FreezeDance: public Modifier { - private: - unsigned long nextStopAtMillis = 0; - const uint8_t minSecondsBetweenStops = 5; - const uint8_t maxSecondsBetweenStops = 30; - - void setNextStopAtMillis() { - uint16_t seconds = random(this->minSecondsBetweenStops, this->maxSecondsBetweenStops + 1); - Serial.println(F("=== FreezeDance::setNextStopAtMillis()")); - Serial.println(seconds); - this->nextStopAtMillis = millis() + seconds * 1000; - } - - public: - void loop() { - if (this->nextStopAtMillis != 0 && millis() > this->nextStopAtMillis) { - Serial.println(F("== FreezeDance::loop() -> FREEZE!")); - if (isPlaying()) { - mp3.playAdvertisement(301); - delay(500); - } - setNextStopAtMillis(); - } - } - FreezeDance(void) { - Serial.println(F("=== FreezeDance()")); - if (isPlaying()) { - delay(1000); - mp3.playAdvertisement(300); - delay(500); - } - setNextStopAtMillis(); - } - uint8_t getActive() { - Serial.println(F("== FreezeDance::getActive()")); - return 2; - } -}; - -class Locked: public Modifier { - public: - virtual bool handlePause() { - Serial.println(F("== Locked::handlePause() -> LOCKED!")); - return true; - } - virtual bool handleNextButton() { - Serial.println(F("== Locked::handleNextButton() -> LOCKED!")); - return true; - } - virtual bool handlePreviousButton() { - Serial.println(F("== Locked::handlePreviousButton() -> LOCKED!")); - return true; - } - virtual bool handleVolumeUp() { - Serial.println(F("== Locked::handleVolumeUp() -> LOCKED!")); - return true; - } - virtual bool handleVolumeDown() { - Serial.println(F("== Locked::handleVolumeDown() -> LOCKED!")); - return true; - } - virtual bool handleRFID(nfcTagObject *newCard) { - Serial.println(F("== Locked::handleRFID() -> LOCKED!")); - return true; - } - Locked(void) { - Serial.println(F("=== Locked()")); - // if (isPlaying()) - // mp3.playAdvertisement(303); - } - uint8_t getActive() { - return 3; - } -}; - -class ToddlerMode: public Modifier { - public: - virtual bool handlePause() { - Serial.println(F("== ToddlerMode::handlePause() -> LOCKED!")); - return true; - } - virtual bool handleNextButton() { - Serial.println(F("== ToddlerMode::handleNextButton() -> LOCKED!")); - return true; - } - virtual bool handlePreviousButton() { - Serial.println(F("== ToddlerMode::handlePreviousButton() -> LOCKED!")); - return true; - } - virtual bool handleVolumeUp() { - Serial.println(F("== ToddlerMode::handleVolumeUp() -> LOCKED!")); - return true; - } - virtual bool handleVolumeDown() { - Serial.println(F("== ToddlerMode::handleVolumeDown() -> LOCKED!")); - return true; - } - ToddlerMode(void) { - Serial.println(F("=== ToddlerMode()")); - // if (isPlaying()) - // mp3.playAdvertisement(304); - } - uint8_t getActive() { - Serial.println(F("== ToddlerMode::getActive()")); - return 4; - } -}; - -class KindergardenMode: public Modifier { - private: - nfcTagObject nextCard; - bool cardQueued = false; - - public: - virtual bool handleNext() { - Serial.println(F("== KindergardenMode::handleNext() -> NEXT")); - //if (this->nextCard.cookie == cardCookie && this->nextCard.nfcFolderSettings.folder != 0 && this->nextCard.nfcFolderSettings.mode != 0) { - //myFolder = &this->nextCard.nfcFolderSettings; - if (this->cardQueued == true) { - this->cardQueued = false; - - myCard = nextCard; - myFolder = &myCard.nfcFolderSettings; - Serial.println(myFolder->folder); - Serial.println(myFolder->mode); - playFolder(); - return true; - } - return false; - } - // virtual bool handlePause() { - // Serial.println(F("== KindergardenMode::handlePause() -> LOCKED!")); - // return true; - // } - virtual bool handleNextButton() { - Serial.println(F("== KindergardenMode::handleNextButton() -> LOCKED!")); - return true; - } - virtual bool handlePreviousButton() { - Serial.println(F("== KindergardenMode::handlePreviousButton() -> LOCKED!")); - return true; - } - virtual bool handleRFID(nfcTagObject * newCard) { // lot of work to do! - Serial.println(F("== KindergardenMode::handleRFID() -> queued!")); - this->nextCard = *newCard; - this->cardQueued = true; - if (!isPlaying()) { - handleNext(); - } - return true; - } - KindergardenMode() { - Serial.println(F("=== KindergardenMode()")); - // if (isPlaying()) - // mp3.playAdvertisement(305); - // delay(500); - } - uint8_t getActive() { - Serial.println(F("== KindergardenMode::getActive()")); - return 5; - } -}; - -class RepeatSingleModifier: public Modifier { - public: - virtual bool handleNext() { - Serial.println(F("== RepeatSingleModifier::handleNext() -> REPEAT CURRENT TRACK")); - delay(50); - if (isPlaying()) return true; - if (myFolder->mode == 3 || myFolder->mode == 9){ - mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); - } - else{ - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - _lastTrackFinished = 0; - return true; - } - RepeatSingleModifier() { - Serial.println(F("=== RepeatSingleModifier()")); - } - uint8_t getActive() { - Serial.println(F("== RepeatSingleModifier::getActive()")); - return 6; - } -}; - -// An modifier can also do somethings in addition to the modified action -// by returning false (not handled) at the end -// This simple FeedbackModifier will tell the volume before changing it and -// give some feedback once a RFID card is detected. -class FeedbackModifier: public Modifier { - public: - virtual bool handleVolumeDown() { - if (volume > mySettings.minVolume) { - mp3.playAdvertisement(volume - 1); - } - else { - mp3.playAdvertisement(volume); - } - delay(500); - Serial.println(F("== FeedbackModifier::handleVolumeDown()!")); - return false; - } - virtual bool handleVolumeUp() { - if (volume < mySettings.maxVolume) { - mp3.playAdvertisement(volume + 1); - } - else { - mp3.playAdvertisement(volume); - } - delay(500); - Serial.println(F("== FeedbackModifier::handleVolumeUp()!")); - return false; - } - virtual bool handleRFID(nfcTagObject *newCard) { - Serial.println(F("== FeedbackModifier::handleRFID()")); - return false; - } -}; - -// Leider kann das Modul selbst keine Queue abspielen, daher müssen wir selbst die Queue verwalten -static void nextTrack(uint16_t track) { - Serial.println(track); - if (activeModifier != NULL) - if (activeModifier->handleNext() == true) - return; - - if (track == _lastTrackFinished) { - return; - } - _lastTrackFinished = track; - - if (knownCard == false) - // Wenn eine neue Karte angelernt wird soll das Ende eines Tracks nicht - // verarbeitet werden - return; - - Serial.println(F("=== nextTrack()")); - - if (myFolder->mode == 1 || myFolder->mode == 7) { - Serial.println(F("Hörspielmodus ist aktiv -> keinen neuen Track spielen")); - setstandbyTimer(); - // mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! - } - if (myFolder->mode == 2 || myFolder->mode == 8) { - if (currentTrack != numTracksInFolder) { - currentTrack = currentTrack + 1; - mp3.playFolderTrack(myFolder->folder, currentTrack); - Serial.print(F("Albummodus ist aktiv -> nächster Track: ")); - Serial.print(currentTrack); - } else - // mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! - setstandbyTimer(); - { } - } - if (myFolder->mode == 3 || myFolder->mode == 9) { - if (currentTrack != numTracksInFolder - firstTrack + 1) { - Serial.print(F("Party -> weiter in der Queue ")); - currentTrack++; - } else { - Serial.println(F("Ende der Queue -> beginne von vorne")); - currentTrack = 1; - //// Wenn am Ende der Queue neu gemischt werden soll bitte die Zeilen wieder aktivieren - // Serial.println(F("Ende der Queue -> mische neu")); - // shuffleQueue(); - } - Serial.println(queue[currentTrack - 1]); - mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); - } - - if (myFolder->mode == 4) { - Serial.println(F("Einzel Modus aktiv -> Strom sparen")); - // mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! - setstandbyTimer(); - } - if (myFolder->mode == 5) { - if (currentTrack != numTracksInFolder) { - currentTrack = currentTrack + 1; - Serial.print(F("Hörbuch Modus ist aktiv -> nächster Track und " - "Fortschritt speichern")); - Serial.println(currentTrack); - mp3.playFolderTrack(myFolder->folder, currentTrack); - // Fortschritt im EEPROM abspeichern - EEPROM.update(myFolder->folder, currentTrack); - } else { - // mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! - // Fortschritt zurück setzen - EEPROM.update(myFolder->folder, 1); - setstandbyTimer(); - } - } - delay(500); -} - -static void previousTrack() { - Serial.println(F("=== previousTrack()")); - /* if (myCard.mode == 1 || myCard.mode == 7) { - Serial.println(F("Hörspielmodus ist aktiv -> Track von vorne spielen")); - mp3.playFolderTrack(myCard.folder, currentTrack); - }*/ - if (myFolder->mode == 2 || myFolder->mode == 8) { - Serial.println(F("Albummodus ist aktiv -> vorheriger Track")); - if (currentTrack != firstTrack) { - currentTrack = currentTrack - 1; - } - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - if (myFolder->mode == 3 || myFolder->mode == 9) { - if (currentTrack != 1) { - Serial.print(F("Party Modus ist aktiv -> zurück in der Qeueue ")); - currentTrack--; - } - else - { - Serial.print(F("Anfang der Queue -> springe ans Ende ")); - currentTrack = numTracksInFolder; - } - Serial.println(queue[currentTrack - 1]); - mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); - } - if (myFolder->mode == 4) { - Serial.println(F("Einzel Modus aktiv -> Track von vorne spielen")); - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - if (myFolder->mode == 5) { - Serial.println(F("Hörbuch Modus ist aktiv -> vorheriger Track und " - "Fortschritt speichern")); - if (currentTrack != 1) { - currentTrack = currentTrack - 1; - } - mp3.playFolderTrack(myFolder->folder, currentTrack); - // Fortschritt im EEPROM abspeichern - EEPROM.update(myFolder->folder, currentTrack); - } - delay(1000); -} - -// MFRC522 -#define RST_PIN 9 // Configurable, see typical pin layout above -#define SS_PIN 10 // Configurable, see typical pin layout above -MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 -MFRC522::MIFARE_Key key; -bool successRead; -byte sector = 1; -byte blockAddr = 4; -byte trailerBlock = 7; -MFRC522::StatusCode status; - -#define buttonPause A0 -#define buttonUp A1 -#define buttonDown A2 -#define busyPin 4 -#define shutdownPin 7 -#define openAnalogPin A7 - -#ifdef FIVEBUTTONS -#define buttonFourPin A3 -#define buttonFivePin A4 -#endif - -#define LONG_PRESS 1000 - -Button pauseButton(buttonPause); -Button upButton(buttonUp); -Button downButton(buttonDown); -#ifdef FIVEBUTTONS -Button buttonFour(buttonFourPin); -Button buttonFive(buttonFivePin); -#endif -bool ignorePauseButton = false; -bool ignoreUpButton = false; -bool ignoreDownButton = false; -#ifdef FIVEBUTTONS -bool ignoreButtonFour = false; -bool ignoreButtonFive = false; -#endif +namespace { -/// Funktionen für den Standby Timer (z.B. über Pololu-Switch oder Mosfet) +const uint8_t openAnalogPin = A7; -void setstandbyTimer() { - Serial.println(F("=== setstandbyTimer()")); - if (mySettings.standbyTimer != 0) - sleepAtMillis = millis() + (mySettings.standbyTimer * 60 * 1000); - else - sleepAtMillis = 0; - Serial.println(sleepAtMillis); } -void disablestandbyTimer() { - Serial.println(F("=== disablestandby()")); - sleepAtMillis = 0; -} - -void checkStandbyAtMillis() { - if (sleepAtMillis != 0 && millis() > sleepAtMillis) { - Serial.println(F("=== power off!")); - // enter sleep state - digitalWrite(shutdownPin, HIGH); - delay(500); - - // http://discourse.voss.earth/t/intenso-s10000-powerbank-automatische-abschaltung-software-only/805 - // powerdown to 27mA (powerbank switches off after 30-60s) - mfrc522.PCD_AntennaOff(); - mfrc522.PCD_SoftPowerDown(); - mp3.sleep(); - - set_sleep_mode(SLEEP_MODE_PWR_DOWN); - cli(); // Disable interrupts - sleep_mode(); - } -} - -bool isPlaying() { - return !digitalRead(busyPin); -} - -void waitForTrackToFinish() { - long currentTime = millis(); -#define TIMEOUT 1000 - do { - mp3.loop(); - } while (!isPlaying() && millis() < currentTime + TIMEOUT); - delay(1000); - do { - mp3.loop(); - } while (isPlaying()); -} void setup() { Serial.begin(115200); // Es gibt ein paar Debug Ausgaben über die serielle Schnittstelle // Wert für randomSeed() erzeugen durch das mehrfache Sammeln von rauschenden LSBs eines offenen Analogeingangs - uint32_t ADC_LSB; - uint32_t ADCSeed; + uint32_t ADC_LSB = 0; + uint32_t ADCSeed = 0; for (uint8_t i = 0; i < 128; i++) { ADC_LSB = analogRead(openAnalogPin) & 0x1; ADCSeed ^= ADC_LSB << (i % 32); @@ -740,1068 +45,11 @@ void setup() { Serial.println(F("created by Thorsten Voß and licensed under GNU/GPL.")); Serial.println(F("Information and contribution at https://tonuino.de.\n")); - // Busy Pin - pinMode(busyPin, INPUT); - - // load Settings from EEPROM - loadSettingsFromFlash(); - - // activate standby timer - setstandbyTimer(); - - // DFPlayer Mini initialisieren - mp3.begin(); - // Zwei Sekunden warten bis der DFPlayer Mini initialisiert ist - delay(2000); - volume = mySettings.initVolume; - mp3.setVolume(volume); - mp3.setEq(mySettings.eq - 1); - // Fix für das Problem mit dem Timeout (ist jetzt in Upstream daher nicht mehr nötig!) - //mySoftwareSerial.setTimeout(10000); - - // NFC Leser initialisieren - SPI.begin(); // Init SPI bus - mfrc522.PCD_Init(); // Init MFRC522 - mfrc522 - .PCD_DumpVersionToSerial(); // Show details of PCD - MFRC522 Card Reader - for (byte i = 0; i < 6; i++) { - key.keyByte[i] = 0xFF; - } - - pinMode(buttonPause, INPUT_PULLUP); - pinMode(buttonUp, INPUT_PULLUP); - pinMode(buttonDown, INPUT_PULLUP); -#ifdef FIVEBUTTONS - pinMode(buttonFourPin, INPUT_PULLUP); - pinMode(buttonFivePin, INPUT_PULLUP); -#endif - pinMode(shutdownPin, OUTPUT); - digitalWrite(shutdownPin, LOW); - - - // RESET --- ALLE DREI KNÖPFE BEIM STARTEN GEDRÜCKT HALTEN -> alle EINSTELLUNGEN werden gelöscht - if (digitalRead(buttonPause) == LOW && digitalRead(buttonUp) == LOW && - digitalRead(buttonDown) == LOW) { - Serial.println(F("Reset -> EEPROM wird gelöscht")); - for (int i = 0; i < EEPROM.length(); i++) { - EEPROM.update(i, 0); - } - loadSettingsFromFlash(); - } - - - // Start Shortcut "at Startup" - e.g. Welcome Sound - playShortCut(3); -} - -void readButtons() { - pauseButton.read(); - upButton.read(); - downButton.read(); -#ifdef FIVEBUTTONS - buttonFour.read(); - buttonFive.read(); -#endif -} - -void volumeUpButton() { - if (activeModifier != NULL) - if (activeModifier->handleVolumeUp() == true) - return; - - Serial.println(F("=== volumeUp()")); - if (volume < mySettings.maxVolume) { - mp3.increaseVolume(); - volume++; - } - Serial.println(volume); -} - -void volumeDownButton() { - if (activeModifier != NULL) - if (activeModifier->handleVolumeDown() == true) - return; - - Serial.println(F("=== volumeDown()")); - if (volume > mySettings.minVolume) { - mp3.decreaseVolume(); - volume--; - } - Serial.println(volume); -} - -void nextButton() { - if (activeModifier != NULL) - if (activeModifier->handleNextButton() == true) - return; - - nextTrack(random(65536)); - delay(1000); -} - -void previousButton() { - if (activeModifier != NULL) - if (activeModifier->handlePreviousButton() == true) - return; - - previousTrack(); - delay(1000); -} - -void playFolder() { - Serial.println(F("== playFolder()")) ; - disablestandbyTimer(); - knownCard = true; - _lastTrackFinished = 0; - numTracksInFolder = mp3.getFolderTrackCount(myFolder->folder); - firstTrack = 1; - Serial.print(numTracksInFolder); - Serial.print(F(" Dateien in Ordner ")); - Serial.println(myFolder->folder); - - // Hörspielmodus: eine zufällige Datei aus dem Ordner - if (myFolder->mode == 1) { - Serial.println(F("Hörspielmodus -> zufälligen Track wiedergeben")); - currentTrack = random(1, numTracksInFolder + 1); - Serial.println(currentTrack); - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - // Album Modus: kompletten Ordner spielen - if (myFolder->mode == 2) { - Serial.println(F("Album Modus -> kompletten Ordner wiedergeben")); - currentTrack = 1; - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - // Party Modus: Ordner in zufälliger Reihenfolge - if (myFolder->mode == 3) { - Serial.println( - F("Party Modus -> Ordner in zufälliger Reihenfolge wiedergeben")); - shuffleQueue(); - currentTrack = 1; - mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); - } - // Einzel Modus: eine Datei aus dem Ordner abspielen - if (myFolder->mode == 4) { - Serial.println( - F("Einzel Modus -> eine Datei aus dem Odrdner abspielen")); - currentTrack = myFolder->special; - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - // Hörbuch Modus: kompletten Ordner spielen und Fortschritt merken - if (myFolder->mode == 5) { - Serial.println(F("Hörbuch Modus -> kompletten Ordner spielen und " - "Fortschritt merken")); - currentTrack = EEPROM.read(myFolder->folder); - if (currentTrack == 0 || currentTrack > numTracksInFolder) { - currentTrack = 1; - } - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - // Spezialmodus Von-Bin: Hörspiel: eine zufällige Datei aus dem Ordner - if (myFolder->mode == 7) { - Serial.println(F("Spezialmodus Von-Bin: Hörspiel -> zufälligen Track wiedergeben")); - Serial.print(myFolder->special); - Serial.print(F(" bis ")); - Serial.println(myFolder->special2); - numTracksInFolder = myFolder->special2; - currentTrack = random(myFolder->special, numTracksInFolder + 1); - Serial.println(currentTrack); - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - - // Spezialmodus Von-Bis: Album: alle Dateien zwischen Start und Ende spielen - if (myFolder->mode == 8) { - Serial.println(F("Spezialmodus Von-Bis: Album: alle Dateien zwischen Start- und Enddatei spielen")); - Serial.print(myFolder->special); - Serial.print(F(" bis ")); - Serial.println(myFolder->special2); - numTracksInFolder = myFolder->special2; - currentTrack = myFolder->special; - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - - // Spezialmodus Von-Bis: Party Ordner in zufälliger Reihenfolge - if (myFolder->mode == 9) { - Serial.println( - F("Spezialmodus Von-Bis: Party -> Ordner in zufälliger Reihenfolge wiedergeben")); - firstTrack = myFolder->special; - numTracksInFolder = myFolder->special2; - shuffleQueue(); - currentTrack = 1; - mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); - } -} - -void playShortCut(uint8_t shortCut) { - Serial.println(F("=== playShortCut()")); - Serial.println(shortCut); - if (mySettings.shortCuts[shortCut].folder != 0) { - myFolder = &mySettings.shortCuts[shortCut]; - playFolder(); - disablestandbyTimer(); - delay(1000); - } - else - Serial.println(F("Shortcut not configured!")); + tonuino.setup(); } void loop() { - do { - checkStandbyAtMillis(); - mp3.loop(); - - // Modifier : WIP! - if (activeModifier != NULL) { - activeModifier->loop(); - } - - // Buttons werden nun über JS_Button gehandelt, dadurch kann jede Taste - // doppelt belegt werden - readButtons(); - - // admin menu - if ((pauseButton.pressedFor(LONG_PRESS) || upButton.pressedFor(LONG_PRESS) || downButton.pressedFor(LONG_PRESS)) && pauseButton.isPressed() && upButton.isPressed() && downButton.isPressed()) { - mp3.pause(); - do { - readButtons(); - } while (pauseButton.isPressed() || upButton.isPressed() || downButton.isPressed()); - readButtons(); - adminMenu(); - break; - } - - if (pauseButton.wasReleased()) { - if (activeModifier != NULL) - if (activeModifier->handlePause() == true) - return; - if (ignorePauseButton == false) - if (isPlaying()) { - mp3.pause(); - setstandbyTimer(); - } - else if (knownCard) { - mp3.start(); - disablestandbyTimer(); - } - ignorePauseButton = false; - } else if (pauseButton.pressedFor(LONG_PRESS) && - ignorePauseButton == false) { - if (activeModifier != NULL) - if (activeModifier->handlePause() == true) - return; - if (isPlaying()) { - uint8_t advertTrack; - if (myFolder->mode == 3 || myFolder->mode == 9) { - advertTrack = (queue[currentTrack - 1]); - } - else { - advertTrack = currentTrack; - } - // Spezialmodus Von-Bis für Album und Party gibt die Dateinummer relativ zur Startposition wieder - if (myFolder->mode == 8 || myFolder->mode == 9) { - advertTrack = advertTrack - myFolder->special + 1; - } - mp3.playAdvertisement(advertTrack); - } - else { - playShortCut(0); - } - ignorePauseButton = true; - } - - if (upButton.pressedFor(LONG_PRESS)) { -#ifndef FIVEBUTTONS - if (isPlaying()) { - if (!mySettings.invertVolumeButtons) { - volumeUpButton(); - } - else { - nextButton(); - } - } - else { - playShortCut(1); - } - ignoreUpButton = true; -#endif - } else if (upButton.wasReleased()) { - if (!ignoreUpButton) - if (!mySettings.invertVolumeButtons) { - nextButton(); - } - else { - volumeUpButton(); - } - ignoreUpButton = false; - } - - if (downButton.pressedFor(LONG_PRESS)) { -#ifndef FIVEBUTTONS - if (isPlaying()) { - if (!mySettings.invertVolumeButtons) { - volumeDownButton(); - } - else { - previousButton(); - } - } - else { - playShortCut(2); - } - ignoreDownButton = true; -#endif - } else if (downButton.wasReleased()) { - if (!ignoreDownButton) { - if (!mySettings.invertVolumeButtons) { - previousButton(); - } - else { - volumeDownButton(); - } - } - ignoreDownButton = false; - } -#ifdef FIVEBUTTONS - if (buttonFour.wasReleased()) { - if (isPlaying()) { - if (!mySettings.invertVolumeButtons) { - volumeUpButton(); - } - else { - nextButton(); - } - } - else { - playShortCut(1); - } - } - if (buttonFive.wasReleased()) { - if (isPlaying()) { - if (!mySettings.invertVolumeButtons) { - volumeDownButton(); - } - else { - previousButton(); - } - } - else { - playShortCut(2); - } - } -#endif - // Ende der Buttons - } while (!mfrc522.PICC_IsNewCardPresent()); - - // RFID Karte wurde aufgelegt - - if (!mfrc522.PICC_ReadCardSerial()) - return; - - if (readCard(&myCard) == true) { - if (myCard.cookie == cardCookie && myCard.nfcFolderSettings.folder != 0 && myCard.nfcFolderSettings.mode != 0) { - playFolder(); - } - - // Neue Karte konfigurieren - else if (myCard.cookie != cardCookie) { - knownCard = false; - mp3.playMp3FolderTrack(300); - waitForTrackToFinish(); - setupCard(); - } - } - mfrc522.PICC_HaltA(); - mfrc522.PCD_StopCrypto1(); -} - -void adminMenu(bool fromCard = false) { - disablestandbyTimer(); - mp3.pause(); - Serial.println(F("=== adminMenu()")); - knownCard = false; - if (fromCard == false) { - // Admin menu has been locked - it still can be trigged via admin card - if (mySettings.adminMenuLocked == 1) { - return; - } - // Pin check - else if (mySettings.adminMenuLocked == 2) { - uint8_t pin[4]; - mp3.playMp3FolderTrack(991); - if (askCode(pin) == true) { - if (checkTwo(pin, mySettings.adminMenuPin) == false) { - return; - } - } else { - return; - } - } - // Match check - else if (mySettings.adminMenuLocked == 3) { - uint8_t a = random(10, 20); - uint8_t b = random(1, 10); - uint8_t c; - mp3.playMp3FolderTrack(992); - waitForTrackToFinish(); - mp3.playMp3FolderTrack(a); - - if (random(1, 3) == 2) { - // a + b - c = a + b; - waitForTrackToFinish(); - mp3.playMp3FolderTrack(993); - } else { - // a - b - b = random(1, a); - c = a - b; - waitForTrackToFinish(); - mp3.playMp3FolderTrack(994); - } - waitForTrackToFinish(); - mp3.playMp3FolderTrack(b); - Serial.println(c); - uint8_t temp = voiceMenu(255, 0, 0, false); - if (temp != c) { - return; - } - } - } - int subMenu = voiceMenu(12, 900, 900, false, false, 0, true); - if (subMenu == 0) - return; - if (subMenu == 1) { - resetCard(); - mfrc522.PICC_HaltA(); - mfrc522.PCD_StopCrypto1(); - } - else if (subMenu == 2) { - // Maximum Volume - mySettings.maxVolume = voiceMenu(30 - mySettings.minVolume, 930, mySettings.minVolume, false, false, mySettings.maxVolume - mySettings.minVolume) + mySettings.minVolume; - } - else if (subMenu == 3) { - // Minimum Volume - mySettings.minVolume = voiceMenu(mySettings.maxVolume - 1, 931, 0, false, false, mySettings.minVolume); - } - else if (subMenu == 4) { - // Initial Volume - mySettings.initVolume = voiceMenu(mySettings.maxVolume - mySettings.minVolume + 1, 932, mySettings.minVolume - 1, false, false, mySettings.initVolume - mySettings.minVolume + 1) + mySettings.minVolume - 1; - } - else if (subMenu == 5) { - // EQ - mySettings.eq = voiceMenu(6, 920, 920, false, false, mySettings.eq); - mp3.setEq(mySettings.eq - 1); - } - else if (subMenu == 6) { - // create modifier card - nfcTagObject tempCard; - tempCard.cookie = cardCookie; - tempCard.version = 1; - tempCard.nfcFolderSettings.folder = 0; - tempCard.nfcFolderSettings.special = 0; - tempCard.nfcFolderSettings.special2 = 0; - tempCard.nfcFolderSettings.mode = voiceMenu(6, 970, 970, false, false, 0, true); - - if (tempCard.nfcFolderSettings.mode != 0) { - if (tempCard.nfcFolderSettings.mode == 1) { - switch (voiceMenu(4, 960, 960)) { - case 1: tempCard.nfcFolderSettings.special = 5; break; - case 2: tempCard.nfcFolderSettings.special = 15; break; - case 3: tempCard.nfcFolderSettings.special = 30; break; - case 4: tempCard.nfcFolderSettings.special = 60; break; - } - } - mp3.playMp3FolderTrack(800); - do { - readButtons(); - if (upButton.wasReleased() || downButton.wasReleased()) { - Serial.println(F("Abgebrochen!")); - mp3.playMp3FolderTrack(802); - return; - } - } while (!mfrc522.PICC_IsNewCardPresent()); - // RFID Karte wurde aufgelegt - if (mfrc522.PICC_ReadCardSerial()) { - Serial.println(F("schreibe Karte...")); - writeCard(tempCard); - delay(100); - mfrc522.PICC_HaltA(); - mfrc522.PCD_StopCrypto1(); - waitForTrackToFinish(); - } - } - } - else if (subMenu == 7) { - uint8_t shortcut = voiceMenu(4, 940, 940); - setupFolder(&mySettings.shortCuts[shortcut - 1]); - mp3.playMp3FolderTrack(400); - } - else if (subMenu == 8) { - switch (voiceMenu(5, 960, 960)) { - case 1: mySettings.standbyTimer = 5; break; - case 2: mySettings.standbyTimer = 15; break; - case 3: mySettings.standbyTimer = 30; break; - case 4: mySettings.standbyTimer = 60; break; - case 5: mySettings.standbyTimer = 0; break; - } - } - else if (subMenu == 9) { - // Create Cards for Folder - // Ordner abfragen - nfcTagObject tempCard; - tempCard.cookie = cardCookie; - tempCard.version = 1; - tempCard.nfcFolderSettings.mode = 4; - tempCard.nfcFolderSettings.folder = voiceMenu(99, 301, 0, true); - uint8_t special = voiceMenu(mp3.getFolderTrackCount(tempCard.nfcFolderSettings.folder), 321, 0, - true, tempCard.nfcFolderSettings.folder); - uint8_t special2 = voiceMenu(mp3.getFolderTrackCount(tempCard.nfcFolderSettings.folder), 322, 0, - true, tempCard.nfcFolderSettings.folder, special); - - mp3.playMp3FolderTrack(936); - waitForTrackToFinish(); - for (uint8_t x = special; x <= special2; x++) { - mp3.playMp3FolderTrack(x); - tempCard.nfcFolderSettings.special = x; - Serial.print(x); - Serial.println(F(" Karte auflegen")); - do { - readButtons(); - if (upButton.wasReleased() || downButton.wasReleased()) { - Serial.println(F("Abgebrochen!")); - mp3.playMp3FolderTrack(802); - return; - } - } while (!mfrc522.PICC_IsNewCardPresent()); - - // RFID Karte wurde aufgelegt - if (mfrc522.PICC_ReadCardSerial()) { - Serial.println(F("schreibe Karte...")); - writeCard(tempCard); - delay(100); - mfrc522.PICC_HaltA(); - mfrc522.PCD_StopCrypto1(); - waitForTrackToFinish(); - } - } - } - else if (subMenu == 10) { - // Invert Functions for Up/Down Buttons - int temp = voiceMenu(2, 933, 933, false); - if (temp == 2) { - mySettings.invertVolumeButtons = true; - } - else { - mySettings.invertVolumeButtons = false; - } - } - else if (subMenu == 11) { - Serial.println(F("Reset -> EEPROM wird gelöscht")); - for (int i = 0; i < EEPROM.length(); i++) { - EEPROM.update(i, 0); - } - resetSettings(); - mp3.playMp3FolderTrack(999); - } - // lock admin menu - else if (subMenu == 12) { - int temp = voiceMenu(4, 980, 980, false); - if (temp == 1) { - mySettings.adminMenuLocked = 0; - } - else if (temp == 2) { - mySettings.adminMenuLocked = 1; - } - else if (temp == 3) { - int8_t pin[4]; - mp3.playMp3FolderTrack(991); - if (askCode(pin)) { - memcpy(mySettings.adminMenuPin, pin, 4); - mySettings.adminMenuLocked = 2; - } - } - else if (temp == 4) { - mySettings.adminMenuLocked = 3; - } - - } - writeSettingsToFlash(); - setstandbyTimer(); -} - -bool askCode(uint8_t *code) { - uint8_t x = 0; - while (x < 4) { - readButtons(); - if (pauseButton.pressedFor(LONG_PRESS)) - break; - if (pauseButton.wasReleased()) - code[x++] = 1; - if (upButton.wasReleased()) - code[x++] = 2; - if (downButton.wasReleased()) - code[x++] = 3; - } - return true; -} - -uint8_t voiceMenu(int numberOfOptions, int startMessage, int messageOffset, - bool preview = false, int previewFromFolder = 0, int defaultValue = 0, bool exitWithLongPress = false) { - uint8_t returnValue = defaultValue; - if (startMessage != 0) - mp3.playMp3FolderTrack(startMessage); - Serial.print(F("=== voiceMenu() (")); - Serial.print(numberOfOptions); - Serial.println(F(" Options)")); - do { - if (Serial.available() > 0) { - int optionSerial = Serial.parseInt(); - if (optionSerial != 0 && optionSerial <= numberOfOptions) - return optionSerial; - } - readButtons(); - mp3.loop(); - if (pauseButton.pressedFor(LONG_PRESS)) { - mp3.playMp3FolderTrack(802); - ignorePauseButton = true; - checkStandbyAtMillis(); - return defaultValue; - } - if (pauseButton.wasReleased()) { - if (returnValue != 0) { - Serial.print(F("=== ")); - Serial.print(returnValue); - Serial.println(F(" ===")); - return returnValue; - } - delay(1000); - } - - if (upButton.pressedFor(LONG_PRESS)) { - returnValue = min(returnValue + 10, numberOfOptions); - Serial.println(returnValue); - //mp3.pause(); - mp3.playMp3FolderTrack(messageOffset + returnValue); - waitForTrackToFinish(); - /*if (preview) { - if (previewFromFolder == 0) - mp3.playFolderTrack(returnValue, 1); - else - mp3.playFolderTrack(previewFromFolder, returnValue); - }*/ - ignoreUpButton = true; - } else if (upButton.wasReleased()) { - if (!ignoreUpButton) { - returnValue = min(returnValue + 1, numberOfOptions); - Serial.println(returnValue); - //mp3.pause(); - mp3.playMp3FolderTrack(messageOffset + returnValue); - if (preview) { - waitForTrackToFinish(); - if (previewFromFolder == 0) { - mp3.playFolderTrack(returnValue, 1); - } else { - mp3.playFolderTrack(previewFromFolder, returnValue); - } - delay(1000); - } - } else { - ignoreUpButton = false; - } - } - - if (downButton.pressedFor(LONG_PRESS)) { - returnValue = max(returnValue - 10, 1); - Serial.println(returnValue); - //mp3.pause(); - mp3.playMp3FolderTrack(messageOffset + returnValue); - waitForTrackToFinish(); - /*if (preview) { - if (previewFromFolder == 0) - mp3.playFolderTrack(returnValue, 1); - else - mp3.playFolderTrack(previewFromFolder, returnValue); - }*/ - ignoreDownButton = true; - } else if (downButton.wasReleased()) { - if (!ignoreDownButton) { - returnValue = max(returnValue - 1, 1); - Serial.println(returnValue); - //mp3.pause(); - mp3.playMp3FolderTrack(messageOffset + returnValue); - if (preview) { - waitForTrackToFinish(); - if (previewFromFolder == 0) { - mp3.playFolderTrack(returnValue, 1); - } - else { - mp3.playFolderTrack(previewFromFolder, returnValue); - } - delay(1000); - } - } else { - ignoreDownButton = false; - } - } - } while (true); -} - -void resetCard() { - mp3.playMp3FolderTrack(800); - do { - pauseButton.read(); - upButton.read(); - downButton.read(); - - if (upButton.wasReleased() || downButton.wasReleased()) { - Serial.print(F("Abgebrochen!")); - mp3.playMp3FolderTrack(802); - return; - } - } while (!mfrc522.PICC_IsNewCardPresent()); - - if (!mfrc522.PICC_ReadCardSerial()) - return; - - Serial.print(F("Karte wird neu konfiguriert!")); - setupCard(); -} - -bool setupFolder(folderSettings * theFolder) { - // Ordner abfragen - theFolder->folder = voiceMenu(99, 301, 0, true, 0, 0, true); - if (theFolder->folder == 0) return false; - - // Wiedergabemodus abfragen - theFolder->mode = voiceMenu(9, 310, 310, false, 0, 0, true); - if (theFolder->mode == 0) return false; - - // // Hörbuchmodus -> Fortschritt im EEPROM auf 1 setzen - // EEPROM.update(theFolder->folder, 1); - - // Einzelmodus -> Datei abfragen - if (theFolder->mode == 4) - theFolder->special = voiceMenu(mp3.getFolderTrackCount(theFolder->folder), 320, 0, - true, theFolder->folder); - // Admin Funktionen - if (theFolder->mode == 6) { - //theFolder->special = voiceMenu(3, 320, 320); - theFolder->folder = 0; - theFolder->mode = 255; - } - // Spezialmodus Von-Bis - if (theFolder->mode == 7 || theFolder->mode == 8 || theFolder->mode == 9) { - theFolder->special = voiceMenu(mp3.getFolderTrackCount(theFolder->folder), 321, 0, - true, theFolder->folder); - theFolder->special2 = voiceMenu(mp3.getFolderTrackCount(theFolder->folder), 322, 0, - true, theFolder->folder, theFolder->special); - } - return true; -} - -void setupCard() { - mp3.pause(); - Serial.println(F("=== setupCard()")); - nfcTagObject newCard; - if (setupFolder(&newCard.nfcFolderSettings) == true) - { - // Karte ist konfiguriert -> speichern - mp3.pause(); - do { - } while (isPlaying()); - writeCard(newCard); - } - delay(1000); -} -bool readCard(nfcTagObject * nfcTag) { - nfcTagObject tempCard; - // Show some details of the PICC (that is: the tag/card) - Serial.print(F("Card UID:")); - dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); - Serial.println(); - Serial.print(F("PICC type: ")); - MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); - Serial.println(mfrc522.PICC_GetTypeName(piccType)); - - byte buffer[18]; - byte size = sizeof(buffer); - - // Authenticate using key A - if ((piccType == MFRC522::PICC_TYPE_MIFARE_MINI ) || - (piccType == MFRC522::PICC_TYPE_MIFARE_1K ) || - (piccType == MFRC522::PICC_TYPE_MIFARE_4K ) ) - { - Serial.println(F("Authenticating Classic using key A...")); - status = mfrc522.PCD_Authenticate( - MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); - } - else if (piccType == MFRC522::PICC_TYPE_MIFARE_UL ) - { - byte pACK[] = {0, 0}; //16 bit PassWord ACK returned by the tempCard - - // Authenticate using key A - Serial.println(F("Authenticating MIFARE UL...")); - status = mfrc522.PCD_NTAG216_AUTH(key.keyByte, pACK); - } - - if (status != MFRC522::STATUS_OK) { - Serial.print(F("PCD_Authenticate() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); - return false; - } - - // Show the whole sector as it currently is - // Serial.println(F("Current data in sector:")); - // mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); - // Serial.println(); - - // Read data from the block - if ((piccType == MFRC522::PICC_TYPE_MIFARE_MINI ) || - (piccType == MFRC522::PICC_TYPE_MIFARE_1K ) || - (piccType == MFRC522::PICC_TYPE_MIFARE_4K ) ) - { - Serial.print(F("Reading data from block ")); - Serial.print(blockAddr); - Serial.println(F(" ...")); - status = (MFRC522::StatusCode)mfrc522.MIFARE_Read(blockAddr, buffer, &size); - if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Read() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); - return false; - } - } - else if (piccType == MFRC522::PICC_TYPE_MIFARE_UL ) - { - byte buffer2[18]; - byte size2 = sizeof(buffer2); - - status = (MFRC522::StatusCode)mfrc522.MIFARE_Read(8, buffer2, &size2); - if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Read_1() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); - return false; - } - memcpy(buffer, buffer2, 4); - - status = (MFRC522::StatusCode)mfrc522.MIFARE_Read(9, buffer2, &size2); - if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Read_2() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); - return false; - } - memcpy(buffer + 4, buffer2, 4); - - status = (MFRC522::StatusCode)mfrc522.MIFARE_Read(10, buffer2, &size2); - if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Read_3() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); - return false; - } - memcpy(buffer + 8, buffer2, 4); - - status = (MFRC522::StatusCode)mfrc522.MIFARE_Read(11, buffer2, &size2); - if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Read_4() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); - return false; - } - memcpy(buffer + 12, buffer2, 4); - } - - Serial.print(F("Data on Card ")); - Serial.println(F(":")); - dump_byte_array(buffer, 16); - Serial.println(); - Serial.println(); - - uint32_t tempCookie; - tempCookie = (uint32_t)buffer[0] << 24; - tempCookie += (uint32_t)buffer[1] << 16; - tempCookie += (uint32_t)buffer[2] << 8; - tempCookie += (uint32_t)buffer[3]; - - tempCard.cookie = tempCookie; - tempCard.version = buffer[4]; - tempCard.nfcFolderSettings.folder = buffer[5]; - tempCard.nfcFolderSettings.mode = buffer[6]; - tempCard.nfcFolderSettings.special = buffer[7]; - tempCard.nfcFolderSettings.special2 = buffer[8]; - - if (tempCard.cookie == cardCookie) { - - if (activeModifier != NULL && tempCard.nfcFolderSettings.folder != 0) { - if (activeModifier->handleRFID(&tempCard) == true) { - return false; - } - } - - if (tempCard.nfcFolderSettings.folder == 0) { - if (activeModifier != NULL) { - if (activeModifier->getActive() == tempCard.nfcFolderSettings.mode) { - activeModifier = NULL; - Serial.println(F("modifier removed")); - if (isPlaying()) { - mp3.playAdvertisement(261); - } - else { - mp3.start(); - delay(100); - mp3.playAdvertisement(261); - delay(100); - mp3.pause(); - } - delay(2000); - return false; - } - } - if (tempCard.nfcFolderSettings.mode != 0 && tempCard.nfcFolderSettings.mode != 255) { - if (isPlaying()) { - mp3.playAdvertisement(260); - } - else { - mp3.start(); - delay(100); - mp3.playAdvertisement(260); - delay(100); - mp3.pause(); - } - } - switch (tempCard.nfcFolderSettings.mode ) { - case 0: - case 255: - mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1(); adminMenu(true); break; - case 1: activeModifier = new SleepTimer(tempCard.nfcFolderSettings.special); break; - case 2: activeModifier = new FreezeDance(); break; - case 3: activeModifier = new Locked(); break; - case 4: activeModifier = new ToddlerMode(); break; - case 5: activeModifier = new KindergardenMode(); break; - case 6: activeModifier = new RepeatSingleModifier(); break; - - } - delay(2000); - return false; - } - else { - memcpy(nfcTag, &tempCard, sizeof(nfcTagObject)); - Serial.println( nfcTag->nfcFolderSettings.folder); - myFolder = &nfcTag->nfcFolderSettings; - Serial.println( myFolder->folder); - } - return true; - } - else { - memcpy(nfcTag, &tempCard, sizeof(nfcTagObject)); - return true; - } -} - - -void writeCard(nfcTagObject nfcTag) { - MFRC522::PICC_Type mifareType; - byte buffer[16] = {0x13, 0x37, 0xb3, 0x47, // 0x1337 0xb347 magic cookie to - // identify our nfc tags - 0x02, // version 1 - nfcTag.nfcFolderSettings.folder, // the folder picked by the user - nfcTag.nfcFolderSettings.mode, // the playback mode picked by the user - nfcTag.nfcFolderSettings.special, // track or function for admin cards - nfcTag.nfcFolderSettings.special2, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - byte size = sizeof(buffer); - - mifareType = mfrc522.PICC_GetType(mfrc522.uid.sak); - - // Authenticate using key B - //authentificate with the card and set card specific parameters - if ((mifareType == MFRC522::PICC_TYPE_MIFARE_MINI ) || - (mifareType == MFRC522::PICC_TYPE_MIFARE_1K ) || - (mifareType == MFRC522::PICC_TYPE_MIFARE_4K ) ) - { - Serial.println(F("Authenticating again using key A...")); - status = mfrc522.PCD_Authenticate( - MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); - } - else if (mifareType == MFRC522::PICC_TYPE_MIFARE_UL ) - { - byte pACK[] = {0, 0}; //16 bit PassWord ACK returned by the NFCtag - - // Authenticate using key A - Serial.println(F("Authenticating UL...")); - status = mfrc522.PCD_NTAG216_AUTH(key.keyByte, pACK); - } - - if (status != MFRC522::STATUS_OK) { - Serial.print(F("PCD_Authenticate() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); - mp3.playMp3FolderTrack(401); - return; - } - - // Write data to the block - Serial.print(F("Writing data into block ")); - Serial.print(blockAddr); - Serial.println(F(" ...")); - dump_byte_array(buffer, 16); - Serial.println(); - - if ((mifareType == MFRC522::PICC_TYPE_MIFARE_MINI ) || - (mifareType == MFRC522::PICC_TYPE_MIFARE_1K ) || - (mifareType == MFRC522::PICC_TYPE_MIFARE_4K ) ) - { - status = (MFRC522::StatusCode)mfrc522.MIFARE_Write(blockAddr, buffer, 16); - } - else if (mifareType == MFRC522::PICC_TYPE_MIFARE_UL ) - { - byte buffer2[16]; - byte size2 = sizeof(buffer2); - - memset(buffer2, 0, size2); - memcpy(buffer2, buffer, 4); - status = (MFRC522::StatusCode)mfrc522.MIFARE_Write(8, buffer2, 16); - - memset(buffer2, 0, size2); - memcpy(buffer2, buffer + 4, 4); - status = (MFRC522::StatusCode)mfrc522.MIFARE_Write(9, buffer2, 16); - - memset(buffer2, 0, size2); - memcpy(buffer2, buffer + 8, 4); - status = (MFRC522::StatusCode)mfrc522.MIFARE_Write(10, buffer2, 16); - - memset(buffer2, 0, size2); - memcpy(buffer2, buffer + 12, 4); - status = (MFRC522::StatusCode)mfrc522.MIFARE_Write(11, buffer2, 16); - } - - if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Write() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); - mp3.playMp3FolderTrack(401); - } - else - mp3.playMp3FolderTrack(400); - Serial.println(); - delay(2000); -} - - - -/** - Helper routine to dump a byte array as hex values to Serial. -*/ -void dump_byte_array(byte * buffer, byte bufferSize) { - for (byte i = 0; i < bufferSize; i++) { - Serial.print(buffer[i] < 0x10 ? " 0" : " "); - Serial.print(buffer[i], HEX); - } -} - -///////////////////////////////////////// Check Bytes /////////////////////////////////// -bool checkTwo ( uint8_t a[], uint8_t b[] ) { - for ( uint8_t k = 0; k < 4; k++ ) { // Loop 4 times - if ( a[k] != b[k] ) { // IF a != b then false, because: one fails, all fail - return false; - } - } - return true; + tonuino.handleButtons(); + tonuino.handleChipCard(); } diff --git a/sd-card/advert/0262.mp3 b/sd-card/advert/0262.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0a191f101210e0bfbe2b23acdd718bd8e36f19e8 GIT binary patch literal 6407 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 literal 0 HcmV?d00001 diff --git a/sd-card/mp3/0262.mp3 b/sd-card/mp3/0262.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0a191f101210e0bfbe2b23acdd718bd8e36f19e8 GIT binary patch literal 6407 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 literal 0 HcmV?d00001 diff --git a/src/array.hpp b/src/array.hpp new file mode 100644 index 00000000..cbd45b78 --- /dev/null +++ b/src/array.hpp @@ -0,0 +1,46 @@ +#ifndef SRC_ARRAY_HPP_ +#define SRC_ARRAY_HPP_ + +template +struct array { + // Storage + T data[N]; + + static size_t length() { return N; } + using type = T; + + // Item access + T &operator[](size_t index) { return data[index]; } + const T &operator[](size_t index) const { return data[index]; } + + // Iterators + T *begin() { return &data[0]; } + const T *begin() const { return &data[0]; } + T *end() { return &data[N]; } + const T *end() const { return &data[N]; } + + // Comparisons + bool operator==(const array &rhs) const { + if (this == &rhs) + return true; + for (size_t i = 0; i < N; i++) + if ((*this)[i] != rhs[i]) + return false; + return true; + } + bool operator!=(const array &rhs) const { + return !(*this == rhs); + } + array& operator=(const array& rhs) { + if (this != &rhs) + for (size_t i = 0; i < N; i++) + (*this)[i] = rhs[i]; + return *this; + } +}; + + + + + +#endif /* SRC_ARRAY_HPP_ */ diff --git a/src/buttons.cpp b/src/buttons.cpp new file mode 100644 index 00000000..d58e848e --- /dev/null +++ b/src/buttons.cpp @@ -0,0 +1,176 @@ +#include "buttons.hpp" + +namespace { + +const uint32_t LONG_PRESS = 1000; +const uint8_t buttonPause = A0; +const uint8_t buttonUp = A1; +const uint8_t buttonDown = A2; + +#ifdef FIVEBUTTONS +const uint8_t buttonFourPin = A3; +const uint8_t buttonFivePin = A4; +#endif + +} + +Buttons::Buttons(const Settings& settings) +: pauseButton(buttonPause ) +, upButton(buttonUp ) +, downButton(buttonDown ) +#ifdef FIVEBUTTONS +, buttonFour (buttonFourPin); +, buttonFive (buttonFivePin); +#endif +, settings(settings) +{ + pinMode(buttonPause , INPUT_PULLUP); + pinMode(buttonUp , INPUT_PULLUP); + pinMode(buttonDown , INPUT_PULLUP); +#ifdef FIVEBUTTONS + pinMode(buttonFourPin, INPUT_PULLUP); + pinMode(buttonFivePin, INPUT_PULLUP); +#endif +} + +button Buttons::get_button() { + button ret = button::none; + readButtons(); + if (( pauseButton.pressedFor(LONG_PRESS) + || upButton .pressedFor(LONG_PRESS) + || downButton .pressedFor(LONG_PRESS) + ) + && pauseButton.isPressed() + && upButton .isPressed() + && downButton .isPressed()) + ret = button::admin; + + else if (pauseButton.wasReleased()) { + if (not ignorePauseButton) + ret = button::pause; + ignorePauseButton = false; + } + + else if (pauseButton.pressedFor(LONG_PRESS) && not ignorePauseButton) { + ret = button::track; + ignorePauseButton = true; + } + + else if (upButton.wasReleased()) { + if (!ignoreUpButton) { + if (!settings.invertVolumeButtons) + ret = button::next; + else + ret = button::volume_up; + } + ignoreUpButton = false; + } + + else if (upButton.pressedFor(LONG_PRESS)) { +#ifndef FIVEBUTTONS + if (!settings.invertVolumeButtons) + ret = button::volume_up; + else + ret = button::next; + ignoreUpButton = true; +#endif + } + + else if (downButton.wasReleased()) { + if (!ignoreDownButton) { + if (!settings.invertVolumeButtons) + ret = button::previous; + else + ret = button::volume_down; + } + ignoreDownButton = false; + } + + else if (downButton.pressedFor(LONG_PRESS)) { +#ifndef FIVEBUTTONS + if (!settings.invertVolumeButtons) + ret = button::volume_down; + else + ret = button::previous; + ignoreDownButton = true; +#endif + } + +#ifdef FIVEBUTTONS + else if (buttonFour.wasReleased()) { + if (!settings.invertVolumeButtons) + ret = button::volume_up; + else + ret = button::next; + } + + else if (buttonFive.wasReleased()) { + if (!settings.invertVolumeButtons) + ret = button::volume_down; + else + ret = button::previous; + } +#endif + +// if (ret != button::none) { +// Serial.print(F("Button: ")); Serial.println(static_cast(ret)); +// } + return ret; +} + +void Buttons::wait_for_no_button() { + do { + readButtons(); + } while ( pauseButton.isPressed() + || upButton .isPressed() + || downButton .isPressed() +#ifdef FIVEBUTTONS + || buttonFour .isPressed() + || buttonFive .isPressed() +#endif + ); +} + +bool Buttons::is_reset() { + return (digitalRead(buttonPause) == LOW && + digitalRead(buttonUp) == LOW && + digitalRead(buttonDown) == LOW ); +} + +bool Buttons::is_break() { + readButtons(); + if (upButton.wasReleased() || downButton.wasReleased()) { + Serial.print(F("Abgebrochen!")); + return true; + } + return false; +} + +bool Buttons::askCode(Settings::pin_t &code) { + uint8_t x = 0; + while (x < 4) { + readButtons(); + if (pauseButton.pressedFor(LONG_PRESS)) + return false; + if (pauseButton.wasReleased()) + code[x++] = 1; + if (upButton.wasReleased()) + code[x++] = 2; + if (downButton.wasReleased()) + code[x++] = 3; + } + return true; +} + +void Buttons::readButtons() { + pauseButton.read(); + upButton .read(); + downButton .read(); +#ifdef FIVEBUTTONS + buttonFour .read(); + buttonFive .read(); +#endif +} + + + diff --git a/src/buttons.hpp b/src/buttons.hpp new file mode 100644 index 00000000..32943035 --- /dev/null +++ b/src/buttons.hpp @@ -0,0 +1,55 @@ +#ifndef SRC_BUTTONS_HPP_ +#define SRC_BUTTONS_HPP_ + +#include +#include + +#include "settings.hpp" + +// uncomment the below line to enable five button support +//#define FIVEBUTTONS + +enum class button { + none, + admin, + pause, + track, + volume_up, + volume_down, + next, + previous, +}; + +class Buttons { +public: + Buttons(const Settings& settings); + + button get_button(); + void wait_for_no_button(); + bool is_reset(); + bool is_break(); + bool askCode(Settings::pin_t &code); + +private: + + void readButtons(); + + Button pauseButton; + Button upButton; + Button downButton; + #ifdef FIVEBUTTONS + Button buttonFour; + Button buttonFive; + #endif + bool ignorePauseButton = false; + bool ignoreUpButton = false; + bool ignoreDownButton = false; + #ifdef FIVEBUTTONS + bool ignoreButtonFour = false; + bool ignoreButtonFive = false; + #endif + + const Settings& settings; +}; + +#endif /* SRC_BUTTONS_HPP_ */ diff --git a/src/chip_card.cpp b/src/chip_card.cpp new file mode 100644 index 00000000..c6de6d0f --- /dev/null +++ b/src/chip_card.cpp @@ -0,0 +1,244 @@ +#include +#include +#include + +#include "chip_card.hpp" + +namespace { +/** + Helper routine to dump a byte array as hex values to Serial. +*/ +void dump_byte_array(byte * buffer, byte bufferSize) { + for (byte i = 0; i < bufferSize; i++) { + Serial.print(buffer[i] < 0x10 ? " 0" : " "); + Serial.print(buffer[i], HEX); + } +} +const byte RST_PIN = 9; // Configurable, see typical pin layout above +const byte SS_PIN = 10; // Configurable, see typical pin layout above + +MFRC522::MIFARE_Key key{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +const byte sector = 1; +const byte trailerBlock = 7; +} // namespace + +Chip_card::Chip_card() +: mfrc522(*(new MFRC522(SS_PIN, RST_PIN))) +{} + +bool Chip_card::readCard(nfcTagObject &nfcTag) { + // Show some details of the PICC (that is: the tag/card) + Serial.print(F("Card UID:")); + dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); + Serial.println(); + Serial.print(F("PICC type: ")); + MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); + Serial.println(mfrc522.PICC_GetTypeName(piccType)); + + byte buffer[18]; + MFRC522::StatusCode status = MFRC522::STATUS_ERROR; + + // Authenticate using key A + if ((piccType == MFRC522::PICC_TYPE_MIFARE_MINI) || + (piccType == MFRC522::PICC_TYPE_MIFARE_1K ) || + (piccType == MFRC522::PICC_TYPE_MIFARE_4K ) ) + { + Serial.println(F("Authenticating Classic using key A...")); + status = mfrc522.PCD_Authenticate( + MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); + } + else if (piccType == MFRC522::PICC_TYPE_MIFARE_UL ) + { + byte pACK[] = {0, 0}; //16 bit PassWord ACK returned by the tempCard + + // Authenticate using key A + Serial.println(F("Authenticating MIFARE UL...")); + status = mfrc522.PCD_NTAG216_AUTH(key.keyByte, pACK); + } + + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + + // Show the whole sector as it currently is + // Serial.println(F("Current data in sector:")); + // mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); + // Serial.println(); + + // Read data from the block + if ((piccType == MFRC522::PICC_TYPE_MIFARE_MINI) || + (piccType == MFRC522::PICC_TYPE_MIFARE_1K ) || + (piccType == MFRC522::PICC_TYPE_MIFARE_4K ) ) + { + byte size = sizeof(buffer); + status = (MFRC522::StatusCode)mfrc522.MIFARE_Read(4, buffer, &size); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + } + else if (piccType == MFRC522::PICC_TYPE_MIFARE_UL ) + { + byte buffer2[18]; + byte size2 = sizeof(buffer2); + + status = static_cast(mfrc522.MIFARE_Read(8, buffer2, &size2)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read_1() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + memcpy(buffer, buffer2, 4); + + status = static_cast(mfrc522.MIFARE_Read(9, buffer2, &size2)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read_2() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + memcpy(buffer + 4, buffer2, 4); + + status = static_cast(mfrc522.MIFARE_Read(10, buffer2, &size2)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read_3() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + memcpy(buffer + 8, buffer2, 4); + + status = static_cast(mfrc522.MIFARE_Read(11, buffer2, &size2)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read_4() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + memcpy(buffer + 12, buffer2, 4); + } + + Serial.print(F("Data on Card ")); + Serial.println(F(":")); + dump_byte_array(buffer, 16); + Serial.println(); + Serial.println(); + + uint32_t tempCookie; + tempCookie = (uint32_t)buffer[0] << 24; + tempCookie += (uint32_t)buffer[1] << 16; + tempCookie += (uint32_t)buffer[2] << 8; + tempCookie += (uint32_t)buffer[3]; + + nfcTag.cookie = tempCookie; + nfcTag.version = buffer[4]; + nfcTag.nfcFolderSettings.folder = buffer[5]; + nfcTag.nfcFolderSettings.mode = static_cast(buffer[5] ? buffer[6] : 0x80 | buffer[6]); + nfcTag.nfcFolderSettings.special = buffer[7]; + nfcTag.nfcFolderSettings.special2 = buffer[8]; + + return true; +} + +bool Chip_card::writeCard(const nfcTagObject &nfcTag) { + MFRC522::PICC_Type mifareType; + byte buffer[16] = {0x13, 0x37, 0xb3, 0x47, // 0x1337 0xb347 magic cookie to + // identify our nfc tags + 0x02, // version 1 + nfcTag.nfcFolderSettings.folder, // the folder picked by the user + static_cast(nfcTag.nfcFolderSettings.mode), // the playback mode picked by the user + nfcTag.nfcFolderSettings.special, // track or function for admin cards + nfcTag.nfcFolderSettings.special2, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + mifareType = mfrc522.PICC_GetType(mfrc522.uid.sak); + + MFRC522::StatusCode status = MFRC522::STATUS_ERROR; + + // Authenticate using key B + //authentificate with the card and set card specific parameters + if ((mifareType == MFRC522::PICC_TYPE_MIFARE_MINI ) || + (mifareType == MFRC522::PICC_TYPE_MIFARE_1K ) || + (mifareType == MFRC522::PICC_TYPE_MIFARE_4K ) ) + { + Serial.println(F("Authenticating again using key A...")); + status = mfrc522.PCD_Authenticate( + MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); + } + else if (mifareType == MFRC522::PICC_TYPE_MIFARE_UL ) + { + byte pACK[] = {0, 0}; //16 bit PassWord ACK returned by the NFCtag + + // Authenticate using key A + Serial.println(F("Authenticating UL...")); + status = mfrc522.PCD_NTAG216_AUTH(key.keyByte, pACK); + } + + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + + // Write data to the block + Serial.println(F("Writing data ...")); + dump_byte_array(buffer, 16); + Serial.println(); + + if ((mifareType == MFRC522::PICC_TYPE_MIFARE_MINI ) || + (mifareType == MFRC522::PICC_TYPE_MIFARE_1K ) || + (mifareType == MFRC522::PICC_TYPE_MIFARE_4K ) ) + { + status = (MFRC522::StatusCode)mfrc522.MIFARE_Write(4, buffer, 16); + } + else if (mifareType == MFRC522::PICC_TYPE_MIFARE_UL ) + { + byte buffer2[16]; + byte size2 = sizeof(buffer2); + + memset(buffer2, 0, size2); + memcpy(buffer2, buffer, 4); + status = static_cast(mfrc522.MIFARE_Write(8, buffer2, 16)); + + memset(buffer2, 0, size2); + memcpy(buffer2, buffer + 4, 4); + status = static_cast(mfrc522.MIFARE_Write(9, buffer2, 16)); + + memset(buffer2, 0, size2); + memcpy(buffer2, buffer + 8, 4); + status = static_cast(mfrc522.MIFARE_Write(10, buffer2, 16)); + + memset(buffer2, 0, size2); + memcpy(buffer2, buffer + 12, 4); + status = static_cast(mfrc522.MIFARE_Write(11, buffer2, 16)); + } + + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + Serial.println(); + return true; +} + +void Chip_card::sleepCard() { + mfrc522.PCD_AntennaOff(); + mfrc522.PCD_SoftPowerDown(); +} + +void Chip_card::initCard() { + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 + mfrc522.PCD_DumpVersionToSerial(); // Show details of PCD - MFRC522 Card Reader +} + +bool Chip_card::newCardPresent() { + return mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial(); +} + +void Chip_card::stopCard() { + mfrc522.PICC_HaltA(); + mfrc522.PCD_StopCrypto1(); +} diff --git a/src/chip_card.hpp b/src/chip_card.hpp new file mode 100644 index 00000000..badc6531 --- /dev/null +++ b/src/chip_card.hpp @@ -0,0 +1,67 @@ +#ifndef SRC_CHIP_CARD_HPP_ +#define SRC_CHIP_CARD_HPP_ + +#include + +const uint32_t cardCookie = 322417479; + +enum class mode_t: uint8_t { + none = 0, + + // folder modes + hoerspiel = 1, + album = 2, + party = 3, + einzel = 4, + hoerbuch = 5, + admin = 6, + hoerspiel_vb = 7, + album_vb = 8, + party_vb = 9, + + // modifier modes + sleep_timer = 0x80 | 1, + freeze_dance = 0x80 | 2, + locked = 0x80 | 3, + toddler = 0x80 | 4, + kindergarden = 0x80 | 5, + repeat_single = 0x80 | 6, + + admin_card = 0xff, +}; + +struct folderSettings { + uint8_t folder; + mode_t mode; + uint8_t special; + uint8_t special2; +}; + +// this object stores nfc tag data +struct nfcTagObject { + uint32_t cookie; + uint8_t version; + folderSettings nfcFolderSettings; +}; + +class MFRC522; // forward declaration + +class Chip_card { +public: + Chip_card(); + + bool readCard(nfcTagObject &nfcTag); + bool writeCard(const nfcTagObject &nfcTag); + void sleepCard(); + void initCard(); + void stopCard(); + bool newCardPresent(); + +private: + MFRC522 &mfrc522; +}; + + + + +#endif /* SRC_CHIP_CARD_HPP_ */ diff --git a/src/modifier.cpp b/src/modifier.cpp new file mode 100644 index 00000000..732439dc --- /dev/null +++ b/src/modifier.cpp @@ -0,0 +1,103 @@ +#include "modifier.hpp" + +#include "mp3.hpp" +#include "tonuino.hpp" + + +void SleepTimer::loop() { + if (sleepAtMillis != 0 && millis() > sleepAtMillis) { + Serial.println(F("=== SleepTimer::loop() -> SLEEP!")); + mp3.pause(); + tonuino.setStandbyTimer(); + tonuino.resetActiveModifier(); + delete this; + } +} + +void SleepTimer::start(uint8_t minutes) { + Serial.println(F("=== SleepTimer()")); + Serial.println(minutes); + sleepAtMillis = millis() + minutes * 60000; + //playAdvertisement(302); +} + +void FreezeDance::loop() { + if (nextStopAtMillis != 0 && millis() > nextStopAtMillis) { + Serial.println(F("== FreezeDance::loop() -> FREEZE!")); + if (mp3.isPlaying()) { + mp3.playAdvertisement(301); + } + setNextStopAtMillis(); + } +} + +void FreezeDance::setNextStopAtMillis() { + const uint16_t seconds = random(minSecondsBetweenStops, maxSecondsBetweenStops + 1); + Serial.println(F("=== FreezeDance::setNextStopAtMillis()")); + Serial.println(seconds); + nextStopAtMillis = millis() + seconds * 1000; +} + +bool KindergardenMode::handleNext() { + Serial.println(F("== KindergardenMode::handleNext() -> NEXT")); + //if (nextCard.cookie == cardCookie && nextCard.nfcFolderSettings.folder != 0 && nextCard.nfcFolderSettings.mode != 0) { + //myFolder = &nextCard.nfcFolderSettings; + if (cardQueued) { + cardQueued = false; + + tonuino.setCard(nextCard); + Serial.println(nextCard.nfcFolderSettings.folder); + //Serial.println(myFolder->mode); + tonuino.playFolder(); + return true; + } + return false; +} +bool KindergardenMode::handleRFID(const nfcTagObject &newCard) { // lot of work to do! + Serial.println(F("== KindergardenMode::handleRFID() -> queued!")); + nextCard = newCard; + cardQueued = true; + if (!mp3.isPlaying()) { + handleNext(); + } + return true; +} + +bool RepeatSingleModifier::handleNext() { + Serial.println(F("== RepeatSingleModifier::handleNext() -> REPEAT CURRENT TRACK")); + delay(50); + if (!mp3.isPlaying()) { + mp3.loop(); // this will call Mp3Notify::OnPlayFinished() but will be blocked by lastTrackFinished + Mp3Notify::ResetLastTrackFinished(); + tonuino.playCurrentTrack(); + } + + return true; +} + +//bool FeedbackModifier::handleVolumeDown() { +// if (volume > settings.minVolume) { +// playAdvertisement(volume - 1, false); +// } else { +// playAdvertisement(volume, false); +// } +// Serial.println(F("== FeedbackModifier::handleVolumeDown()!")); +// return false; +//} +//bool FeedbackModifier::handleVolumeUp() { +// if (volume < settings.maxVolume) { +// playAdvertisement(volume + 1, false); +// } else { +// playAdvertisement(volume, false); +// } +// Serial.println(F("== FeedbackModifier::handleVolumeUp()!")); +// return false; +//} +//bool FeedbackModifier::handleRFID(const nfcTagObject &/*newCard*/) { +// Serial.println(F("== FeedbackModifier::handleRFID()")); +// return false; +//} + + + + diff --git a/src/modifier.hpp b/src/modifier.hpp new file mode 100644 index 00000000..79f7e2f6 --- /dev/null +++ b/src/modifier.hpp @@ -0,0 +1,125 @@ +#ifndef SRC_MODIFIER_HPP_ +#define SRC_MODIFIER_HPP_ + +#include + +#include "chip_card.hpp" + +class Tonuino; +class Mp3; +class Settings; +struct nfcTagObject; + +class Modifier { +public: + Modifier(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): tonuino(tonuino), mp3(mp3), settings(settings) {} + virtual ~Modifier () {} + virtual void loop () {} + virtual bool handlePause () { return false; } + virtual bool handleNext () { return false; } + virtual bool handlePrevious () { return false; } + virtual bool handleNextButton () { return false; } + virtual bool handlePreviousButton() { return false; } + virtual bool handleVolumeUp () { return false; } + virtual bool handleVolumeDown () { return false; } + virtual bool handleRFID(const nfcTagObject&) + { return false; } + virtual mode_t getActive () { return mode_t::none; } + virtual void init () {} + Modifier& operator=(const Modifier&) = delete; +protected: + Tonuino &tonuino; + Mp3 &mp3; + const Settings &settings; +}; + +class SleepTimer: public Modifier { +public: + SleepTimer(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} + void loop () final; + mode_t getActive() final { return mode_t::sleep_timer; } + void start (uint8_t minutes); + +private: + unsigned long sleepAtMillis = 0; +}; + +class FreezeDance: public Modifier { +public: + FreezeDance(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} + void loop () final; + mode_t getActive() final { return mode_t::freeze_dance; } + void init () final { setNextStopAtMillis(); } + + void setNextStopAtMillis(); + +private: + unsigned long nextStopAtMillis = 0; + const uint8_t minSecondsBetweenStops = 5; + const uint8_t maxSecondsBetweenStops = 30; +}; + +class Locked: public Modifier { +public: + Locked(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} + bool handlePause () final { Serial.println(F("== Locked::handlePause() -> LOCKED!")) ; return true; } + bool handleNextButton () final { Serial.println(F("== Locked::handleNextButton() -> LOCKED!")) ; return true; } + bool handlePreviousButton() final { Serial.println(F("== Locked::handlePreviousButton() -> LOCKED!")); return true; } + bool handleVolumeUp () final { Serial.println(F("== Locked::handleVolumeUp() -> LOCKED!")) ; return true; } + bool handleVolumeDown () final { Serial.println(F("== Locked::handleVolumeDown() -> LOCKED!")) ; return true; } + bool handleRFID(const nfcTagObject&) + final { Serial.println(F("== Locked::handleRFID() -> LOCKED!")) ; return true; } + + mode_t getActive() final { return mode_t::locked; } +}; + +class ToddlerMode: public Modifier { +public: + ToddlerMode(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} + bool handlePause () final { Serial.println(F("== ToddlerMode::handlePause() -> LOCKED!")) ; return true; } + bool handleNextButton () final { Serial.println(F("== ToddlerMode::handleNextButton() -> LOCKED!")) ; return true; } + bool handlePreviousButton() final { Serial.println(F("== ToddlerMode::handlePreviousButton() -> LOCKED!")); return true; } + bool handleVolumeUp () final { Serial.println(F("== ToddlerMode::handleVolumeUp() -> LOCKED!")) ; return true; } + bool handleVolumeDown () final { Serial.println(F("== ToddlerMode::handleVolumeDown() -> LOCKED!")) ; return true; } + + mode_t getActive() final { return mode_t::toddler; } +}; + +class KindergardenMode: public Modifier { +public: + KindergardenMode(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} + bool handleNext() final; + + //bool handlePause () final { Serial.println(F("== KindergardenMode::handlePause() -> LOCKED!")) ; return true; } + bool handleNextButton () final { Serial.println(F("== KindergardenMode::handleNextButton() -> LOCKED!")) ; return true; } + bool handlePreviousButton() final { Serial.println(F("== KindergardenMode::handlePreviousButton() -> LOCKED!")); return true; } + + bool handleRFID(const nfcTagObject &newCard) final; + mode_t getActive () final { return mode_t::kindergarden; } + void init () final { cardQueued = false; } + +private: + nfcTagObject nextCard{}; + bool cardQueued = false; +}; + +class RepeatSingleModifier: public Modifier { +public: + RepeatSingleModifier(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} + bool handleNext() final; + mode_t getActive () final { return mode_t::repeat_single; } +}; + +// An modifier can also do somethings in addition to the modified action +// by returning false (not handled) at the end +// This simple FeedbackModifier will tell the volume before changing it and +// give some feedback once a RFID card is detected. +//class FeedbackModifier: public Modifier { +//public: +// FeedbackModifier(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} +// bool handleVolumeDown() final; +// bool handleVolumeUp () final; +// bool handleRFID (const nfcTagObject &newCard) final; +//}; + +#endif /* SRC_MODIFIER_HPP_ */ diff --git a/src/mp3.cpp b/src/mp3.cpp new file mode 100644 index 00000000..d66b261a --- /dev/null +++ b/src/mp3.cpp @@ -0,0 +1,112 @@ +#include "mp3.hpp" + +#include "tonuino.hpp" + +namespace { + +const uint8_t receivePin = 2; +const uint8_t transmitPin = 3; +const uint8_t busyPin = 4; +} + +uint16_t Mp3Notify::lastTrackFinished = 0; +void Mp3Notify::OnError(uint16_t errorCode) { + // see DfMp3_Error for code meaning + Serial.println(); + Serial.print(F("Com Error ")); + Serial.println(errorCode); +} +void Mp3Notify::OnPlaySourceOnline (DfMp3_PlaySources source) { PrintlnSourceAction(source, F("online" )); } +void Mp3Notify::OnPlaySourceInserted(DfMp3_PlaySources source) { PrintlnSourceAction(source, F("bereit" )); } +void Mp3Notify::OnPlaySourceRemoved (DfMp3_PlaySources source) { PrintlnSourceAction(source, F("entfernt")); } +void Mp3Notify::PrintlnSourceAction(DfMp3_PlaySources source, const __FlashStringHelper* action) { + if (source & DfMp3_PlaySources_Sd ) Serial.print(F("SD Karte ")); + if (source & DfMp3_PlaySources_Usb ) Serial.print(F("USB " )); + if (source & DfMp3_PlaySources_Flash) Serial.print(F("Flash " )); + Serial.println(action); +} + +void Mp3Notify::OnPlayFinished(DfMp3_PlaySources /*source*/, uint16_t track) { +// Serial.print(F("Track beendet")); +// Serial.println(track); + if (track == lastTrackFinished) + return; + else + lastTrackFinished = track; + tonuino.nextTrack(); +} + +Mp3::Mp3(const Settings& settings) +: DFMiniMp3{softwareSerial} +, softwareSerial{receivePin, transmitPin} +, settings{settings} +{ + // Busy Pin + pinMode(busyPin, INPUT); +} + +bool Mp3::isPlaying() const { + return !digitalRead(busyPin); +} + +void Mp3::waitForTrackToFinish() { + unsigned long currentTime = millis(); + const unsigned long maxStartTime = 1000; + + // wait until track is started + do { + loop(); + } while (!isPlaying() && millis() < currentTime + maxStartTime); + delay(1000); + + // wait until track is finished + do { + loop(); + } while (isPlaying()); +} + +void Mp3::playMp3FolderTrack(uint16_t track) { + DFMiniMp3::playMp3FolderTrack(track); +} + +void Mp3::playMp3FolderTrack(mp3Tracks track) { + DFMiniMp3::playMp3FolderTrack(static_cast(track)); +} + +void Mp3::playAdvertisement(uint16_t track, bool olnyIfIsPlaying) { + if (isPlaying()) { + DFMiniMp3::playAdvertisement(track); + delay(500); + } else if (not olnyIfIsPlaying) { + start(); + DFMiniMp3::playAdvertisement(track); + waitForTrackToFinish(); + pause(); + } +} + +void Mp3::playAdvertisement(advertTracks track, bool olnyIfIsPlaying) { + playAdvertisement(static_cast(track), olnyIfIsPlaying); +} + + +void Mp3::increaseVolume() { + if (volume < settings.maxVolume) { + DFMiniMp3::increaseVolume(); + ++volume; + } + Serial.println(volume); +} + +void Mp3::decreaseVolume() { + if (volume > settings.minVolume) { + DFMiniMp3::decreaseVolume(); + --volume; + } + Serial.println(volume); +} + +void Mp3::setVolume() { + volume = settings.initVolume; + DFMiniMp3::setVolume(volume); +} diff --git a/src/mp3.hpp b/src/mp3.hpp new file mode 100644 index 00000000..9233861c --- /dev/null +++ b/src/mp3.hpp @@ -0,0 +1,144 @@ +#ifndef SRC_MP3_HPP_ +#define SRC_MP3_HPP_ + +#include +#include +#include + +#include "settings.hpp" + +enum class mp3Tracks: uint16_t { + t_0 = 0, + t_262_pling = 262, + t_300_new_tag = 300, + t_301_select_folder = 301, + t_310_select_mode = 310, + t_311_mode_random_episode = 311, + t_312_mode_album = 312, + t_313_mode_party = 313, + t_314_mode_single_track = 314, + t_315_mode_audio_book = 315, + t_316_admin = 316, + t_317_special_random = 317, + t_318_special_album = 318, + t_319_special_party = 319, + t_320_select_file = 320, + t_321_select_first_file = 321, + t_322_select_last_file = 322, + t_330_select_say_number = 330, + t_331_do_not_say_number = 331, + t_332_say_number = 332, + t_400_ok = 400, + t_401_error = 401, + t_800_waiting_for_card = 800, + t_802_reset_aborted = 802, + t_900_admin = 900, + t_901_card_reset = 901, + t_902_max_volume = 902, + t_903_min_volume = 903, + t_904_init_volume = 904, + t_905_eq = 905, + t_906_modifiers = 906, + t_907_shortcut = 907, + t_908_standbytimer = 908, + t_909_batch_cards = 909, + t_910_switch_volume = 910, + t_911_reset = 911, + t_912_admin_lock = 912, + t_920_eq_intro = 920, + t_921_normal = 921, + t_922_pop = 922, + t_923_rock = 923, + t_924_jazz = 924, + t_925_classic = 925, + t_926_bass = 926, + t_930_max_volume_intro = 930, + t_931_min_volume_into = 931, + t_932_init_volume_into = 932, + t_933_switch_volume_intro = 933, + t_934_no = 934, + t_935_yes = 935, + t_936_batch_cards_intro = 936, + t_940_shortcut_into = 940, + t_941_pause = 941, + t_942_up = 942, + t_943_down = 943, + t_944_startup = 944, + t_960_timer_intro = 960, + t_961_timer_5 = 961, + t_962_timer_15 = 962, + t_963_timer_30 = 963, + t_964_timer_60 = 964, + t_965_timer_disabled = 965, + t_970_modifier_Intro = 970, + t_971_modifier_SleepTimer = 971, + t_972_modifier_FreezeDance = 972, + t_973_modifier_Locked = 973, + t_974_modifier_Toddler = 974, + t_975_modifier_KinderGarden = 975, + t_976_modifier_repeat1 = 976, + t_980_admin_lock_intro = 980, + t_981_admin_lock_disabled = 981, + t_982_admin_lock_card = 982, + t_983_admin_lock_pin = 983, + t_984_admin_lock_calc = 984, + t_991_admin_pin = 991, + t_992_admin_calc = 992, + t_993_admin_calc = 993, + t_994_admin_calc = 994, + t_999_reset_ok = 999, +}; + +inline mp3Tracks operator+(mp3Tracks lhs, uint16_t rhs) { return static_cast(static_cast(lhs)+rhs); } + +enum class advertTracks: uint16_t { + t_260_activate_mod_card = 260, + t_261_deactivate_mod_card = 261, + t_262_pling = 262, + t_300_freeze_into = 300, + t_301_freeze_freeze = 301, + t_302_sleep = 302, + t_303_locked = 303, + t_304_buttonslocked = 304, + t_305_kindergarden = 305, +}; + +// implement a notification class, +// its member methods will get called +// +class Mp3Notify { +public: + static void OnError (uint16_t errorCode); + static void OnPlayFinished (DfMp3_PlaySources source, uint16_t track); + static void OnPlaySourceOnline (DfMp3_PlaySources source); + static void OnPlaySourceInserted(DfMp3_PlaySources source); + static void OnPlaySourceRemoved (DfMp3_PlaySources source); + static void ResetLastTrackFinished() { lastTrackFinished = 0; } +private: + static void PrintlnSourceAction (DfMp3_PlaySources source, const __FlashStringHelper* action); + static uint16_t lastTrackFinished; +}; + +class Mp3: public DFMiniMp3 { +public: + Mp3(const Settings& settings); + + bool isPlaying() const; + void waitForTrackToFinish(); + void playMp3FolderTrack(uint16_t track); + void playMp3FolderTrack(mp3Tracks track); + void playAdvertisement(uint16_t track, bool olnyIfIsPlaying = true); + void playAdvertisement(advertTracks track, bool olnyIfIsPlaying = true); + + void increaseVolume(); + void decreaseVolume(); + void setVolume (); + +private: + SoftwareSerial softwareSerial; + const Settings& settings; + + uint8_t volume{}; +}; + +#endif /* SRC_MP3_HPP_ */ diff --git a/src/settings.cpp b/src/settings.cpp new file mode 100644 index 00000000..b3634ed3 --- /dev/null +++ b/src/settings.cpp @@ -0,0 +1,110 @@ +#include "settings.hpp" + +#include + +namespace { + +const int startAddressAdminSettings = sizeof(folderSettings::folder) * 100; + +} + +void Settings::clearEEPROM() { + Serial.println(F("Reset -> EEPROM wird gelöscht")); + for (unsigned int i = 0; i < EEPROM.length(); i++) { + writeFolderSettingToFlash(i, 0); + } +} + +void Settings::writeSettingsToFlash() { + Serial.println(F("=== writeSettingsToFlash()")); + EEPROM.put(startAddressAdminSettings, *this); +} + +void Settings::resetSettings() { + Serial.println(F("=== resetSettings()")); + cookie = cardCookie; + version = 2; + maxVolume = 25; + minVolume = 5; + initVolume = 15; + eq = 1; + locked = false; + standbyTimer = 0; + invertVolumeButtons = true; + shortCuts[0].folder = 0; + shortCuts[1].folder = 0; + shortCuts[2].folder = 0; + shortCuts[3].folder = 0; + adminMenuLocked = 0; + adminMenuPin[0] = 1; + adminMenuPin[1] = 1; + adminMenuPin[2] = 1; + adminMenuPin[3] = 1; + + writeSettingsToFlash(); +} + +void Settings::migrateSettings(int oldVersion) { + if (oldVersion == 1) { + Serial.println(F("=== resetSettings()")); + Serial.println(F("1 -> 2")); + version = 2; + adminMenuLocked = 0; + adminMenuPin[0] = 1; + adminMenuPin[1] = 1; + adminMenuPin[2] = 1; + adminMenuPin[3] = 1; + writeSettingsToFlash(); + } +} + +void Settings::loadSettingsFromFlash() { + Serial.println(F("=== loadSettingsFromFlash()")); + EEPROM.get(startAddressAdminSettings, *this); + if (cookie != cardCookie) + resetSettings(); + migrateSettings(version); + + Serial.print(F("Version: ")); + Serial.println(version); + + Serial.print(F("Maximal Volume: ")); + Serial.println(maxVolume); + + Serial.print(F("Minimal Volume: ")); + Serial.println(minVolume); + + Serial.print(F("Initial Volume: ")); + Serial.println(initVolume); + + Serial.print(F("EQ: ")); + Serial.println(eq); + + Serial.print(F("Locked: ")); + Serial.println(locked); + + Serial.print(F("Sleep Timer: ")); + Serial.println(standbyTimer); + + Serial.print(F("Inverted Volume Buttons: ")); + Serial.println(invertVolumeButtons); + + Serial.print(F("Admin Menu locked: ")); + Serial.println(adminMenuLocked); + + Serial.print(F("Admin Menu Pin: ")); + Serial.print(adminMenuPin[0]); + Serial.print(adminMenuPin[1]); + Serial.print(adminMenuPin[2]); + Serial.println(adminMenuPin[3]); +} + +void Settings::writeFolderSettingToFlash(uint8_t folder, uint16_t track) { + EEPROM.update(folder, track); +} + +uint16_t Settings::readFolderSettingFromFlash(uint8_t folder) { + return EEPROM.read(folder); +} + + diff --git a/src/settings.hpp b/src/settings.hpp new file mode 100644 index 00000000..c3133e4a --- /dev/null +++ b/src/settings.hpp @@ -0,0 +1,38 @@ +#ifndef SRC_SETTINGS_HPP_ +#define SRC_SETTINGS_HPP_ + +#include + +#include "array.hpp" +#include "chip_card.hpp" + +// admin settings stored in eeprom +struct Settings { + typedef array shortCuts_t; + typedef array pin_t; + + void clearEEPROM(); + + void writeSettingsToFlash(); + void resetSettings(); + void migrateSettings(int oldVersion); + void loadSettingsFromFlash(); + + void writeFolderSettingToFlash(uint8_t folder, uint16_t track); + uint16_t readFolderSettingFromFlash(uint8_t folder); + + uint32_t cookie; + byte version; + uint8_t maxVolume; + uint8_t minVolume; + uint8_t initVolume; + uint8_t eq; + bool locked; + long standbyTimer; + bool invertVolumeButtons; + shortCuts_t shortCuts; + uint8_t adminMenuLocked; + pin_t adminMenuPin; +}; + +#endif /* SRC_SETTINGS_HPP_ */ diff --git a/src/tonuino.cpp b/src/tonuino.cpp new file mode 100644 index 00000000..d49809ba --- /dev/null +++ b/src/tonuino.cpp @@ -0,0 +1,886 @@ +#include "tonuino.hpp" + +#include +#include + +#include "array.hpp" +#include "chip_card.hpp" + +Tonuino tonuino; + +namespace { + +const uint8_t shutdownPin = 7; + +} + +void Tonuino::setup() { + pinMode(shutdownPin , OUTPUT); + digitalWrite(shutdownPin, LOW); + + // load Settings from EEPROM + settings.loadSettingsFromFlash(); + + // activate standby timer + tonuino.setStandbyTimer(); + + // DFPlayer Mini initialisieren + mp3.begin(); + // Zwei Sekunden warten bis der DFPlayer Mini initialisiert ist + delay(2000); + mp3.setVolume(); + mp3.setEq(static_cast(settings.eq - 1)); + + // NFC Leser initialisieren + chip_card.initCard(); + + // RESET --- ALLE DREI KNÖPFE BEIM STARTEN GEDRÜCKT HALTEN -> alle EINSTELLUNGEN werden gelöscht + if (buttons.is_reset()) { + settings.clearEEPROM(); + settings.loadSettingsFromFlash(); + } + + // Start Shortcut "at Startup" - e.g. Welcome Sound + tonuino.playShortCut(3); +} + +void Tonuino::handleButtons() { + checkStandbyAtMillis(); + + mp3.loop(); + + // Modifier : WIP! + activeModifier->loop(); + + switch (buttons.get_button()) { + + case button::admin: + mp3.pause(); + buttons.wait_for_no_button(); + if (not adminMenuAllowed()) { + mp3.start(); + break; + } + adminMenu(); + break; + + case button::pause: + if (activeModifier->handlePause()) + break; + if (mp3.isPlaying()) { + mp3.pause(); + setStandbyTimer(); + } else if (knownCard) { + mp3.start(); + disableStandbyTimer(); + } + break; + + case button::track: + if (activeModifier->handlePause()) + break; + if (mp3.isPlaying()) { + uint8_t advertTrack = getCurrentTrack(); + // Spezialmodus Von-Bis für Album und Party gibt die Dateinummer relativ zur Startposition wieder + if (myFolder->mode == mode_t::album_vb || myFolder->mode == mode_t::party_vb) { + advertTrack = advertTrack - myFolder->special + 1; + } + mp3.playAdvertisement(advertTrack); + } else { + playShortCut(0); + } + break; + + case button::volume_up: + if (mp3.isPlaying()) + volumeUpButton(); + else + playShortCut(1); + break; + + case button::next: + if (mp3.isPlaying()) + nextButton(); + else + playShortCut(1); + break; + + case button::volume_down: + if (mp3.isPlaying()) + volumeDownButton(); + else + playShortCut(2); + break; + + case button::previous: + if (mp3.isPlaying()) + previousButton(); + else + playShortCut(2); + break; + default: + break; + } +} + +void Tonuino::handleChipCard() { + if (!chip_card.newCardPresent()) + return; + + // RFID Karte wurde aufgelegt + nfcTagObject tempCard; + if (chip_card.readCard(tempCard) && !specialCard(tempCard)) { + setCard(tempCard); + Serial.println(myCard.nfcFolderSettings.folder); + + if (myCard.cookie == cardCookie && myCard.nfcFolderSettings.folder != 0 + && myCard.nfcFolderSettings.mode != mode_t::none) { + knownCard = false; + mp3.playMp3FolderTrack(mp3Tracks::t_262_pling); + mp3.waitForTrackToFinish(); + playFolder(); + } + + // Neue Karte konfigurieren + else if (myCard.cookie != cardCookie) { + knownCard = false; + mp3.playMp3FolderTrack(mp3Tracks::t_300_new_tag); + mp3.waitForTrackToFinish(); + setupCard(); + } + } + chip_card.stopCard(); +} + +void Tonuino::playFolder() { + Serial.println(F("== playFolder()")); + disableStandbyTimer(); + knownCard = true; + numTracksInFolder = mp3.getFolderTrackCount(myFolder->folder); + firstTrack = 1; + Serial.print(numTracksInFolder); + Serial.print(F(" Dateien in Ordner ")); + Serial.println(myFolder->folder); + + switch (myFolder->mode) { + + case mode_t::hoerspiel: + // Hörspielmodus: eine zufällige Datei aus dem Ordner + Serial.println(F("Hörspielmodus -> zufälligen Track wiedergeben")); + currentTrack = random(1, numTracksInFolder + 1); + Serial.println(currentTrack); + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::album: + // Album Modus: kompletten Ordner spielen + Serial.println(F("Album Modus -> kompletten Ordner wiedergeben")); + currentTrack = 1; + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::party: + // Party Modus: Ordner in zufälliger Reihenfolge + Serial.println( + F("Party Modus -> Ordner in zufälliger Reihenfolge wiedergeben")); + shuffleQueue(); + currentTrack = 1; + mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); + break; + + case mode_t::einzel: + // Einzel Modus: eine Datei aus dem Ordner abspielen + Serial.println(F("Einzel Modus -> eine Datei aus dem Odrdner abspielen")); + currentTrack = myFolder->special; + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::hoerbuch: + // Hörbuch Modus: kompletten Ordner spielen und Fortschritt merken + Serial.println(F("Hörbuch Modus -> kompletten Ordner spielen und Fortschritt merken")); + currentTrack = settings.readFolderSettingFromFlash(myFolder->folder); + if (currentTrack == 0 || currentTrack > numTracksInFolder) { + currentTrack = 1; + } + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::hoerspiel_vb: + // Spezialmodus Von-Bin: Hörspiel: eine zufällige Datei aus dem Ordner + Serial.println(F("Spezialmodus Von-Bin: Hörspiel -> zufälligen Track wiedergeben")); + Serial.print(myFolder->special); + Serial.print(F(" bis ")); + Serial.println(myFolder->special2); + firstTrack = myFolder->special; + numTracksInFolder = myFolder->special2; + currentTrack = random(myFolder->special, numTracksInFolder + 1); + Serial.println(currentTrack); + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::album_vb: + // Spezialmodus Von-Bis: Album: alle Dateien zwischen Start und Ende spielen + Serial.println(F("Spezialmodus Von-Bis: Album: alle Dateien zwischen Start- und Enddatei spielen")); + Serial.print(myFolder->special); + Serial.print(F(" bis ")); + Serial.println(myFolder->special2); + firstTrack = myFolder->special; + numTracksInFolder = myFolder->special2; + currentTrack = myFolder->special; + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::party_vb: + // Spezialmodus Von-Bis: Party Ordner in zufälliger Reihenfolge + Serial.println(F("Spezialmodus Von-Bis: Party -> Ordner in zufälliger Reihenfolge wiedergeben")); + firstTrack = myFolder->special; + numTracksInFolder = myFolder->special2; + shuffleQueue(); + currentTrack = 1; + mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); + break; + default: + break; + } +} + +void Tonuino::playShortCut(uint8_t shortCut) { + Serial.println(F("=== playShortCut()")); + Serial.println(shortCut); + if (settings.shortCuts[shortCut].folder != 0) { + setFolder(&settings.shortCuts[shortCut]); + playFolder(); + disableStandbyTimer(); + delay(1000); + } else { + Serial.println(F("Shortcut not configured! Playing greating")); + mp3.playMp3FolderTrack(mp3Tracks::t_262_pling); + } +} + +// Leider kann das Modul selbst keine Queue abspielen, daher müssen wir selbst die Queue verwalten +void Tonuino::nextTrack() { + if (activeModifier->handleNext()) + return; + + if (not knownCard) + // Wenn eine neue Karte angelernt wird soll das Ende eines Tracks nicht + // verarbeitet werden + return; + + Serial.println(F("=== nextTrack()")); + + switch (myFolder->mode) { + case mode_t::hoerspiel : + case mode_t::hoerspiel_vb: + if (not mp3.isPlaying()) { + Serial.println(F("Hörspielmodus ist aktiv -> keinen neuen Track spielen")); + knownCard = false; + setStandbyTimer(); + //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! + } + break; + + case mode_t::album : + case mode_t::album_vb: + if (currentTrack != numTracksInFolder) { + ++currentTrack; + mp3.playFolderTrack(myFolder->folder, currentTrack); + Serial.print(F("Albummodus ist aktiv -> nächster Track: ")); + Serial.println(currentTrack); + } else { + // mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! + knownCard = false; + setStandbyTimer(); + } + break; + + case mode_t::party : + case mode_t::party_vb: + if (currentTrack != numTracksInFolder - firstTrack + 1) { + Serial.print(F("Party -> weiter in der Queue ")); + ++currentTrack; + } else { + Serial.println(F("Ende der Queue -> beginne von vorne")); + currentTrack = 1; + //// Wenn am Ende der Queue neu gemischt werden soll bitte die Zeilen wieder aktivieren + //Serial.println(F("Ende der Queue -> mische neu")); + //shuffleQueue(); + } + Serial.println(queue[currentTrack - 1]); + mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); + break; + + case mode_t::einzel: + Serial.println(F("Einzel Modus aktiv -> Strom sparen")); + //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! + knownCard = false; + setStandbyTimer(); + break; + + case mode_t::hoerbuch: + if (currentTrack != numTracksInFolder) { + ++currentTrack; + Serial.print(F("Hörbuch Modus ist aktiv -> nächster Track und Fortschritt speichern")); + Serial.println(currentTrack); + mp3.playFolderTrack(myFolder->folder, currentTrack); + // Fortschritt im EEPROM abspeichern + settings.writeFolderSettingToFlash(myFolder->folder, currentTrack); + } else { + //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! + // Fortschritt zurück setzen + settings.writeFolderSettingToFlash(myFolder->folder, 1); + knownCard = false; + setStandbyTimer(); + } + break; + default: + break; + } + delay(500); +} + +void Tonuino::previousTrack() { + Serial.println(F("=== previousTrack()")); + + switch (myFolder->mode) { + case mode_t::hoerspiel: + case mode_t::hoerspiel_vb: + Serial.println(F("Hörspielmodus ist aktiv -> Track von vorne spielen")); + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::album: + case mode_t::album_vb: + Serial.println(F("Albummodus ist aktiv -> vorheriger Track")); + if (currentTrack != firstTrack) { + --currentTrack; + } + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::party: + case mode_t::party_vb: + if (currentTrack != 1) { + Serial.print(F("Party Modus ist aktiv -> zurück in der Qeueue ")); + --currentTrack; + } else { + Serial.print(F("Anfang der Queue -> springe ans Ende ")); + currentTrack = numTracksInFolder - firstTrack + 1; + } + Serial.println(queue[currentTrack - 1]); + mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); + break; + + case mode_t::einzel: + Serial.println(F("Einzel Modus aktiv -> Track von vorne spielen")); + mp3.playFolderTrack(myFolder->folder, currentTrack); + break; + + case mode_t::hoerbuch: + Serial.println(F("Hörbuch Modus ist aktiv -> vorheriger Track und Fortschritt speichern")); + if (currentTrack != 1) { + --currentTrack; + } + mp3.playFolderTrack(myFolder->folder, currentTrack); + // Fortschritt im EEPROM abspeichern + settings.writeFolderSettingToFlash(myFolder->folder, currentTrack); + break; + default: + break; + } + delay(500); +} + +// Funktionen für den Standby Timer (z.B. über Pololu-Switch oder Mosfet) +void Tonuino::setStandbyTimer() { + Serial.println(F("=== setStandbyTimer()")); + if (settings.standbyTimer != 0) + standbyAtMillis = millis() + (settings.standbyTimer * 60 * 1000); + else + standbyAtMillis = 0; + Serial.println(standbyAtMillis); +} + +void Tonuino::disableStandbyTimer() { + Serial.println(F("=== disablestandby()")); + standbyAtMillis = 0; +} + +void Tonuino::checkStandbyAtMillis() { + if (standbyAtMillis != 0 && millis() > standbyAtMillis) { + Serial.println(F("=== power off!")); + // enter sleep state + digitalWrite(shutdownPin, HIGH); + delay(500); + + // http://discourse.voss.earth/t/intenso-s10000-powerbank-automatische-abschaltung-software-only/805 + // powerdown to 27mA (powerbank switches off after 30-60s) + chip_card.sleepCard(); + mp3.sleep(); + + set_sleep_mode(SLEEP_MODE_PWR_DOWN); + cli(); // Disable interrupts + sleep_mode(); + } +} + +uint8_t Tonuino::getCurrentTrack() const { + if (myFolder->mode == mode_t::party || myFolder->mode == mode_t::party_vb) + return (queue[currentTrack - 1]); + else + return currentTrack; +} + +void Tonuino::volumeUpButton() { + if (activeModifier->handleVolumeUp()) + return; + + Serial.println(F("=== volumeUp()")); + mp3.increaseVolume(); +} + +void Tonuino::volumeDownButton() { + if (activeModifier->handleVolumeDown()) + return; + + Serial.println(F("=== volumeDown()")); + mp3.decreaseVolume(); +} + +void Tonuino::nextButton() { + if (activeModifier->handleNextButton()) + return; + + nextTrack(); +} + +void Tonuino::previousButton() { + if (activeModifier->handlePreviousButton()) + return; + + previousTrack(); +} + +bool Tonuino::setupFolder(folderSettings * theFolder) { + // Ordner abfragen + theFolder->folder = voiceMenu(99, mp3Tracks::t_301_select_folder, mp3Tracks::t_0, true, 0, 0, true); + if (theFolder->folder == 0) return false; + + // Wiedergabemodus abfragen + theFolder->mode = static_cast(voiceMenu(9, mp3Tracks::t_310_select_mode, mp3Tracks::t_310_select_mode, false, 0, 0, true)); + if (theFolder->mode == mode_t::none) return false; + + // // Hörbuchmodus -> Fortschritt im EEPROM auf 1 setzen + // writeFolderSettingToFlash(theFolder->folder, 1); + + // Einzelmodus -> Datei abfragen + if (theFolder->mode == mode_t::einzel) + theFolder->special = voiceMenu(mp3.getFolderTrackCount(theFolder->folder), mp3Tracks::t_320_select_file, mp3Tracks::t_0, + true, theFolder->folder); + // Admin Funktionen + if (theFolder->mode == mode_t::admin) { + //theFolder->special = voiceMenu(3, 320, 320); + theFolder->folder = 0; + theFolder->mode = mode_t::admin_card; + } + // Spezialmodus Von-Bis + if (theFolder->mode == mode_t::hoerspiel_vb || theFolder->mode == mode_t::album_vb || theFolder->mode == mode_t::party_vb) { + theFolder->special = voiceMenu(mp3.getFolderTrackCount(theFolder->folder), mp3Tracks::t_321_select_first_file, mp3Tracks::t_0, + true, theFolder->folder); + theFolder->special2 = voiceMenu(mp3.getFolderTrackCount(theFolder->folder), mp3Tracks::t_322_select_last_file, mp3Tracks::t_0, + true, theFolder->folder, theFolder->special); + } + return true; +} + +void Tonuino::resetCard() { + mp3.playMp3FolderTrack(mp3Tracks::t_800_waiting_for_card); + do { + if (buttons.is_break()) { + mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); + return; + } + } while (!chip_card.newCardPresent()); + + Serial.print(F("Karte wird neu konfiguriert!")); + setupCard(); +} + +void Tonuino::setupCard() { + mp3.pause(); + Serial.println(F("=== setupCard()")); + nfcTagObject newCard; + if (setupFolder(&newCard.nfcFolderSettings)) + { + // Karte ist konfiguriert -> speichern + mp3.pause(); + do { + } while (mp3.isPlaying()); + if (chip_card.writeCard(newCard)) + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + else + mp3.playMp3FolderTrack(mp3Tracks::t_401_error); + mp3.waitForTrackToFinish(); + } +} + +bool Tonuino::specialCard(const nfcTagObject &nfcTag) { + if (nfcTag.cookie != cardCookie) + return false; + + if (nfcTag.nfcFolderSettings.folder != 0) { + if (activeModifier->handleRFID(nfcTag)) { + return true; + } + } + + else { //if (nfcTag.nfcFolderSettings.folder == 0) + //Serial.print(F("special card, mode = ")); + //Serial.println(static_cast(nfcTag.nfcFolderSettings.mode)); + if (activeModifier->getActive() == nfcTag.nfcFolderSettings.mode) { + resetActiveModifier(); + Serial.println(F("modifier removed")); + mp3.playAdvertisement(advertTracks::t_261_deactivate_mod_card, false); + return true; + } + const Modifier *oldModifier = activeModifier; + + switch (nfcTag.nfcFolderSettings.mode) { + case mode_t::none: + case mode_t::admin_card: chip_card.stopCard(); + adminMenu() ;break; + case mode_t::sleep_timer: Serial.println(F("activate sleepTimer")); + mp3.playAdvertisement(advertTracks::t_302_sleep, false); + activeModifier = &sleepTimer; + sleepTimer.start(nfcTag.nfcFolderSettings.special);break; + case mode_t::freeze_dance: Serial.println(F("activate freezeDance")); + mp3.playAdvertisement(advertTracks::t_300_freeze_into, false); + activeModifier = &freezeDance; ;break; + case mode_t::locked: Serial.println(F("activate locked")); + mp3.playAdvertisement(advertTracks::t_303_locked, false); + activeModifier = &locked ;break; + case mode_t::toddler: Serial.println(F("activate toddlerMode")); + mp3.playAdvertisement(advertTracks::t_304_buttonslocked, false); + activeModifier = &toddlerMode ;break; + case mode_t::kindergarden: Serial.println(F("activate kindergardenMode")); + mp3.playAdvertisement(advertTracks::t_305_kindergarden, false); + activeModifier = &kindergardenMode ;break; + case mode_t::repeat_single:Serial.println(F("activate repeatSingleModifier")); + mp3.playAdvertisement(advertTracks::t_260_activate_mod_card, false); + activeModifier = &repeatSingleModifier ;break; + default: break; + } + if (oldModifier != activeModifier) + activeModifier->init(); + delay(2000); + return true; + } + return false; +} + +bool Tonuino::adminMenuAllowed() { + // Admin menu has been locked - it still can be trigged via admin card + switch (settings.adminMenuLocked) { + case 1: + return false; + + // Pin check + case 2: + Settings::pin_t pin; + mp3.playMp3FolderTrack(mp3Tracks::t_991_admin_pin); + return (buttons.askCode(pin) && pin == settings.adminMenuPin); + + // Match check + case 3: + { + const uint8_t a = random(10, 20); + const uint8_t b = random(1, a); + uint8_t c; + mp3.playMp3FolderTrack(mp3Tracks::t_992_admin_calc); + mp3.waitForTrackToFinish(); + mp3.playMp3FolderTrack(a); + mp3.waitForTrackToFinish(); + + if (random(1, 3) == 2) { + // a + b + c = a + b; + mp3.playMp3FolderTrack(mp3Tracks::t_993_admin_calc); + } else { + // a - b + c = a - b; + mp3.playMp3FolderTrack(mp3Tracks::t_994_admin_calc); + } + mp3.waitForTrackToFinish(); + mp3.playMp3FolderTrack(b); + Serial.println(c); + return (voiceMenu(255, mp3Tracks::t_0, mp3Tracks::t_0, false) == c); + } + } + + // not locked + return true; +} + +void Tonuino::adminMenu() { + disableStandbyTimer(); + mp3.pause(); + Serial.println(F("=== adminMenu()")); + knownCard = false; + + const int subMenu = voiceMenu(12, mp3Tracks::t_900_admin, mp3Tracks::t_900_admin, false, false, 0, true); + + switch (subMenu) { + case 0: setStandbyTimer(); + return; + case 1: resetCard(); + chip_card.stopCard(); + break; + case 2: // Maximum Volume + settings.maxVolume = voiceMenu(30 - settings.minVolume, mp3Tracks::t_930_max_volume_intro, static_cast(settings.minVolume), false, false, settings.maxVolume - settings.minVolume) + settings.minVolume; + break; + case 3: // Minimum Volume + settings.minVolume = voiceMenu(settings.maxVolume - 1, mp3Tracks::t_931_min_volume_into, mp3Tracks::t_0, false, false, settings.minVolume); + break; + case 4: // Initial Volume + settings.initVolume = voiceMenu(settings.maxVolume - settings.minVolume + 1, mp3Tracks::t_932_init_volume_into, static_cast(settings.minVolume - 1), false, false, settings.initVolume - settings.minVolume + 1) + settings.minVolume - 1; + break; + case 5: // EQ + settings.eq = voiceMenu(6, mp3Tracks::t_920_eq_intro, mp3Tracks::t_920_eq_intro, false, false, settings.eq); + mp3.setEq(static_cast(settings.eq - 1)); + break; + case 6: // create modifier card + createModifierCard(); + break; + case 7: // shortcut + setupFolder(&settings.shortCuts[voiceMenu(4, mp3Tracks::t_940_shortcut_into, mp3Tracks::t_940_shortcut_into) - 1]); + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + break; + case 8: // standby timer + switch (voiceMenu(5, mp3Tracks::t_960_timer_intro, mp3Tracks::t_960_timer_intro)) { + case 1: settings.standbyTimer = 5; break; + case 2: settings.standbyTimer = 15; break; + case 3: settings.standbyTimer = 30; break; + case 4: settings.standbyTimer = 60; break; + case 5: settings.standbyTimer = 0; break; + } + break; + case 9: // Create Cards for Folder + createCardsForFolder(); + break; + case 10: // Invert Functions for Up/Down Buttons + if (voiceMenu(2, mp3Tracks::t_933_switch_volume_intro, mp3Tracks::t_933_switch_volume_intro, false) == 2) { + settings.invertVolumeButtons = true; + } + else { + settings.invertVolumeButtons = false; + } + break; + case 11: // reset EEPROM + settings.clearEEPROM(); + settings.resetSettings(); + mp3.playMp3FolderTrack(mp3Tracks::t_999_reset_ok); + break; + case 12: // lock admin menu + switch (voiceMenu(4, mp3Tracks::t_980_admin_lock_intro, mp3Tracks::t_980_admin_lock_intro, false)) { + case 1: settings.adminMenuLocked = 0; + break; + case 2: settings.adminMenuLocked = 1; + break; + case 3: { + Settings::pin_t pin; + mp3.playMp3FolderTrack(mp3Tracks::t_991_admin_pin); + if (buttons.askCode(pin)) + settings.adminMenuPin = pin; + else + break; + } + settings.adminMenuLocked = 2; + break; + } + break; + } + settings.writeSettingsToFlash(); + setStandbyTimer(); +} + +void Tonuino::voiceMenuNextOption( uint8_t returnValue + , mp3Tracks messageOffset + , bool preview + , int previewFromFolder) { + Serial.println(returnValue); + //mp3.pause(); + mp3.playMp3FolderTrack(messageOffset + returnValue); + if (preview) { + mp3.waitForTrackToFinish(); + if (previewFromFolder == 0) + mp3.playFolderTrack(returnValue, 1); + else + mp3.playFolderTrack(previewFromFolder, returnValue); + } +} + +uint8_t Tonuino::voiceMenu( int numberOfOptions + , mp3Tracks startMessage + , mp3Tracks messageOffset + , bool preview + , int previewFromFolder + , int defaultValue + , bool exitWithLongPress) { + uint8_t returnValue = defaultValue; + + if (startMessage != mp3Tracks::t_0) + mp3.playMp3FolderTrack(startMessage); + + Serial.print(F("=== voiceMenu() (")); + Serial.print(numberOfOptions); + Serial.println(F(" Options)")); + + do { + if (Serial.available() > 0) { + int optionSerial = Serial.parseInt(); + if (optionSerial != 0 && optionSerial <= numberOfOptions) + return optionSerial; + } + mp3.loop(); + switch(buttons.get_button()) { + case button::track: + if (exitWithLongPress) { + mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); + return defaultValue; + } + break; + + case button::pause: + if (returnValue != 0) { + Serial.print(F("=== ")); + Serial.print(returnValue); + Serial.println(F(" ===")); + return returnValue; + } + //delay(1000); + break; + + case button::next: + returnValue = min(returnValue + 10, numberOfOptions); + voiceMenuNextOption(returnValue, messageOffset, preview, previewFromFolder); + break; + + case button::volume_up: + returnValue = min(returnValue + 1, numberOfOptions); + voiceMenuNextOption(returnValue, messageOffset, preview, previewFromFolder); + break; + + case button::previous: + returnValue = max(returnValue - 10, 1); + voiceMenuNextOption(returnValue, messageOffset, preview, previewFromFolder); + break; + + case button::volume_down: + returnValue = max(returnValue - 1, 1); + voiceMenuNextOption(returnValue, messageOffset, preview, previewFromFolder); + break; + default: + break; + } + } while (true); +} + +void Tonuino::createModifierCard() { + nfcTagObject tempCard; + tempCard.cookie = cardCookie; + tempCard.version = 1; + tempCard.nfcFolderSettings.folder = 0; + tempCard.nfcFolderSettings.special = 0; + tempCard.nfcFolderSettings.special2 = 0; + tempCard.nfcFolderSettings.mode = static_cast(0x80 | voiceMenu(6, mp3Tracks::t_970_modifier_Intro, mp3Tracks::t_970_modifier_Intro, false, false, 0, true)); + + if (tempCard.nfcFolderSettings.mode != mode_t::none) { + if (tempCard.nfcFolderSettings.mode == mode_t::sleep_timer) { + switch (voiceMenu(4, mp3Tracks::t_960_timer_intro, mp3Tracks::t_960_timer_intro)) { + case 1: tempCard.nfcFolderSettings.special = 5; break; + case 2: tempCard.nfcFolderSettings.special = 15; break; + case 3: tempCard.nfcFolderSettings.special = 30; break; + case 4: tempCard.nfcFolderSettings.special = 60; break; + } + } + mp3.playMp3FolderTrack(mp3Tracks::t_800_waiting_for_card); + do { + if (buttons.is_break()) { + mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); + return; + } + } while (!chip_card.newCardPresent()); + + // RFID Karte wurde aufgelegt + Serial.println(F("schreibe Karte...")); + if (chip_card.writeCard(tempCard)) + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + else + mp3.playMp3FolderTrack(mp3Tracks::t_401_error); + mp3.waitForTrackToFinish(); + chip_card.stopCard(); + } +} + +void Tonuino::createCardsForFolder() { + // Ordner abfragen + nfcTagObject tempCard; + tempCard.cookie = cardCookie; + tempCard.version = 1; + tempCard.nfcFolderSettings.mode = mode_t::einzel; + tempCard.nfcFolderSettings.folder = voiceMenu(99, mp3Tracks::t_301_select_folder, mp3Tracks::t_0, true); + uint8_t special = voiceMenu(mp3.getFolderTrackCount(tempCard.nfcFolderSettings.folder), mp3Tracks::t_321_select_first_file, mp3Tracks::t_0, + true, tempCard.nfcFolderSettings.folder); + uint8_t special2 = voiceMenu(mp3.getFolderTrackCount(tempCard.nfcFolderSettings.folder), mp3Tracks::t_322_select_last_file, mp3Tracks::t_0, + true, tempCard.nfcFolderSettings.folder, special); + + mp3.playMp3FolderTrack(mp3Tracks::t_936_batch_cards_intro); + mp3.waitForTrackToFinish(); + for (uint8_t x = special; x <= special2; x++) { + mp3.playMp3FolderTrack(x); + tempCard.nfcFolderSettings.special = x; + Serial.print(x); + Serial.println(F(" Karte auflegen")); + do { + if (buttons.is_break()) { + mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); + return; + } + } while (!chip_card.newCardPresent()); + + // RFID Karte wurde aufgelegt + Serial.println(F("schreibe Karte...")); + if (chip_card.writeCard(tempCard)) + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + else + mp3.playMp3FolderTrack(mp3Tracks::t_401_error); + mp3.waitForTrackToFinish(); + chip_card.stopCard(); + mp3.waitForTrackToFinish(); + } +} + +void Tonuino::shuffleQueue() { + // Queue für die Zufallswiedergabe erstellen + for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1; x++) + queue[x] = x + firstTrack; + + // Rest mit 0 auffüllen + for (uint8_t x = numTracksInFolder - firstTrack + 1; x < maxTracksInFolder; x++) + queue[x] = 0; + + // Queue mischen + for (uint8_t i = 0; i < numTracksInFolder - firstTrack + 1; i++) { + const uint8_t j = random(0, numTracksInFolder - firstTrack + 1); + const uint8_t t = queue[i]; + queue[i] = queue[j]; + queue[j] = t; + } +// Serial.println(F("Queue :")); +// for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1 ; x++) +// Serial.println(queue[x]); +} + + + diff --git a/src/tonuino.hpp b/src/tonuino.hpp new file mode 100644 index 00000000..216c50bb --- /dev/null +++ b/src/tonuino.hpp @@ -0,0 +1,106 @@ +#ifndef SRC_TONUINO_HPP_ +#define SRC_TONUINO_HPP_ + +#include "settings.hpp" +#include "buttons.hpp" +#include "mp3.hpp" +#include "modifier.hpp" + +class Tonuino { +public: + Tonuino() {} + + void setup (); + void handleButtons (); + void handleChipCard(); + + void playFolder (); + void playShortCut (uint8_t shortCut); + void playCurrentTrack() { mp3.playFolderTrack(myFolder->folder, getCurrentTrack()); } + + void nextTrack(); + void previousTrack(); + + void resetActiveModifier() { activeModifier = &noneModifier; } + + void setStandbyTimer (); + + void setCard (const nfcTagObject &newCard ) { myCard = newCard; setFolder(&myCard.nfcFolderSettings); } + void setFolder(const folderSettings *newFolder) { myFolder = newFolder; } + +private: + + uint8_t getCurrentTrack() const; + + void checkStandbyAtMillis(); + void disableStandbyTimer (); + + void volumeUpButton (); + void volumeDownButton(); + void nextButton (); + void previousButton (); + + bool setupFolder(folderSettings * theFolder); + + void resetCard (); + void setupCard (); + bool specialCard(const nfcTagObject &nfcTag); + + + bool adminMenuAllowed(); + void adminMenu (); + + void voiceMenuNextOption( uint8_t returnValue + , mp3Tracks messageOffset + , bool preview + , int previewFromFolder); + uint8_t voiceMenu( int numberOfOptions + , mp3Tracks startMessage + , mp3Tracks messageOffset + , bool preview = false + , int previewFromFolder = 0 + , int defaultValue = 0 + , bool exitWithLongPress = false); + void createModifierCard (); + void createCardsForFolder(); + void shuffleQueue (); + + static const size_t maxTracksInFolder = 255; + typedef array queue_t; + + Settings settings {}; + Mp3 mp3 {settings}; + Buttons buttons {settings}; + Chip_card chip_card {}; + + + Modifier noneModifier {*this, mp3, settings}; + SleepTimer sleepTimer {*this, mp3, settings}; + FreezeDance freezeDance {*this, mp3, settings}; + Locked locked {*this, mp3, settings}; + ToddlerMode toddlerMode {*this, mp3, settings}; + KindergardenMode kindergardenMode {*this, mp3, settings}; + RepeatSingleModifier repeatSingleModifier{*this, mp3, settings}; + //FeedbackModifier feedbackModifier {*this, mp3, settings}; + + Modifier *activeModifier = &noneModifier; + + uint16_t numTracksInFolder = 0; + uint16_t currentTrack = 0; + uint16_t firstTrack = 0; + queue_t queue{}; + + unsigned long standbyAtMillis = 0; + + bool knownCard = false; + + nfcTagObject myCard; + const folderSettings *myFolder = &myCard.nfcFolderSettings; + +}; + +extern Tonuino tonuino; + + + +#endif /* SRC_TONUINO_HPP_ */ From 512e13d6f4416eb73fb3908e377b3559a9e19093 Mon Sep 17 00:00:00 2001 From: bernd Date: Sat, 30 Oct 2021 19:43:54 +0200 Subject: [PATCH 02/32] Play jingel at the end of admin menu after writing settings --- src/tonuino.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tonuino.cpp b/src/tonuino.cpp index d49809ba..b3097102 100644 --- a/src/tonuino.cpp +++ b/src/tonuino.cpp @@ -701,6 +701,7 @@ void Tonuino::adminMenu() { break; } settings.writeSettingsToFlash(); + mp3.playMp3FolderTrack(mp3Tracks::t_262_pling); setStandbyTimer(); } From 4c62aa2e6af2789bd210e42f04bb5c1ea62709fe Mon Sep 17 00:00:00 2001 From: bernd Date: Mon, 1 Nov 2021 15:44:44 +0100 Subject: [PATCH 03/32] Some improvements and bug fixes * restore old modifier codes * renaming and improvements in buttons * fix resetting EEPROM for addresses > 0xff * do not repeatSingle after admin menue --- Tonuino.ino | 3 +- src/buttons.cpp | 24 +++--- src/buttons.hpp | 12 +-- src/chip_card.cpp | 2 +- src/chip_card.hpp | 20 ++--- src/mp3.cpp | 1 + src/mp3.hpp | 1 + src/settings.cpp | 42 ++++++---- src/settings.hpp | 5 +- src/tonuino.cpp | 199 ++++++++++++++++++++++++---------------------- src/tonuino.hpp | 9 ++- 11 files changed, 169 insertions(+), 149 deletions(-) diff --git a/Tonuino.ino b/Tonuino.ino index 94f656e1..5640f67c 100644 --- a/Tonuino.ino +++ b/Tonuino.ino @@ -50,6 +50,5 @@ void setup() { void loop() { - tonuino.handleButtons(); - tonuino.handleChipCard(); + tonuino.loop(); } diff --git a/src/buttons.cpp b/src/buttons.cpp index d58e848e..df154aee 100644 --- a/src/buttons.cpp +++ b/src/buttons.cpp @@ -33,7 +33,7 @@ Buttons::Buttons(const Settings& settings) #endif } -button Buttons::get_button() { +button Buttons::getButton() { button ret = button::none; readButtons(); if (( pauseButton.pressedFor(LONG_PRESS) @@ -48,7 +48,8 @@ button Buttons::get_button() { else if (pauseButton.wasReleased()) { if (not ignorePauseButton) ret = button::pause; - ignorePauseButton = false; + else + ignorePauseButton = false; } else if (pauseButton.pressedFor(LONG_PRESS) && not ignorePauseButton) { @@ -63,10 +64,11 @@ button Buttons::get_button() { else ret = button::volume_up; } - ignoreUpButton = false; + else + ignoreUpButton = false; } - else if (upButton.pressedFor(LONG_PRESS)) { + else if (upButton.pressedFor(LONG_PRESS) && not ignoreUpButton) { #ifndef FIVEBUTTONS if (!settings.invertVolumeButtons) ret = button::volume_up; @@ -83,10 +85,11 @@ button Buttons::get_button() { else ret = button::volume_down; } - ignoreDownButton = false; + else + ignoreDownButton = false; } - else if (downButton.pressedFor(LONG_PRESS)) { + else if (downButton.pressedFor(LONG_PRESS) && not ignoreDownButton) { #ifndef FIVEBUTTONS if (!settings.invertVolumeButtons) ret = button::volume_down; @@ -118,7 +121,7 @@ button Buttons::get_button() { return ret; } -void Buttons::wait_for_no_button() { +void Buttons::waitForNoButton() { do { readButtons(); } while ( pauseButton.isPressed() @@ -129,15 +132,18 @@ void Buttons::wait_for_no_button() { || buttonFive .isPressed() #endif ); + ignorePauseButton = false; + ignoreUpButton = false; + ignoreDownButton = false; } -bool Buttons::is_reset() { +bool Buttons::isReset() { return (digitalRead(buttonPause) == LOW && digitalRead(buttonUp) == LOW && digitalRead(buttonDown) == LOW ); } -bool Buttons::is_break() { +bool Buttons::isBreak() { readButtons(); if (upButton.wasReleased() || downButton.wasReleased()) { Serial.print(F("Abgebrochen!")); diff --git a/src/buttons.hpp b/src/buttons.hpp index 32943035..98973082 100644 --- a/src/buttons.hpp +++ b/src/buttons.hpp @@ -24,10 +24,10 @@ class Buttons { public: Buttons(const Settings& settings); - button get_button(); - void wait_for_no_button(); - bool is_reset(); - bool is_break(); + button getButton(); + void waitForNoButton(); + bool isReset(); + bool isBreak(); bool askCode(Settings::pin_t &code); private: @@ -44,10 +44,6 @@ class Buttons { bool ignorePauseButton = false; bool ignoreUpButton = false; bool ignoreDownButton = false; - #ifdef FIVEBUTTONS - bool ignoreButtonFour = false; - bool ignoreButtonFive = false; - #endif const Settings& settings; }; diff --git a/src/chip_card.cpp b/src/chip_card.cpp index c6de6d0f..cc72ec58 100644 --- a/src/chip_card.cpp +++ b/src/chip_card.cpp @@ -133,7 +133,7 @@ bool Chip_card::readCard(nfcTagObject &nfcTag) { nfcTag.cookie = tempCookie; nfcTag.version = buffer[4]; nfcTag.nfcFolderSettings.folder = buffer[5]; - nfcTag.nfcFolderSettings.mode = static_cast(buffer[5] ? buffer[6] : 0x80 | buffer[6]); + nfcTag.nfcFolderSettings.mode = static_cast(buffer[6]); nfcTag.nfcFolderSettings.special = buffer[7]; nfcTag.nfcFolderSettings.special2 = buffer[8]; diff --git a/src/chip_card.hpp b/src/chip_card.hpp index badc6531..0f5411e3 100644 --- a/src/chip_card.hpp +++ b/src/chip_card.hpp @@ -20,12 +20,12 @@ enum class mode_t: uint8_t { party_vb = 9, // modifier modes - sleep_timer = 0x80 | 1, - freeze_dance = 0x80 | 2, - locked = 0x80 | 3, - toddler = 0x80 | 4, - kindergarden = 0x80 | 5, - repeat_single = 0x80 | 6, + sleep_timer = 1, + freeze_dance = 2, + locked = 3, + toddler = 4, + kindergarden = 5, + repeat_single = 6, admin_card = 0xff, }; @@ -44,17 +44,17 @@ struct nfcTagObject { folderSettings nfcFolderSettings; }; -class MFRC522; // forward declaration +class MFRC522; // forward declaration to not have to include it here class Chip_card { public: Chip_card(); - bool readCard(nfcTagObject &nfcTag); + bool readCard ( nfcTagObject &nfcTag); bool writeCard(const nfcTagObject &nfcTag); void sleepCard(); - void initCard(); - void stopCard(); + void initCard (); + void stopCard (); bool newCardPresent(); private: diff --git a/src/mp3.cpp b/src/mp3.cpp index d66b261a..c17f1379 100644 --- a/src/mp3.cpp +++ b/src/mp3.cpp @@ -10,6 +10,7 @@ const uint8_t busyPin = 4; } uint16_t Mp3Notify::lastTrackFinished = 0; + void Mp3Notify::OnError(uint16_t errorCode) { // see DfMp3_Error for code meaning Serial.println(); diff --git a/src/mp3.hpp b/src/mp3.hpp index 9233861c..da509875 100644 --- a/src/mp3.hpp +++ b/src/mp3.hpp @@ -113,6 +113,7 @@ class Mp3Notify { static void OnPlaySourceOnline (DfMp3_PlaySources source); static void OnPlaySourceInserted(DfMp3_PlaySources source); static void OnPlaySourceRemoved (DfMp3_PlaySources source); + static void ResetLastTrackFinished() { lastTrackFinished = 0; } private: static void PrintlnSourceAction (DfMp3_PlaySources source, const __FlashStringHelper* action); diff --git a/src/settings.cpp b/src/settings.cpp index b3634ed3..c9c8d110 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -8,10 +8,18 @@ const int startAddressAdminSettings = sizeof(folderSettings::folder) * 100; } +void Settings::writeByteToFlash(uint16_t address, uint8_t value) { + EEPROM.update(address, value); +} + +uint8_t Settings::readByteFromFlash(uint16_t address) { + return EEPROM.read(address); +} + void Settings::clearEEPROM() { Serial.println(F("Reset -> EEPROM wird gelöscht")); - for (unsigned int i = 0; i < EEPROM.length(); i++) { - writeFolderSettingToFlash(i, 0); + for (uint16_t i = 0; i < EEPROM.length(); i++) { + writeByteToFlash(i, 0); } } @@ -29,17 +37,17 @@ void Settings::resetSettings() { initVolume = 15; eq = 1; locked = false; - standbyTimer = 0; + standbyTimer = 0; invertVolumeButtons = true; - shortCuts[0].folder = 0; - shortCuts[1].folder = 0; - shortCuts[2].folder = 0; - shortCuts[3].folder = 0; - adminMenuLocked = 0; - adminMenuPin[0] = 1; - adminMenuPin[1] = 1; - adminMenuPin[2] = 1; - adminMenuPin[3] = 1; + shortCuts[0].folder = 0; + shortCuts[1].folder = 0; + shortCuts[2].folder = 0; + shortCuts[3].folder = 0; + adminMenuLocked = 0; + adminMenuPin[0] = 1; + adminMenuPin[1] = 1; + adminMenuPin[2] = 1; + adminMenuPin[3] = 1; writeSettingsToFlash(); } @@ -93,18 +101,18 @@ void Settings::loadSettingsFromFlash() { Serial.println(adminMenuLocked); Serial.print(F("Admin Menu Pin: ")); - Serial.print(adminMenuPin[0]); - Serial.print(adminMenuPin[1]); - Serial.print(adminMenuPin[2]); + Serial.print (adminMenuPin[0]); + Serial.print (adminMenuPin[1]); + Serial.print (adminMenuPin[2]); Serial.println(adminMenuPin[3]); } void Settings::writeFolderSettingToFlash(uint8_t folder, uint16_t track) { - EEPROM.update(folder, track); + writeByteToFlash(folder, min(track, 0xff)); } uint16_t Settings::readFolderSettingFromFlash(uint8_t folder) { - return EEPROM.read(folder); + return readByteFromFlash(folder); } diff --git a/src/settings.hpp b/src/settings.hpp index c3133e4a..617fb225 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -11,6 +11,9 @@ struct Settings { typedef array shortCuts_t; typedef array pin_t; + void writeByteToFlash (uint16_t address, uint8_t value); + uint8_t readByteFromFlash(uint16_t address); + void clearEEPROM(); void writeSettingsToFlash(); @@ -18,7 +21,7 @@ struct Settings { void migrateSettings(int oldVersion); void loadSettingsFromFlash(); - void writeFolderSettingToFlash(uint8_t folder, uint16_t track); + void writeFolderSettingToFlash (uint8_t folder, uint16_t track); uint16_t readFolderSettingFromFlash(uint8_t folder); uint32_t cookie; diff --git a/src/tonuino.cpp b/src/tonuino.cpp index b3097102..84ca9acc 100644 --- a/src/tonuino.cpp +++ b/src/tonuino.cpp @@ -35,7 +35,7 @@ void Tonuino::setup() { chip_card.initCard(); // RESET --- ALLE DREI KNÖPFE BEIM STARTEN GEDRÜCKT HALTEN -> alle EINSTELLUNGEN werden gelöscht - if (buttons.is_reset()) { + if (buttons.isReset()) { settings.clearEEPROM(); settings.loadSettingsFromFlash(); } @@ -44,7 +44,8 @@ void Tonuino::setup() { tonuino.playShortCut(3); } -void Tonuino::handleButtons() { +void Tonuino::loop() { + checkStandbyAtMillis(); mp3.loop(); @@ -52,11 +53,18 @@ void Tonuino::handleButtons() { // Modifier : WIP! activeModifier->loop(); - switch (buttons.get_button()) { + handleButtons(); + handleChipCard(); + +} + +void Tonuino::handleButtons() { + + switch (buttons.getButton()) { case button::admin: mp3.pause(); - buttons.wait_for_no_button(); + buttons.waitForNoButton(); if (not adminMenuAllowed()) { mp3.start(); break; @@ -129,20 +137,19 @@ void Tonuino::handleChipCard() { // RFID Karte wurde aufgelegt nfcTagObject tempCard; - if (chip_card.readCard(tempCard) && !specialCard(tempCard)) { + if (chip_card.readCard(tempCard) && !specialCard(tempCard) && !activeModifier->handleRFID(tempCard)) { setCard(tempCard); Serial.println(myCard.nfcFolderSettings.folder); - if (myCard.cookie == cardCookie && myCard.nfcFolderSettings.folder != 0 - && myCard.nfcFolderSettings.mode != mode_t::none) { - knownCard = false; + if (myCard.cookie == cardCookie && myCard.nfcFolderSettings.mode != mode_t::none) { + knownCard = false; // prevent nextTrack() when calling Mp3Notify::OnPlayFinished() in mp3.waitForTrackToFinish(); mp3.playMp3FolderTrack(mp3Tracks::t_262_pling); mp3.waitForTrackToFinish(); playFolder(); } // Neue Karte konfigurieren - else if (myCard.cookie != cardCookie) { + else { knownCard = false; mp3.playMp3FolderTrack(mp3Tracks::t_300_new_tag); mp3.waitForTrackToFinish(); @@ -169,14 +176,12 @@ void Tonuino::playFolder() { Serial.println(F("Hörspielmodus -> zufälligen Track wiedergeben")); currentTrack = random(1, numTracksInFolder + 1); Serial.println(currentTrack); - mp3.playFolderTrack(myFolder->folder, currentTrack); break; case mode_t::album: // Album Modus: kompletten Ordner spielen Serial.println(F("Album Modus -> kompletten Ordner wiedergeben")); currentTrack = 1; - mp3.playFolderTrack(myFolder->folder, currentTrack); break; case mode_t::party: @@ -185,14 +190,12 @@ void Tonuino::playFolder() { F("Party Modus -> Ordner in zufälliger Reihenfolge wiedergeben")); shuffleQueue(); currentTrack = 1; - mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); break; case mode_t::einzel: // Einzel Modus: eine Datei aus dem Ordner abspielen Serial.println(F("Einzel Modus -> eine Datei aus dem Odrdner abspielen")); currentTrack = myFolder->special; - mp3.playFolderTrack(myFolder->folder, currentTrack); break; case mode_t::hoerbuch: @@ -202,7 +205,6 @@ void Tonuino::playFolder() { if (currentTrack == 0 || currentTrack > numTracksInFolder) { currentTrack = 1; } - mp3.playFolderTrack(myFolder->folder, currentTrack); break; case mode_t::hoerspiel_vb: @@ -215,7 +217,6 @@ void Tonuino::playFolder() { numTracksInFolder = myFolder->special2; currentTrack = random(myFolder->special, numTracksInFolder + 1); Serial.println(currentTrack); - mp3.playFolderTrack(myFolder->folder, currentTrack); break; case mode_t::album_vb: @@ -227,7 +228,6 @@ void Tonuino::playFolder() { firstTrack = myFolder->special; numTracksInFolder = myFolder->special2; currentTrack = myFolder->special; - mp3.playFolderTrack(myFolder->folder, currentTrack); break; case mode_t::party_vb: @@ -237,11 +237,13 @@ void Tonuino::playFolder() { numTracksInFolder = myFolder->special2; shuffleQueue(); currentTrack = 1; - mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); break; default: - break; + knownCard = false; + setStandbyTimer(); + return; } + playCurrentTrack(); } void Tonuino::playShortCut(uint8_t shortCut) { @@ -250,10 +252,9 @@ void Tonuino::playShortCut(uint8_t shortCut) { if (settings.shortCuts[shortCut].folder != 0) { setFolder(&settings.shortCuts[shortCut]); playFolder(); - disableStandbyTimer(); delay(1000); } else { - Serial.println(F("Shortcut not configured! Playing greating")); + Serial.println(F("Shortcut not configured!")); mp3.playMp3FolderTrack(mp3Tracks::t_262_pling); } } @@ -462,34 +463,44 @@ void Tonuino::previousButton() { previousTrack(); } -bool Tonuino::setupFolder(folderSettings * theFolder) { +bool Tonuino::setupFolder(folderSettings& theFolder) { // Ordner abfragen - theFolder->folder = voiceMenu(99, mp3Tracks::t_301_select_folder, mp3Tracks::t_0, true, 0, 0, true); - if (theFolder->folder == 0) return false; + theFolder.folder = voiceMenu(99, mp3Tracks::t_301_select_folder, mp3Tracks::t_0, true, 0, 0, true); + if (theFolder.folder == 0) return false; // Wiedergabemodus abfragen - theFolder->mode = static_cast(voiceMenu(9, mp3Tracks::t_310_select_mode, mp3Tracks::t_310_select_mode, false, 0, 0, true)); - if (theFolder->mode == mode_t::none) return false; + theFolder.mode = static_cast(voiceMenu(9, mp3Tracks::t_310_select_mode, mp3Tracks::t_310_select_mode, false, 0, 0, true)); + if (theFolder.mode == mode_t::none) return false; + + //// Hörbuchmodus -> Fortschritt im EEPROM auf 1 setzen + //writeFolderSettingToFlash(theFolder.folder, 1); - // // Hörbuchmodus -> Fortschritt im EEPROM auf 1 setzen - // writeFolderSettingToFlash(theFolder->folder, 1); + switch (theFolder.mode) { // Einzelmodus -> Datei abfragen - if (theFolder->mode == mode_t::einzel) - theFolder->special = voiceMenu(mp3.getFolderTrackCount(theFolder->folder), mp3Tracks::t_320_select_file, mp3Tracks::t_0, - true, theFolder->folder); + case mode_t::einzel: + theFolder.special = voiceMenu(mp3.getFolderTrackCount(theFolder.folder), mp3Tracks::t_320_select_file, mp3Tracks::t_0, + true, theFolder.folder); + break; + // Admin Funktionen - if (theFolder->mode == mode_t::admin) { - //theFolder->special = voiceMenu(3, 320, 320); - theFolder->folder = 0; - theFolder->mode = mode_t::admin_card; - } + case mode_t::admin: + theFolder.folder = 0; + theFolder.mode = mode_t::admin_card; + break; + // Spezialmodus Von-Bis - if (theFolder->mode == mode_t::hoerspiel_vb || theFolder->mode == mode_t::album_vb || theFolder->mode == mode_t::party_vb) { - theFolder->special = voiceMenu(mp3.getFolderTrackCount(theFolder->folder), mp3Tracks::t_321_select_first_file, mp3Tracks::t_0, - true, theFolder->folder); - theFolder->special2 = voiceMenu(mp3.getFolderTrackCount(theFolder->folder), mp3Tracks::t_322_select_last_file, mp3Tracks::t_0, - true, theFolder->folder, theFolder->special); + case mode_t::hoerspiel_vb: + case mode_t::album_vb: + case mode_t::party_vb: + theFolder.special = voiceMenu(mp3.getFolderTrackCount(theFolder.folder), mp3Tracks::t_321_select_first_file, mp3Tracks::t_0, + true, theFolder.folder); + theFolder.special2 = voiceMenu(mp3.getFolderTrackCount(theFolder.folder), mp3Tracks::t_322_select_last_file, mp3Tracks::t_0, + true, theFolder.folder, theFolder.special); + break; + + default: + break; } return true; } @@ -497,7 +508,7 @@ bool Tonuino::setupFolder(folderSettings * theFolder) { void Tonuino::resetCard() { mp3.playMp3FolderTrack(mp3Tracks::t_800_waiting_for_card); do { - if (buttons.is_break()) { + if (buttons.isBreak()) { mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); return; } @@ -511,7 +522,7 @@ void Tonuino::setupCard() { mp3.pause(); Serial.println(F("=== setupCard()")); nfcTagObject newCard; - if (setupFolder(&newCard.nfcFolderSettings)) + if (setupFolder(newCard.nfcFolderSettings)) { // Karte ist konfiguriert -> speichern mp3.pause(); @@ -529,54 +540,48 @@ bool Tonuino::specialCard(const nfcTagObject &nfcTag) { if (nfcTag.cookie != cardCookie) return false; - if (nfcTag.nfcFolderSettings.folder != 0) { - if (activeModifier->handleRFID(nfcTag)) { - return true; - } - } + if (nfcTag.nfcFolderSettings.folder != 0) + return false; - else { //if (nfcTag.nfcFolderSettings.folder == 0) - //Serial.print(F("special card, mode = ")); - //Serial.println(static_cast(nfcTag.nfcFolderSettings.mode)); - if (activeModifier->getActive() == nfcTag.nfcFolderSettings.mode) { - resetActiveModifier(); - Serial.println(F("modifier removed")); - mp3.playAdvertisement(advertTracks::t_261_deactivate_mod_card, false); - return true; - } - const Modifier *oldModifier = activeModifier; - - switch (nfcTag.nfcFolderSettings.mode) { - case mode_t::none: - case mode_t::admin_card: chip_card.stopCard(); - adminMenu() ;break; - case mode_t::sleep_timer: Serial.println(F("activate sleepTimer")); - mp3.playAdvertisement(advertTracks::t_302_sleep, false); - activeModifier = &sleepTimer; - sleepTimer.start(nfcTag.nfcFolderSettings.special);break; - case mode_t::freeze_dance: Serial.println(F("activate freezeDance")); - mp3.playAdvertisement(advertTracks::t_300_freeze_into, false); - activeModifier = &freezeDance; ;break; - case mode_t::locked: Serial.println(F("activate locked")); - mp3.playAdvertisement(advertTracks::t_303_locked, false); - activeModifier = &locked ;break; - case mode_t::toddler: Serial.println(F("activate toddlerMode")); - mp3.playAdvertisement(advertTracks::t_304_buttonslocked, false); - activeModifier = &toddlerMode ;break; - case mode_t::kindergarden: Serial.println(F("activate kindergardenMode")); - mp3.playAdvertisement(advertTracks::t_305_kindergarden, false); - activeModifier = &kindergardenMode ;break; - case mode_t::repeat_single:Serial.println(F("activate repeatSingleModifier")); - mp3.playAdvertisement(advertTracks::t_260_activate_mod_card, false); - activeModifier = &repeatSingleModifier ;break; - default: break; - } - if (oldModifier != activeModifier) - activeModifier->init(); - delay(2000); + //Serial.print(F("special card, mode = ")); + //Serial.println(static_cast(nfcTag.nfcFolderSettings.mode)); + if (activeModifier->getActive() == nfcTag.nfcFolderSettings.mode) { + resetActiveModifier(); + Serial.println(F("modifier removed")); + mp3.playAdvertisement(advertTracks::t_261_deactivate_mod_card, false); return true; } - return false; + const Modifier *oldModifier = activeModifier; + + switch (nfcTag.nfcFolderSettings.mode) { + case mode_t::none: + case mode_t::admin_card: chip_card.stopCard(); + adminMenu() ;break; + case mode_t::sleep_timer: Serial.println(F("activate sleepTimer")); + mp3.playAdvertisement(advertTracks::t_302_sleep, false); + activeModifier = &sleepTimer; + sleepTimer.start(nfcTag.nfcFolderSettings.special) ;break; + case mode_t::freeze_dance: Serial.println(F("activate freezeDance")); + mp3.playAdvertisement(advertTracks::t_300_freeze_into, false); + activeModifier = &freezeDance; ;break; + case mode_t::locked: Serial.println(F("activate locked")); + mp3.playAdvertisement(advertTracks::t_303_locked, false); + activeModifier = &locked ;break; + case mode_t::toddler: Serial.println(F("activate toddlerMode")); + mp3.playAdvertisement(advertTracks::t_304_buttonslocked, false); + activeModifier = &toddlerMode ;break; + case mode_t::kindergarden: Serial.println(F("activate kindergardenMode")); + mp3.playAdvertisement(advertTracks::t_305_kindergarden, false); + activeModifier = &kindergardenMode ;break; + case mode_t::repeat_single:Serial.println(F("activate repeatSingleModifier")); + mp3.playAdvertisement(advertTracks::t_260_activate_mod_card, false); + activeModifier = &repeatSingleModifier ;break; + default: break; + } + if (oldModifier != activeModifier) + activeModifier->init(); + delay(2000); + return true; } bool Tonuino::adminMenuAllowed() { @@ -653,7 +658,7 @@ void Tonuino::adminMenu() { createModifierCard(); break; case 7: // shortcut - setupFolder(&settings.shortCuts[voiceMenu(4, mp3Tracks::t_940_shortcut_into, mp3Tracks::t_940_shortcut_into) - 1]); + setupFolder(settings.shortCuts[voiceMenu(4, mp3Tracks::t_940_shortcut_into, mp3Tracks::t_940_shortcut_into) - 1]); mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); break; case 8: // standby timer @@ -705,7 +710,7 @@ void Tonuino::adminMenu() { setStandbyTimer(); } -void Tonuino::voiceMenuNextOption( uint8_t returnValue +void Tonuino::voiceMenuPlayOption( uint8_t returnValue , mp3Tracks messageOffset , bool preview , int previewFromFolder) { @@ -744,7 +749,7 @@ uint8_t Tonuino::voiceMenu( int numberOfOptions return optionSerial; } mp3.loop(); - switch(buttons.get_button()) { + switch(buttons.getButton()) { case button::track: if (exitWithLongPress) { mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); @@ -764,22 +769,22 @@ uint8_t Tonuino::voiceMenu( int numberOfOptions case button::next: returnValue = min(returnValue + 10, numberOfOptions); - voiceMenuNextOption(returnValue, messageOffset, preview, previewFromFolder); + voiceMenuPlayOption(returnValue, messageOffset, preview, previewFromFolder); break; case button::volume_up: returnValue = min(returnValue + 1, numberOfOptions); - voiceMenuNextOption(returnValue, messageOffset, preview, previewFromFolder); + voiceMenuPlayOption(returnValue, messageOffset, preview, previewFromFolder); break; case button::previous: returnValue = max(returnValue - 10, 1); - voiceMenuNextOption(returnValue, messageOffset, preview, previewFromFolder); + voiceMenuPlayOption(returnValue, messageOffset, preview, previewFromFolder); break; case button::volume_down: returnValue = max(returnValue - 1, 1); - voiceMenuNextOption(returnValue, messageOffset, preview, previewFromFolder); + voiceMenuPlayOption(returnValue, messageOffset, preview, previewFromFolder); break; default: break; @@ -794,7 +799,7 @@ void Tonuino::createModifierCard() { tempCard.nfcFolderSettings.folder = 0; tempCard.nfcFolderSettings.special = 0; tempCard.nfcFolderSettings.special2 = 0; - tempCard.nfcFolderSettings.mode = static_cast(0x80 | voiceMenu(6, mp3Tracks::t_970_modifier_Intro, mp3Tracks::t_970_modifier_Intro, false, false, 0, true)); + tempCard.nfcFolderSettings.mode = static_cast(voiceMenu(6, mp3Tracks::t_970_modifier_Intro, mp3Tracks::t_970_modifier_Intro, false, false, 0, true)); if (tempCard.nfcFolderSettings.mode != mode_t::none) { if (tempCard.nfcFolderSettings.mode == mode_t::sleep_timer) { @@ -807,7 +812,7 @@ void Tonuino::createModifierCard() { } mp3.playMp3FolderTrack(mp3Tracks::t_800_waiting_for_card); do { - if (buttons.is_break()) { + if (buttons.isBreak()) { mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); return; } @@ -844,7 +849,7 @@ void Tonuino::createCardsForFolder() { Serial.print(x); Serial.println(F(" Karte auflegen")); do { - if (buttons.is_break()) { + if (buttons.isBreak()) { mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); return; } diff --git a/src/tonuino.hpp b/src/tonuino.hpp index 216c50bb..e050ccfb 100644 --- a/src/tonuino.hpp +++ b/src/tonuino.hpp @@ -11,19 +11,20 @@ class Tonuino { Tonuino() {} void setup (); + void loop (); void handleButtons (); void handleChipCard(); void playFolder (); void playShortCut (uint8_t shortCut); - void playCurrentTrack() { mp3.playFolderTrack(myFolder->folder, getCurrentTrack()); } + void playCurrentTrack() { if (knownCard) mp3.playFolderTrack(myFolder->folder, getCurrentTrack()); } void nextTrack(); void previousTrack(); void resetActiveModifier() { activeModifier = &noneModifier; } - void setStandbyTimer (); + void setStandbyTimer(); void setCard (const nfcTagObject &newCard ) { myCard = newCard; setFolder(&myCard.nfcFolderSettings); } void setFolder(const folderSettings *newFolder) { myFolder = newFolder; } @@ -40,7 +41,7 @@ class Tonuino { void nextButton (); void previousButton (); - bool setupFolder(folderSettings * theFolder); + bool setupFolder(folderSettings& theFolder); void resetCard (); void setupCard (); @@ -50,7 +51,7 @@ class Tonuino { bool adminMenuAllowed(); void adminMenu (); - void voiceMenuNextOption( uint8_t returnValue + void voiceMenuPlayOption( uint8_t returnValue , mp3Tracks messageOffset , bool preview , int previewFromFolder); From 70586dd1311eeffe8415e8f03a965acc13003540 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Wed, 10 Nov 2021 19:13:52 +0100 Subject: [PATCH 04/32] new mode Hoerbuch einzeln --- sd-card/mp3/0320_mode_audio_book_single.mp3 | Bin 0 -> 47496 bytes ...0_select_file.mp3 => 0327_select_file.mp3} | Bin ...st_file.mp3 => 0328_select_first_file.mp3} | Bin ...ast_file.mp3 => 0329_select_last_file.mp3} | Bin src/chip_card.hpp | 31 +++++++++--------- src/modifier.cpp | 2 -- src/mp3.hpp | 7 ++-- src/tonuino.cpp | 30 +++++++++++++---- 8 files changed, 43 insertions(+), 27 deletions(-) create mode 100644 sd-card/mp3/0320_mode_audio_book_single.mp3 rename sd-card/mp3/{0320_select_file.mp3 => 0327_select_file.mp3} (100%) rename sd-card/mp3/{0321_select_first_file.mp3 => 0328_select_first_file.mp3} (100%) rename sd-card/mp3/{0322_select_last_file.mp3 => 0329_select_last_file.mp3} (100%) diff --git a/sd-card/mp3/0320_mode_audio_book_single.mp3 b/sd-card/mp3/0320_mode_audio_book_single.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..e2d9b5c1e689d68c8d20fc0bc0a21cd4438e3a43 GIT binary patch literal 47496 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 literal 0 HcmV?d00001 diff --git a/sd-card/mp3/0320_select_file.mp3 b/sd-card/mp3/0327_select_file.mp3 similarity index 100% rename from sd-card/mp3/0320_select_file.mp3 rename to sd-card/mp3/0327_select_file.mp3 diff --git a/sd-card/mp3/0321_select_first_file.mp3 b/sd-card/mp3/0328_select_first_file.mp3 similarity index 100% rename from sd-card/mp3/0321_select_first_file.mp3 rename to sd-card/mp3/0328_select_first_file.mp3 diff --git a/sd-card/mp3/0322_select_last_file.mp3 b/sd-card/mp3/0329_select_last_file.mp3 similarity index 100% rename from sd-card/mp3/0322_select_last_file.mp3 rename to sd-card/mp3/0329_select_last_file.mp3 diff --git a/src/chip_card.hpp b/src/chip_card.hpp index 0f5411e3..f0213098 100644 --- a/src/chip_card.hpp +++ b/src/chip_card.hpp @@ -6,20 +6,21 @@ const uint32_t cardCookie = 322417479; enum class mode_t: uint8_t { - none = 0, - - // folder modes - hoerspiel = 1, - album = 2, - party = 3, - einzel = 4, - hoerbuch = 5, - admin = 6, - hoerspiel_vb = 7, - album_vb = 8, - party_vb = 9, - - // modifier modes + none = 0, + + // folder modes + hoerspiel = 1, + album = 2, + party = 3, + einzel = 4, + hoerbuch = 5, + admin = 6, + hoerspiel_vb = 7, + album_vb = 8, + party_vb = 9, + hoerbuch_1 = 10, + + // modifier modes sleep_timer = 1, freeze_dance = 2, locked = 3, @@ -27,7 +28,7 @@ enum class mode_t: uint8_t { kindergarden = 5, repeat_single = 6, - admin_card = 0xff, + admin_card = 0xff, }; struct folderSettings { diff --git a/src/modifier.cpp b/src/modifier.cpp index 732439dc..c7bd55ee 100644 --- a/src/modifier.cpp +++ b/src/modifier.cpp @@ -40,8 +40,6 @@ void FreezeDance::setNextStopAtMillis() { bool KindergardenMode::handleNext() { Serial.println(F("== KindergardenMode::handleNext() -> NEXT")); - //if (nextCard.cookie == cardCookie && nextCard.nfcFolderSettings.folder != 0 && nextCard.nfcFolderSettings.mode != 0) { - //myFolder = &nextCard.nfcFolderSettings; if (cardQueued) { cardQueued = false; diff --git a/src/mp3.hpp b/src/mp3.hpp index da509875..fe11da1f 100644 --- a/src/mp3.hpp +++ b/src/mp3.hpp @@ -22,9 +22,10 @@ enum class mp3Tracks: uint16_t { t_317_special_random = 317, t_318_special_album = 318, t_319_special_party = 319, - t_320_select_file = 320, - t_321_select_first_file = 321, - t_322_select_last_file = 322, + t_320_mode_audio_book_single = 320, + t_327_select_file = 327, + t_328_select_first_file = 328, + t_329_select_last_file = 329, t_330_select_say_number = 330, t_331_do_not_say_number = 331, t_332_say_number = 332, diff --git a/src/tonuino.cpp b/src/tonuino.cpp index 84ca9acc..50345f15 100644 --- a/src/tonuino.cpp +++ b/src/tonuino.cpp @@ -199,7 +199,8 @@ void Tonuino::playFolder() { break; case mode_t::hoerbuch: - // Hörbuch Modus: kompletten Ordner spielen und Fortschritt merken + case mode_t::hoerbuch_1: + // Hörbuch Modus: kompletten Ordner spielen und Fortschritt merken (oder nur eine Datei) Serial.println(F("Hörbuch Modus -> kompletten Ordner spielen und Fortschritt merken")); currentTrack = settings.readFolderSettingFromFlash(myFolder->folder); if (currentTrack == 0 || currentTrack > numTracksInFolder) { @@ -335,6 +336,20 @@ void Tonuino::nextTrack() { setStandbyTimer(); } break; + case mode_t::hoerbuch_1: + if (currentTrack != numTracksInFolder) + ++currentTrack; + else + currentTrack = 1; + + Serial.print(F("Hörbuch Modus single ist aktiv -> Fortschritt speichern und beenden")); + Serial.println(currentTrack); + // Fortschritt im EEPROM abspeichern + settings.writeFolderSettingToFlash(myFolder->folder, currentTrack); + //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! + knownCard = false; + setStandbyTimer(); + break; default: break; } @@ -379,6 +394,7 @@ void Tonuino::previousTrack() { break; case mode_t::hoerbuch: + case mode_t::hoerbuch_1: Serial.println(F("Hörbuch Modus ist aktiv -> vorheriger Track und Fortschritt speichern")); if (currentTrack != 1) { --currentTrack; @@ -469,7 +485,7 @@ bool Tonuino::setupFolder(folderSettings& theFolder) { if (theFolder.folder == 0) return false; // Wiedergabemodus abfragen - theFolder.mode = static_cast(voiceMenu(9, mp3Tracks::t_310_select_mode, mp3Tracks::t_310_select_mode, false, 0, 0, true)); + theFolder.mode = static_cast(voiceMenu(10, mp3Tracks::t_310_select_mode, mp3Tracks::t_310_select_mode, false, 0, 0, true)); if (theFolder.mode == mode_t::none) return false; //// Hörbuchmodus -> Fortschritt im EEPROM auf 1 setzen @@ -479,7 +495,7 @@ bool Tonuino::setupFolder(folderSettings& theFolder) { // Einzelmodus -> Datei abfragen case mode_t::einzel: - theFolder.special = voiceMenu(mp3.getFolderTrackCount(theFolder.folder), mp3Tracks::t_320_select_file, mp3Tracks::t_0, + theFolder.special = voiceMenu(mp3.getFolderTrackCount(theFolder.folder), mp3Tracks::t_327_select_file, mp3Tracks::t_0, true, theFolder.folder); break; @@ -493,9 +509,9 @@ bool Tonuino::setupFolder(folderSettings& theFolder) { case mode_t::hoerspiel_vb: case mode_t::album_vb: case mode_t::party_vb: - theFolder.special = voiceMenu(mp3.getFolderTrackCount(theFolder.folder), mp3Tracks::t_321_select_first_file, mp3Tracks::t_0, + theFolder.special = voiceMenu(mp3.getFolderTrackCount(theFolder.folder), mp3Tracks::t_328_select_first_file, mp3Tracks::t_0, true, theFolder.folder); - theFolder.special2 = voiceMenu(mp3.getFolderTrackCount(theFolder.folder), mp3Tracks::t_322_select_last_file, mp3Tracks::t_0, + theFolder.special2 = voiceMenu(mp3.getFolderTrackCount(theFolder.folder), mp3Tracks::t_329_select_last_file, mp3Tracks::t_0, true, theFolder.folder, theFolder.special); break; @@ -836,9 +852,9 @@ void Tonuino::createCardsForFolder() { tempCard.version = 1; tempCard.nfcFolderSettings.mode = mode_t::einzel; tempCard.nfcFolderSettings.folder = voiceMenu(99, mp3Tracks::t_301_select_folder, mp3Tracks::t_0, true); - uint8_t special = voiceMenu(mp3.getFolderTrackCount(tempCard.nfcFolderSettings.folder), mp3Tracks::t_321_select_first_file, mp3Tracks::t_0, + uint8_t special = voiceMenu(mp3.getFolderTrackCount(tempCard.nfcFolderSettings.folder), mp3Tracks::t_328_select_first_file, mp3Tracks::t_0, true, tempCard.nfcFolderSettings.folder); - uint8_t special2 = voiceMenu(mp3.getFolderTrackCount(tempCard.nfcFolderSettings.folder), mp3Tracks::t_322_select_last_file, mp3Tracks::t_0, + uint8_t special2 = voiceMenu(mp3.getFolderTrackCount(tempCard.nfcFolderSettings.folder), mp3Tracks::t_329_select_last_file, mp3Tracks::t_0, true, tempCard.nfcFolderSettings.folder, special); mp3.playMp3FolderTrack(mp3Tracks::t_936_batch_cards_intro); From f6950cda17108d0c7bdf27502a82767da182c85e Mon Sep 17 00:00:00 2001 From: boerge1 Date: Mon, 15 Nov 2021 17:20:53 +0100 Subject: [PATCH 05/32] New feature: pause on card removed --- src/chip_card.cpp | 27 +++++++++++++++++++++++---- src/chip_card.hpp | 18 ++++++++++++++++++ src/tonuino.cpp | 35 +++++++++++++++++++++++++++++++---- src/tonuino.hpp | 1 + 4 files changed, 73 insertions(+), 8 deletions(-) diff --git a/src/chip_card.cpp b/src/chip_card.cpp index cc72ec58..7cb4f355 100644 --- a/src/chip_card.cpp +++ b/src/chip_card.cpp @@ -20,10 +20,13 @@ const byte SS_PIN = 10; // Configurable, see typical pin layout above MFRC522::MIFARE_Key key{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; const byte sector = 1; const byte trailerBlock = 7; + +const unsigned int removeDelay = 3; } // namespace Chip_card::Chip_card() : mfrc522(*(new MFRC522(SS_PIN, RST_PIN))) +, cardRemovedSwitch(removeDelay) {} bool Chip_card::readCard(nfcTagObject &nfcTag) { @@ -234,11 +237,27 @@ void Chip_card::initCard() { mfrc522.PCD_DumpVersionToSerial(); // Show details of PCD - MFRC522 Card Reader } +void Chip_card::stopCard() { + mfrc522.PICC_HaltA(); +} + +void Chip_card::stopCrypto1() { + mfrc522.PCD_StopCrypto1(); +} + bool Chip_card::newCardPresent() { - return mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial(); + byte bufferATQA[2]; + byte bufferSize = sizeof(bufferATQA); + MFRC522::StatusCode result = mfrc522.PICC_RequestA(bufferATQA, &bufferSize); + + if(result != mfrc522.STATUS_OK) + ++cardRemovedSwitch; + else + cardRemovedSwitch.reset(); + + return result == mfrc522.STATUS_OK && mfrc522.PICC_ReadCardSerial(); } -void Chip_card::stopCard() { - mfrc522.PICC_HaltA(); - mfrc522.PCD_StopCrypto1(); +bool Chip_card::cardRemoved() { + return cardRemovedSwitch.on(); } diff --git a/src/chip_card.hpp b/src/chip_card.hpp index 0f5411e3..dc29e3fd 100644 --- a/src/chip_card.hpp +++ b/src/chip_card.hpp @@ -46,6 +46,20 @@ struct nfcTagObject { class MFRC522; // forward declaration to not have to include it here +class delayedSwitchOn { +public: + delayedSwitchOn(unsigned int delay) + : delay(delay) + {} + delayedSwitchOn& operator++() { if (counter < delay) ++counter; return *this; } + void reset() { counter = 0; } + bool on() { return counter == delay; } + +private: + const unsigned int delay; + unsigned int counter = 0; +}; + class Chip_card { public: Chip_card(); @@ -55,10 +69,14 @@ class Chip_card { void sleepCard(); void initCard (); void stopCard (); + void stopCrypto1(); bool newCardPresent(); + bool cardRemoved(); private: MFRC522 &mfrc522; + + delayedSwitchOn cardRemovedSwitch; }; diff --git a/src/tonuino.cpp b/src/tonuino.cpp index 84ca9acc..365acfbe 100644 --- a/src/tonuino.cpp +++ b/src/tonuino.cpp @@ -10,7 +10,8 @@ Tonuino tonuino; namespace { -const uint8_t shutdownPin = 7; +const uint8_t shutdownPin = 7; +const bool pauseOnRemoveCard = false; // Set to true, if you want this feature } @@ -55,7 +56,6 @@ void Tonuino::loop() { handleButtons(); handleChipCard(); - } void Tonuino::handleButtons() { @@ -132,7 +132,34 @@ void Tonuino::handleButtons() { } void Tonuino::handleChipCard() { - if (!chip_card.newCardPresent()) + + const bool newCardPresent = chip_card.newCardPresent(); + + if (chip_card.cardRemoved()) { + if (not cardRemoved) { + Serial.println(F("Card Removed")); + cardRemoved = true; + chip_card.stopCard(); + if (pauseOnRemoveCard) { + if (not activeModifier->handlePause() && mp3.isPlaying()) { + mp3.pause(); + setStandbyTimer(); + } + } + } + return; + } + else { + if (cardRemoved) { + Serial.println(F("Card in")); + cardRemoved = false; + } + else { + return; + } + } + + if (!newCardPresent) return; // RFID Karte wurde aufgelegt @@ -156,7 +183,7 @@ void Tonuino::handleChipCard() { setupCard(); } } - chip_card.stopCard(); + chip_card.stopCrypto1(); } void Tonuino::playFolder() { diff --git a/src/tonuino.hpp b/src/tonuino.hpp index e050ccfb..d5b39145 100644 --- a/src/tonuino.hpp +++ b/src/tonuino.hpp @@ -94,6 +94,7 @@ class Tonuino { unsigned long standbyAtMillis = 0; bool knownCard = false; + bool cardRemoved = true; nfcTagObject myCard; const folderSettings *myFolder = &myCard.nfcFolderSettings; From 0e188f452493b4fa21a2a4bb9749cf178f89a2f3 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Mon, 15 Nov 2021 19:29:51 +0100 Subject: [PATCH 06/32] New feature: pause on card removed * if the same card is used after removing: the track is continued --- src/chip_card.hpp | 11 +++++++++++ src/tonuino.cpp | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/chip_card.hpp b/src/chip_card.hpp index dc29e3fd..951065fb 100644 --- a/src/chip_card.hpp +++ b/src/chip_card.hpp @@ -35,6 +35,12 @@ struct folderSettings { mode_t mode; uint8_t special; uint8_t special2; + bool operator==(const folderSettings& rhs) { + return folder == rhs.folder && + mode == rhs.mode && + special == rhs.special && + special2 == rhs.special2; + } }; // this object stores nfc tag data @@ -42,6 +48,11 @@ struct nfcTagObject { uint32_t cookie; uint8_t version; folderSettings nfcFolderSettings; + bool operator==(const nfcTagObject& rhs) { + return cookie == rhs.cookie && + version == rhs.version && + nfcFolderSettings == rhs.nfcFolderSettings; + } }; class MFRC522; // forward declaration to not have to include it here diff --git a/src/tonuino.cpp b/src/tonuino.cpp index 365acfbe..dc3d7fe5 100644 --- a/src/tonuino.cpp +++ b/src/tonuino.cpp @@ -165,6 +165,16 @@ void Tonuino::handleChipCard() { // RFID Karte wurde aufgelegt nfcTagObject tempCard; if (chip_card.readCard(tempCard) && !specialCard(tempCard) && !activeModifier->handleRFID(tempCard)) { + + if (pauseOnRemoveCard && knownCard && myCard == tempCard) { + if (not mp3.isPlaying()) { + mp3.start(); + disableStandbyTimer(); + } + chip_card.stopCrypto1(); + return; + } + setCard(tempCard); Serial.println(myCard.nfcFolderSettings.folder); From 233c34d2017938f95ee198459fcb7513b6040db2 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Tue, 16 Nov 2021 16:28:51 +0100 Subject: [PATCH 07/32] New feature: pause on card removed * fix bug: no pause if card is removed before track was started * add settings (EEPROM) for this feature --- sd-card/mp3/0913_pause_on_card_removed.mp3 | Bin 0 -> 12584 bytes src/mp3.cpp | 16 +++++++++++----- src/mp3.hpp | 2 ++ src/settings.cpp | 5 +++++ src/settings.hpp | 1 + src/tonuino.cpp | 17 +++++++++++++---- 6 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 sd-card/mp3/0913_pause_on_card_removed.mp3 diff --git a/sd-card/mp3/0913_pause_on_card_removed.mp3 b/sd-card/mp3/0913_pause_on_card_removed.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..a089242dec70de912735b4ea7715bc9f757506e7 GIT binary patch literal 12584 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 T4$)SYRFUCFJhY(w|2_T(nS7W0 literal 0 HcmV?d00001 diff --git a/src/mp3.cpp b/src/mp3.cpp index c17f1379..18765479 100644 --- a/src/mp3.cpp +++ b/src/mp3.cpp @@ -51,13 +51,9 @@ bool Mp3::isPlaying() const { } void Mp3::waitForTrackToFinish() { - unsigned long currentTime = millis(); - const unsigned long maxStartTime = 1000; // wait until track is started - do { - loop(); - } while (!isPlaying() && millis() < currentTime + maxStartTime); + waitForTrackToStart(); delay(1000); // wait until track is finished @@ -66,6 +62,16 @@ void Mp3::waitForTrackToFinish() { } while (isPlaying()); } +void Mp3::waitForTrackToStart() { + unsigned long currentTime = millis(); + const unsigned long maxStartTime = 1000; + + // wait until track is started + do { + loop(); + } while (!isPlaying() && millis() < currentTime + maxStartTime); +} + void Mp3::playMp3FolderTrack(uint16_t track) { DFMiniMp3::playMp3FolderTrack(track); } diff --git a/src/mp3.hpp b/src/mp3.hpp index da509875..d35051e8 100644 --- a/src/mp3.hpp +++ b/src/mp3.hpp @@ -45,6 +45,7 @@ enum class mp3Tracks: uint16_t { t_910_switch_volume = 910, t_911_reset = 911, t_912_admin_lock = 912, + t_913_pause_on_card_removed = 913, t_920_eq_intro = 920, t_921_normal = 921, t_922_pop = 922, @@ -126,6 +127,7 @@ class Mp3: public DFMiniMp3 { bool isPlaying() const; void waitForTrackToFinish(); + void waitForTrackToStart(); void playMp3FolderTrack(uint16_t track); void playMp3FolderTrack(mp3Tracks track); void playAdvertisement(uint16_t track, bool olnyIfIsPlaying = true); diff --git a/src/settings.cpp b/src/settings.cpp index c9c8d110..d27b4bd5 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -48,6 +48,7 @@ void Settings::resetSettings() { adminMenuPin[1] = 1; adminMenuPin[2] = 1; adminMenuPin[3] = 1; + pauseWhenCardRemoved= false; writeSettingsToFlash(); } @@ -62,6 +63,7 @@ void Settings::migrateSettings(int oldVersion) { adminMenuPin[1] = 1; adminMenuPin[2] = 1; adminMenuPin[3] = 1; + pauseWhenCardRemoved = false; writeSettingsToFlash(); } } @@ -105,6 +107,9 @@ void Settings::loadSettingsFromFlash() { Serial.print (adminMenuPin[1]); Serial.print (adminMenuPin[2]); Serial.println(adminMenuPin[3]); + + Serial.print(F("Pause when card removed: ")); + Serial.println(pauseWhenCardRemoved); } void Settings::writeFolderSettingToFlash(uint8_t folder, uint16_t track) { diff --git a/src/settings.hpp b/src/settings.hpp index 617fb225..46fdd475 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -36,6 +36,7 @@ struct Settings { shortCuts_t shortCuts; uint8_t adminMenuLocked; pin_t adminMenuPin; + bool pauseWhenCardRemoved; }; #endif /* SRC_SETTINGS_HPP_ */ diff --git a/src/tonuino.cpp b/src/tonuino.cpp index dc3d7fe5..a6e87e10 100644 --- a/src/tonuino.cpp +++ b/src/tonuino.cpp @@ -11,7 +11,6 @@ Tonuino tonuino; namespace { const uint8_t shutdownPin = 7; -const bool pauseOnRemoveCard = false; // Set to true, if you want this feature } @@ -140,7 +139,7 @@ void Tonuino::handleChipCard() { Serial.println(F("Card Removed")); cardRemoved = true; chip_card.stopCard(); - if (pauseOnRemoveCard) { + if (settings.pauseWhenCardRemoved) { if (not activeModifier->handlePause() && mp3.isPlaying()) { mp3.pause(); setStandbyTimer(); @@ -166,7 +165,7 @@ void Tonuino::handleChipCard() { nfcTagObject tempCard; if (chip_card.readCard(tempCard) && !specialCard(tempCard) && !activeModifier->handleRFID(tempCard)) { - if (pauseOnRemoveCard && knownCard && myCard == tempCard) { + if (settings.pauseWhenCardRemoved && knownCard && myCard == tempCard) { if (not mp3.isPlaying()) { mp3.start(); disableStandbyTimer(); @@ -281,6 +280,8 @@ void Tonuino::playFolder() { return; } playCurrentTrack(); + if (knownCard && settings.pauseWhenCardRemoved) + mp3.waitForTrackToStart(); } void Tonuino::playShortCut(uint8_t shortCut) { @@ -670,7 +671,7 @@ void Tonuino::adminMenu() { Serial.println(F("=== adminMenu()")); knownCard = false; - const int subMenu = voiceMenu(12, mp3Tracks::t_900_admin, mp3Tracks::t_900_admin, false, false, 0, true); + const int subMenu = voiceMenu(13, mp3Tracks::t_900_admin, mp3Tracks::t_900_admin, false, false, 0, true); switch (subMenu) { case 0: setStandbyTimer(); @@ -741,6 +742,14 @@ void Tonuino::adminMenu() { break; } break; + case 13: // Pause, wenn Karte entfernt wird + if (voiceMenu(2, mp3Tracks::t_913_pause_on_card_removed, mp3Tracks::t_933_switch_volume_intro, false) == 2) { + settings.pauseWhenCardRemoved = true; + } + else { + settings.pauseWhenCardRemoved = false; + } + break; } settings.writeSettingsToFlash(); mp3.playMp3FolderTrack(mp3Tracks::t_262_pling); From 58cdd54ec2c81fd09dfca62e909510d0825564d8 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Thu, 9 Dec 2021 19:34:50 +0100 Subject: [PATCH 08/32] fix writing card * fix bug: writing card not possible (introduced by pause if card removed feature) --- src/tonuino.cpp | 81 ++++++++++++++++++++++--------------------------- src/tonuino.hpp | 7 +++-- 2 files changed, 41 insertions(+), 47 deletions(-) diff --git a/src/tonuino.cpp b/src/tonuino.cpp index b9c9c1cd..7107d4a6 100644 --- a/src/tonuino.cpp +++ b/src/tonuino.cpp @@ -130,7 +130,7 @@ void Tonuino::handleButtons() { } } -void Tonuino::handleChipCard() { +void Tonuino::handleChipCard(bool no_action) { const bool newCardPresent = chip_card.newCardPresent(); @@ -158,7 +158,7 @@ void Tonuino::handleChipCard() { } } - if (!newCardPresent) + if (no_action || !newCardPresent) return; // RFID Karte wurde aufgelegt @@ -195,6 +195,34 @@ void Tonuino::handleChipCard() { chip_card.stopCrypto1(); } +void Tonuino::waitForCardRemoved() { + chip_card.stopCrypto1(); + while (!cardRemoved) { + handleChipCard(true); + } +} + +void Tonuino::writeCard(const nfcTagObject &nfcTag) { + + mp3.playMp3FolderTrack(mp3Tracks::t_800_waiting_for_card); + do { + if (buttons.isBreak()) { + mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); + return; + } + handleChipCard(true); + } while (cardRemoved); + + Serial.println(F("schreibe Karte...")); + if (chip_card.writeCard(nfcTag)) + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + else + mp3.playMp3FolderTrack(mp3Tracks::t_401_error); + mp3.waitForTrackToFinish(); + + waitForCardRemoved(); +} + void Tonuino::playFolder() { Serial.println(F("== playFolder()")); disableStandbyTimer(); @@ -560,14 +588,6 @@ bool Tonuino::setupFolder(folderSettings& theFolder) { } void Tonuino::resetCard() { - mp3.playMp3FolderTrack(mp3Tracks::t_800_waiting_for_card); - do { - if (buttons.isBreak()) { - mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); - return; - } - } while (!chip_card.newCardPresent()); - Serial.print(F("Karte wird neu konfiguriert!")); setupCard(); } @@ -582,11 +602,8 @@ void Tonuino::setupCard() { mp3.pause(); do { } while (mp3.isPlaying()); - if (chip_card.writeCard(newCard)) - mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); - else - mp3.playMp3FolderTrack(mp3Tracks::t_401_error); - mp3.waitForTrackToFinish(); + + writeCard(newCard); } } @@ -682,6 +699,8 @@ bool Tonuino::adminMenuAllowed() { } void Tonuino::adminMenu() { + waitForCardRemoved(); + disableStandbyTimer(); mp3.pause(); Serial.println(F("=== adminMenu()")); @@ -872,22 +891,8 @@ void Tonuino::createModifierCard() { case 4: tempCard.nfcFolderSettings.special = 60; break; } } - mp3.playMp3FolderTrack(mp3Tracks::t_800_waiting_for_card); - do { - if (buttons.isBreak()) { - mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); - return; - } - } while (!chip_card.newCardPresent()); - // RFID Karte wurde aufgelegt - Serial.println(F("schreibe Karte...")); - if (chip_card.writeCard(tempCard)) - mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); - else - mp3.playMp3FolderTrack(mp3Tracks::t_401_error); - mp3.waitForTrackToFinish(); - chip_card.stopCard(); + writeCard(tempCard); } } @@ -910,22 +915,8 @@ void Tonuino::createCardsForFolder() { tempCard.nfcFolderSettings.special = x; Serial.print(x); Serial.println(F(" Karte auflegen")); - do { - if (buttons.isBreak()) { - mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); - return; - } - } while (!chip_card.newCardPresent()); - // RFID Karte wurde aufgelegt - Serial.println(F("schreibe Karte...")); - if (chip_card.writeCard(tempCard)) - mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); - else - mp3.playMp3FolderTrack(mp3Tracks::t_401_error); - mp3.waitForTrackToFinish(); - chip_card.stopCard(); - mp3.waitForTrackToFinish(); + writeCard(tempCard); } } diff --git a/src/tonuino.hpp b/src/tonuino.hpp index d5b39145..02697796 100644 --- a/src/tonuino.hpp +++ b/src/tonuino.hpp @@ -12,8 +12,6 @@ class Tonuino { void setup (); void loop (); - void handleButtons (); - void handleChipCard(); void playFolder (); void playShortCut (uint8_t shortCut); @@ -33,6 +31,11 @@ class Tonuino { uint8_t getCurrentTrack() const; + void handleButtons (); + void handleChipCard(bool no_action = false); + void waitForCardRemoved(); + void writeCard(const nfcTagObject &nfcTag); + void checkStandbyAtMillis(); void disableStandbyTimer (); From 64cb6547864c5b3567b9a2924836829483ba76a3 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Sat, 18 Dec 2021 17:20:08 +0100 Subject: [PATCH 09/32] Improvements * put all constants into new file constants.hpp * make activeHigh/Low for pins configurable * refactor logging (with severity) * do increase/decrease volume by setvolume() * fix bug: no delete of modifier * fix bug: no play after pause - up/down - pause --- Tonuino.ino | 23 +++---- src/buttons.cpp | 97 +++++++++++++------------- src/buttons.hpp | 10 ++- src/chip_card.cpp | 59 +++++++--------- src/chip_card.hpp | 2 - src/constants.hpp | 59 ++++++++++++++++ src/log.cpp | 15 ++++ src/log.hpp | 76 ++++++++++++++++++++ src/logger.hpp | 20 ++++++ src/modifier.cpp | 27 ++++---- src/modifier.hpp | 30 ++++---- src/mp3.cpp | 39 ++++------- src/settings.cpp | 60 +++++----------- src/tonuino.cpp | 172 ++++++++++++++++++++++------------------------ 14 files changed, 396 insertions(+), 293 deletions(-) create mode 100644 src/constants.hpp create mode 100644 src/log.cpp create mode 100644 src/log.hpp create mode 100644 src/logger.hpp diff --git a/Tonuino.ino b/Tonuino.ino index 5640f67c..ff321228 100644 --- a/Tonuino.ino +++ b/Tonuino.ino @@ -3,6 +3,8 @@ #include "src/settings.hpp" #include "src/mp3.hpp" #include "src/buttons.hpp" +#include "src/logger.hpp" +#include "src/constants.hpp" /* @@ -16,13 +18,6 @@ Information and contribution at https://tonuino.de. */ -namespace { - -const uint8_t openAnalogPin = A7; - -} - - void setup() { Serial.begin(115200); // Es gibt ein paar Debug Ausgaben über die serielle Schnittstelle @@ -37,13 +32,13 @@ void setup() { randomSeed(ADCSeed); // Zufallsgenerator initialisieren // Dieser Hinweis darf nicht entfernt werden - Serial.println(F("\n _____ _____ _____ _____ _____")); - Serial.println(F("|_ _|___ ___| | | | | | |")); - Serial.println(F(" | | | . | | | |- -| | | | | |")); - Serial.println(F(" |_| |___|_|_|_____|_____|_|___|_____|\n")); - Serial.println(F("TonUINO Version 2.1")); - Serial.println(F("created by Thorsten Voß and licensed under GNU/GPL.")); - Serial.println(F("Information and contribution at https://tonuino.de.\n")); + LOG(init_log, s_debug, F(" _____ _____ _____ _____ _____")); + LOG(init_log, s_debug, F("|_ _|___ ___| | | | | | |")); + LOG(init_log, s_debug, F(" | | | . | | | |- -| | | | | |")); + LOG(init_log, s_debug, F(" |_| |___|_|_|_____|_____|_|___|_____|\n")); + LOG(init_log, s_debug, F("TonUINO Version 2.1")); + LOG(init_log, s_debug, F("created by Thorsten Voß and licensed under GNU/GPL.")); + LOG(init_log, s_debug, F("Information and contribution at https://tonuino.de.\n")); tonuino.setup(); } diff --git a/src/buttons.cpp b/src/buttons.cpp index df154aee..59f1016a 100644 --- a/src/buttons.cpp +++ b/src/buttons.cpp @@ -1,63 +1,57 @@ #include "buttons.hpp" -namespace { - -const uint32_t LONG_PRESS = 1000; -const uint8_t buttonPause = A0; -const uint8_t buttonUp = A1; -const uint8_t buttonDown = A2; - -#ifdef FIVEBUTTONS -const uint8_t buttonFourPin = A3; -const uint8_t buttonFivePin = A4; -#endif +#include "constants.hpp" +#include "logger.hpp" +namespace { +const bool buttonPinIsActiveLow = (buttonPinType == levelType::activeLow); } Buttons::Buttons(const Settings& settings) -: pauseButton(buttonPause ) -, upButton(buttonUp ) -, downButton(buttonDown ) +// pin dbTime puEnable invert +: buttonPause(buttonPausePin, buttonDbTime, buttonPinIsActiveLow, buttonPinIsActiveLow) +, buttonUp (buttonUpPin , buttonDbTime, buttonPinIsActiveLow, buttonPinIsActiveLow) +, buttonDown (buttonDownPin , buttonDbTime, buttonPinIsActiveLow, buttonPinIsActiveLow) #ifdef FIVEBUTTONS -, buttonFour (buttonFourPin); -, buttonFive (buttonFivePin); +, buttonFour (buttonFourPin , buttonDbTime, buttonPinIsActiveLow, buttonPinIsActiveLow) +, buttonFive (buttonFivePin , buttonDbTime, buttonPinIsActiveLow, buttonPinIsActiveLow) #endif , settings(settings) { - pinMode(buttonPause , INPUT_PULLUP); - pinMode(buttonUp , INPUT_PULLUP); - pinMode(buttonDown , INPUT_PULLUP); + buttonPause.begin(); + buttonUp .begin(); + buttonDown .begin(); #ifdef FIVEBUTTONS - pinMode(buttonFourPin, INPUT_PULLUP); - pinMode(buttonFivePin, INPUT_PULLUP); + buttonFour .begin(); + buttonFive .begin(); #endif } button Buttons::getButton() { button ret = button::none; readButtons(); - if (( pauseButton.pressedFor(LONG_PRESS) - || upButton .pressedFor(LONG_PRESS) - || downButton .pressedFor(LONG_PRESS) + if (( buttonPause.pressedFor(buttonLongPress) + || buttonUp .pressedFor(buttonLongPress) + || buttonDown .pressedFor(buttonLongPress) ) - && pauseButton.isPressed() - && upButton .isPressed() - && downButton .isPressed()) + && buttonPause.isPressed() + && buttonUp .isPressed() + && buttonDown .isPressed()) ret = button::admin; - else if (pauseButton.wasReleased()) { + else if (buttonPause.wasReleased()) { if (not ignorePauseButton) ret = button::pause; else ignorePauseButton = false; } - else if (pauseButton.pressedFor(LONG_PRESS) && not ignorePauseButton) { + else if (buttonPause.pressedFor(buttonLongPress) && not ignorePauseButton) { ret = button::track; ignorePauseButton = true; } - else if (upButton.wasReleased()) { + else if (buttonUp.wasReleased()) { if (!ignoreUpButton) { if (!settings.invertVolumeButtons) ret = button::next; @@ -68,7 +62,7 @@ button Buttons::getButton() { ignoreUpButton = false; } - else if (upButton.pressedFor(LONG_PRESS) && not ignoreUpButton) { + else if (buttonUp.pressedFor(buttonLongPress) && not ignoreUpButton) { #ifndef FIVEBUTTONS if (!settings.invertVolumeButtons) ret = button::volume_up; @@ -78,7 +72,7 @@ button Buttons::getButton() { #endif } - else if (downButton.wasReleased()) { + else if (buttonDown.wasReleased()) { if (!ignoreDownButton) { if (!settings.invertVolumeButtons) ret = button::previous; @@ -89,7 +83,7 @@ button Buttons::getButton() { ignoreDownButton = false; } - else if (downButton.pressedFor(LONG_PRESS) && not ignoreDownButton) { + else if (buttonDown.pressedFor(buttonLongPress) && not ignoreDownButton) { #ifndef FIVEBUTTONS if (!settings.invertVolumeButtons) ret = button::volume_down; @@ -115,18 +109,18 @@ button Buttons::getButton() { } #endif -// if (ret != button::none) { -// Serial.print(F("Button: ")); Serial.println(static_cast(ret)); -// } + if (ret != button::none) { + LOG(button_log, s_debug, F("Button: "), static_cast(ret)); + } return ret; } void Buttons::waitForNoButton() { do { readButtons(); - } while ( pauseButton.isPressed() - || upButton .isPressed() - || downButton .isPressed() + } while ( buttonPause.isPressed() + || buttonUp .isPressed() + || buttonDown .isPressed() #ifdef FIVEBUTTONS || buttonFour .isPressed() || buttonFive .isPressed() @@ -138,15 +132,16 @@ void Buttons::waitForNoButton() { } bool Buttons::isReset() { - return (digitalRead(buttonPause) == LOW && - digitalRead(buttonUp) == LOW && - digitalRead(buttonDown) == LOW ); + const int buttonActiveLevel = getLevel(buttonPinType, level::active); + return (digitalRead(buttonPausePin) == buttonActiveLevel && + digitalRead(buttonUpPin ) == buttonActiveLevel && + digitalRead(buttonDownPin ) == buttonActiveLevel ); } bool Buttons::isBreak() { readButtons(); - if (upButton.wasReleased() || downButton.wasReleased()) { - Serial.print(F("Abgebrochen!")); + if (buttonUp.wasReleased() || buttonDown.wasReleased()) { + LOG(button_log, s_info, F("Abgebrochen!")); return true; } return false; @@ -156,22 +151,22 @@ bool Buttons::askCode(Settings::pin_t &code) { uint8_t x = 0; while (x < 4) { readButtons(); - if (pauseButton.pressedFor(LONG_PRESS)) + if (buttonPause.pressedFor(buttonLongPress)) return false; - if (pauseButton.wasReleased()) + if (buttonPause.wasReleased()) code[x++] = 1; - if (upButton.wasReleased()) + if (buttonUp.wasReleased()) code[x++] = 2; - if (downButton.wasReleased()) + if (buttonDown.wasReleased()) code[x++] = 3; } return true; } void Buttons::readButtons() { - pauseButton.read(); - upButton .read(); - downButton .read(); + buttonPause.read(); + buttonUp .read(); + buttonDown .read(); #ifdef FIVEBUTTONS buttonFour .read(); buttonFive .read(); diff --git a/src/buttons.hpp b/src/buttons.hpp index 98973082..e51a7fc2 100644 --- a/src/buttons.hpp +++ b/src/buttons.hpp @@ -5,9 +5,7 @@ #include #include "settings.hpp" - -// uncomment the below line to enable five button support -//#define FIVEBUTTONS +#include "constants.hpp" enum class button { none, @@ -34,9 +32,9 @@ class Buttons { void readButtons(); - Button pauseButton; - Button upButton; - Button downButton; + Button buttonPause; + Button buttonUp ; + Button buttonDown ; #ifdef FIVEBUTTONS Button buttonFour; Button buttonFive; diff --git a/src/chip_card.cpp b/src/chip_card.cpp index 7cb4f355..66de8a3f 100644 --- a/src/chip_card.cpp +++ b/src/chip_card.cpp @@ -1,8 +1,11 @@ +#include "chip_card.hpp" + #include #include #include -#include "chip_card.hpp" +#include "constants.hpp" +#include "logger.hpp" namespace { /** @@ -13,9 +16,8 @@ void dump_byte_array(byte * buffer, byte bufferSize) { Serial.print(buffer[i] < 0x10 ? " 0" : " "); Serial.print(buffer[i], HEX); } + Serial.println(); } -const byte RST_PIN = 9; // Configurable, see typical pin layout above -const byte SS_PIN = 10; // Configurable, see typical pin layout above MFRC522::MIFARE_Key key{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; const byte sector = 1; @@ -25,18 +27,16 @@ const unsigned int removeDelay = 3; } // namespace Chip_card::Chip_card() -: mfrc522(*(new MFRC522(SS_PIN, RST_PIN))) +: mfrc522(*(new MFRC522(mfrc522_SSPin, mfrc522_RSTPin))) , cardRemovedSwitch(removeDelay) {} bool Chip_card::readCard(nfcTagObject &nfcTag) { // Show some details of the PICC (that is: the tag/card) - Serial.print(F("Card UID:")); + LOG(card_log, s_info, F("Card UID:")); dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); - Serial.println(); - Serial.print(F("PICC type: ")); MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); - Serial.println(mfrc522.PICC_GetTypeName(piccType)); + LOG(card_log, s_info, F("PICC type: "), mfrc522.PICC_GetTypeName(piccType)); byte buffer[18]; MFRC522::StatusCode status = MFRC522::STATUS_ERROR; @@ -46,7 +46,7 @@ bool Chip_card::readCard(nfcTagObject &nfcTag) { (piccType == MFRC522::PICC_TYPE_MIFARE_1K ) || (piccType == MFRC522::PICC_TYPE_MIFARE_4K ) ) { - Serial.println(F("Authenticating Classic using key A...")); + LOG(card_log, s_info, F("Authenticating Classic using key A...")); status = mfrc522.PCD_Authenticate( MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); } @@ -55,18 +55,17 @@ bool Chip_card::readCard(nfcTagObject &nfcTag) { byte pACK[] = {0, 0}; //16 bit PassWord ACK returned by the tempCard // Authenticate using key A - Serial.println(F("Authenticating MIFARE UL...")); + LOG(card_log, s_info, F("Authenticating MIFARE UL...")); status = mfrc522.PCD_NTAG216_AUTH(key.keyByte, pACK); } if (status != MFRC522::STATUS_OK) { - Serial.print(F("PCD_Authenticate() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); + LOG(card_log, s_error, F("PCD_Authenticate() failed: "), mfrc522.GetStatusCodeName(status)); return false; } // Show the whole sector as it currently is - // Serial.println(F("Current data in sector:")); + // LOG(card_log, s_info, F("Current data in sector:")); // mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); // Serial.println(); @@ -78,8 +77,7 @@ bool Chip_card::readCard(nfcTagObject &nfcTag) { byte size = sizeof(buffer); status = (MFRC522::StatusCode)mfrc522.MIFARE_Read(4, buffer, &size); if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Read() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); + LOG(card_log, s_error, F("MIFARE_Read() failed: "), mfrc522.GetStatusCodeName(status)); return false; } } @@ -90,42 +88,35 @@ bool Chip_card::readCard(nfcTagObject &nfcTag) { status = static_cast(mfrc522.MIFARE_Read(8, buffer2, &size2)); if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Read_1() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); + LOG(card_log, s_error, F("MIFARE_Read_1() failed: "), mfrc522.GetStatusCodeName(status)); return false; } memcpy(buffer, buffer2, 4); status = static_cast(mfrc522.MIFARE_Read(9, buffer2, &size2)); if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Read_2() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); + LOG(card_log, s_error, F("MIFARE_Read_2() failed: "), mfrc522.GetStatusCodeName(status)); return false; } memcpy(buffer + 4, buffer2, 4); status = static_cast(mfrc522.MIFARE_Read(10, buffer2, &size2)); if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Read_3() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); + LOG(card_log, s_error, F("MIFARE_Read_3() failed: "), mfrc522.GetStatusCodeName(status)); return false; } memcpy(buffer + 8, buffer2, 4); status = static_cast(mfrc522.MIFARE_Read(11, buffer2, &size2)); if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Read_4() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); + LOG(card_log, s_error, F("MIFARE_Read_4() failed: "), mfrc522.GetStatusCodeName(status)); return false; } memcpy(buffer + 12, buffer2, 4); } - Serial.print(F("Data on Card ")); - Serial.println(F(":")); + LOG(card_log, s_info, F("Data on Card: ")); dump_byte_array(buffer, 16); - Serial.println(); - Serial.println(); uint32_t tempCookie; tempCookie = (uint32_t)buffer[0] << 24; @@ -165,7 +156,7 @@ bool Chip_card::writeCard(const nfcTagObject &nfcTag) { (mifareType == MFRC522::PICC_TYPE_MIFARE_1K ) || (mifareType == MFRC522::PICC_TYPE_MIFARE_4K ) ) { - Serial.println(F("Authenticating again using key A...")); + LOG(card_log, s_info, F("Authenticating again using key A...")); status = mfrc522.PCD_Authenticate( MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); } @@ -174,20 +165,18 @@ bool Chip_card::writeCard(const nfcTagObject &nfcTag) { byte pACK[] = {0, 0}; //16 bit PassWord ACK returned by the NFCtag // Authenticate using key A - Serial.println(F("Authenticating UL...")); + LOG(card_log, s_info, F("Authenticating UL...")); status = mfrc522.PCD_NTAG216_AUTH(key.keyByte, pACK); } if (status != MFRC522::STATUS_OK) { - Serial.print(F("PCD_Authenticate() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); + LOG(card_log, s_error, F("PCD_Authenticate() failed: "), mfrc522.GetStatusCodeName(status)); return false; } // Write data to the block - Serial.println(F("Writing data ...")); + LOG(card_log, s_info, F("Writing data ...")); dump_byte_array(buffer, 16); - Serial.println(); if ((mifareType == MFRC522::PICC_TYPE_MIFARE_MINI ) || (mifareType == MFRC522::PICC_TYPE_MIFARE_1K ) || @@ -218,11 +207,9 @@ bool Chip_card::writeCard(const nfcTagObject &nfcTag) { } if (status != MFRC522::STATUS_OK) { - Serial.print(F("MIFARE_Write() failed: ")); - Serial.println(mfrc522.GetStatusCodeName(status)); + LOG(card_log, s_error, F("MIFARE_Write() failed: "), mfrc522.GetStatusCodeName(status)); return false; } - Serial.println(); return true; } diff --git a/src/chip_card.hpp b/src/chip_card.hpp index 9dda12b0..dd736d69 100644 --- a/src/chip_card.hpp +++ b/src/chip_card.hpp @@ -3,8 +3,6 @@ #include -const uint32_t cardCookie = 322417479; - enum class mode_t: uint8_t { none = 0, diff --git a/src/constants.hpp b/src/constants.hpp new file mode 100644 index 00000000..f9d47af5 --- /dev/null +++ b/src/constants.hpp @@ -0,0 +1,59 @@ +#ifndef SRC_CONSTANTS_HPP_ +#define SRC_CONSTANTS_HPP_ + +#include + +// ####### helper for level ############################ + +enum class level { + inactive, + active , +}; +enum class levelType { + activeHigh, + activeLow, +}; + +inline int getLevel(levelType t, level l) { return (l == level::inactive) ? (t==levelType::activeHigh ? LOW : HIGH) + : (t==levelType::activeHigh ? HIGH : LOW ); } + +// ####### buttons ##################################### + +// uncomment the below line to enable five button support +//#define FIVEBUTTONS + +const uint32_t buttonLongPress = 1000; // timeout for long press button in ms +const uint8_t buttonPausePin = A0; +const uint8_t buttonUpPin = A1; +const uint8_t buttonDownPin = A2; + +#ifdef FIVEBUTTONS +const uint8_t buttonFourPin = A3; +const uint8_t buttonFivePin = A4; +#endif + +const levelType buttonPinType = levelType::activeLow; +const uint32_t buttonDbTime = 25; // Debounce time in milliseconds (default 25ms) + +// ####### chip_card ################################### + +const uint32_t cardCookie = 322417479; +const byte mfrc522_RSTPin = 9; // Configurable, see typical pin layout above +const byte mfrc522_SSPin = 10; // Configurable, see typical pin layout above + +// ####### mp3 ######################################### + +const uint8_t dfPlayer_receivePin = 2; +const uint8_t dfPlayer_transmitPin = 3; +const uint8_t dfPlayer_busyPin = 4; +const levelType dfPlayer_busyPinType = levelType::activeHigh; + + +// ####### tonuino ##################################### + +const uint8_t shutdownPin = 7; +const levelType shutdownPinType = levelType::activeHigh; +const uint8_t openAnalogPin = A7; +const unsigned long cycleTime = 100; + +#endif /* SRC_CONSTANTS_HPP_ */ diff --git a/src/log.cpp b/src/log.cpp new file mode 100644 index 00000000..29053046 --- /dev/null +++ b/src/log.cpp @@ -0,0 +1,15 @@ +#include "log.hpp" + +const __FlashStringHelper* getSeverityName(severity sev) { + switch (sev) { + case s_debug : return F("debug" ); + case s_info : return F("info" ); + case s_warning: return F("warning"); + case s_error : return F("error" ); + } + return F("unknown"); +} + + + + diff --git a/src/log.hpp b/src/log.hpp new file mode 100644 index 00000000..877cd390 --- /dev/null +++ b/src/log.hpp @@ -0,0 +1,76 @@ +#ifndef SRC_LOG_HPP_ +#define SRC_LOG_HPP_ + +#include + +#define DEFINE_LOGGER(Logger_, MinSeverity_, Forwarder_) \ + struct Logger_ : public logger_base \ + { static __FlashStringHelper const* name() {return F(#Logger_);} } + +#define LOG(Logger_, Severity_, Expression_...) \ + if constexpr ( Logger_::will_forward::result ) \ + Logger_::template log< Severity_ >(Logger_::name(), Expression_) + +enum severity { + s_debug , + s_info , + s_warning, + s_error , +}; + +extern const __FlashStringHelper* getSeverityName(severity sev); + +//! compile-time if +template struct if_ { typedef T1 result_type; }; +template< typename T1, typename T2> struct if_ { typedef T2 result_type; }; +//! bool-to-type +template struct bool_ { enum { result = b }; typedef bool_ result_type; }; +//! determines whether two types have the same type +template struct is_same_type : bool_ {}; +template struct is_same_type : bool_ {}; + +class logger { +public: + template + struct will_forward { + static const bool result = true; + }; + + static void log() { + Serial.println(); + } + template + static void log(T t, Types ... types) { + Serial.print(t); + log(types...); + } + + template + static void log(const __FlashStringHelper* /*logname*/, Types ... types) { +// Serial.print(getSeverityName(Severity)); +// Serial.print(F("-")); +// Serial.print(logname); +// Serial.print(F(": ")); + log(types...); + } + +}; + +template +class logger_base { +public: + typedef typename if_::result, logger, FwdLogger>::result_type forward_logger_type; + + template + struct will_forward { + static const bool result = Severity >= MinSeverity + && forward_logger_type::template will_forward::result; + }; + + template + static void log(const __FlashStringHelper* logname, Types ... types) { + forward_logger_type::template log(logname, types...); + } +}; + +#endif /* SRC_LOG_HPP_ */ diff --git a/src/logger.hpp b/src/logger.hpp new file mode 100644 index 00000000..6eaaf005 --- /dev/null +++ b/src/logger.hpp @@ -0,0 +1,20 @@ +#ifndef SRC_LOGGER_HPP_ +#define SRC_LOGGER_HPP_ + +#include "log.hpp" + +DEFINE_LOGGER(tonuino_log , s_info, void); + +DEFINE_LOGGER(init_log , s_info, tonuino_log); +DEFINE_LOGGER(card_log , s_info, tonuino_log); +DEFINE_LOGGER(play_log , s_info, tonuino_log); +DEFINE_LOGGER(standby_log , s_info, tonuino_log); +DEFINE_LOGGER(cmd_log , s_info, tonuino_log); +DEFINE_LOGGER(admin_log , s_info, tonuino_log); +DEFINE_LOGGER(menu_log , s_info, tonuino_log); +DEFINE_LOGGER(button_log , s_info, tonuino_log); +DEFINE_LOGGER(modifier_log, s_info, tonuino_log); +DEFINE_LOGGER(mp3_log , s_info, tonuino_log); +DEFINE_LOGGER(settings_log, s_info, tonuino_log); + +#endif /* SRC_LOGGER_HPP_ */ diff --git a/src/modifier.cpp b/src/modifier.cpp index c7bd55ee..28e9a2ff 100644 --- a/src/modifier.cpp +++ b/src/modifier.cpp @@ -2,28 +2,27 @@ #include "mp3.hpp" #include "tonuino.hpp" +#include "logger.hpp" void SleepTimer::loop() { if (sleepAtMillis != 0 && millis() > sleepAtMillis) { - Serial.println(F("=== SleepTimer::loop() -> SLEEP!")); + LOG(modifier_log, s_info, F("=== SleepTimer::loop() -> SLEEP!")); mp3.pause(); tonuino.setStandbyTimer(); tonuino.resetActiveModifier(); - delete this; } } void SleepTimer::start(uint8_t minutes) { - Serial.println(F("=== SleepTimer()")); - Serial.println(minutes); + LOG(modifier_log, s_info, F("=== SleepTimer(), minutes: "), minutes); sleepAtMillis = millis() + minutes * 60000; //playAdvertisement(302); } void FreezeDance::loop() { if (nextStopAtMillis != 0 && millis() > nextStopAtMillis) { - Serial.println(F("== FreezeDance::loop() -> FREEZE!")); + LOG(modifier_log, s_info, F("== FreezeDance::loop() -> FREEZE!")); if (mp3.isPlaying()) { mp3.playAdvertisement(301); } @@ -33,26 +32,24 @@ void FreezeDance::loop() { void FreezeDance::setNextStopAtMillis() { const uint16_t seconds = random(minSecondsBetweenStops, maxSecondsBetweenStops + 1); - Serial.println(F("=== FreezeDance::setNextStopAtMillis()")); - Serial.println(seconds); + LOG(modifier_log, s_info, F("=== FreezeDance::setNextStopAtMillis(), seconds: "), seconds); nextStopAtMillis = millis() + seconds * 1000; } bool KindergardenMode::handleNext() { - Serial.println(F("== KindergardenMode::handleNext() -> NEXT")); + LOG(modifier_log, s_info, F("== KindergardenMode::handleNext() -> NEXT")); if (cardQueued) { cardQueued = false; tonuino.setCard(nextCard); - Serial.println(nextCard.nfcFolderSettings.folder); - //Serial.println(myFolder->mode); + LOG(modifier_log, s_info, F("Folder: "), nextCard.nfcFolderSettings.folder, F(" Mode: "), static_cast(nextCard.nfcFolderSettings.mode)); tonuino.playFolder(); return true; } return false; } bool KindergardenMode::handleRFID(const nfcTagObject &newCard) { // lot of work to do! - Serial.println(F("== KindergardenMode::handleRFID() -> queued!")); + LOG(modifier_log, s_info, F("== KindergardenMode::handleRFID() -> queued!")); nextCard = newCard; cardQueued = true; if (!mp3.isPlaying()) { @@ -62,7 +59,7 @@ bool KindergardenMode::handleRFID(const nfcTagObject &newCard) { // lot of work } bool RepeatSingleModifier::handleNext() { - Serial.println(F("== RepeatSingleModifier::handleNext() -> REPEAT CURRENT TRACK")); + LOG(modifier_log, s_info, F("== RepeatSingleModifier::handleNext() -> REPEAT CURRENT TRACK")); delay(50); if (!mp3.isPlaying()) { mp3.loop(); // this will call Mp3Notify::OnPlayFinished() but will be blocked by lastTrackFinished @@ -79,7 +76,7 @@ bool RepeatSingleModifier::handleNext() { // } else { // playAdvertisement(volume, false); // } -// Serial.println(F("== FeedbackModifier::handleVolumeDown()!")); +// LOG(modifier_log, s_info, F("== FeedbackModifier::handleVolumeDown()!")); // return false; //} //bool FeedbackModifier::handleVolumeUp() { @@ -88,11 +85,11 @@ bool RepeatSingleModifier::handleNext() { // } else { // playAdvertisement(volume, false); // } -// Serial.println(F("== FeedbackModifier::handleVolumeUp()!")); +// LOG(modifier_log, s_info, F("== FeedbackModifier::handleVolumeUp()!")); // return false; //} //bool FeedbackModifier::handleRFID(const nfcTagObject &/*newCard*/) { -// Serial.println(F("== FeedbackModifier::handleRFID()")); +// LOG(modifier_log, s_info, F("== FeedbackModifier::handleRFID()")); // return false; //} diff --git a/src/modifier.hpp b/src/modifier.hpp index 79f7e2f6..afa87633 100644 --- a/src/modifier.hpp +++ b/src/modifier.hpp @@ -4,6 +4,7 @@ #include #include "chip_card.hpp" +#include "logger.hpp" class Tonuino; class Mp3; @@ -13,7 +14,6 @@ struct nfcTagObject; class Modifier { public: Modifier(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): tonuino(tonuino), mp3(mp3), settings(settings) {} - virtual ~Modifier () {} virtual void loop () {} virtual bool handlePause () { return false; } virtual bool handleNext () { return false; } @@ -62,13 +62,13 @@ class FreezeDance: public Modifier { class Locked: public Modifier { public: Locked(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} - bool handlePause () final { Serial.println(F("== Locked::handlePause() -> LOCKED!")) ; return true; } - bool handleNextButton () final { Serial.println(F("== Locked::handleNextButton() -> LOCKED!")) ; return true; } - bool handlePreviousButton() final { Serial.println(F("== Locked::handlePreviousButton() -> LOCKED!")); return true; } - bool handleVolumeUp () final { Serial.println(F("== Locked::handleVolumeUp() -> LOCKED!")) ; return true; } - bool handleVolumeDown () final { Serial.println(F("== Locked::handleVolumeDown() -> LOCKED!")) ; return true; } + bool handlePause () final { LOG(modifier_log, s_info, F("== Locked::handlePause() -> LOCKED!")) ; return true; } + bool handleNextButton () final { LOG(modifier_log, s_info, F("== Locked::handleNextButton() -> LOCKED!")) ; return true; } + bool handlePreviousButton() final { LOG(modifier_log, s_info, F("== Locked::handlePreviousButton() -> LOCKED!")); return true; } + bool handleVolumeUp () final { LOG(modifier_log, s_info, F("== Locked::handleVolumeUp() -> LOCKED!")) ; return true; } + bool handleVolumeDown () final { LOG(modifier_log, s_info, F("== Locked::handleVolumeDown() -> LOCKED!")) ; return true; } bool handleRFID(const nfcTagObject&) - final { Serial.println(F("== Locked::handleRFID() -> LOCKED!")) ; return true; } + final { LOG(modifier_log, s_info, F("== Locked::handleRFID() -> LOCKED!")) ; return true; } mode_t getActive() final { return mode_t::locked; } }; @@ -76,11 +76,11 @@ class Locked: public Modifier { class ToddlerMode: public Modifier { public: ToddlerMode(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} - bool handlePause () final { Serial.println(F("== ToddlerMode::handlePause() -> LOCKED!")) ; return true; } - bool handleNextButton () final { Serial.println(F("== ToddlerMode::handleNextButton() -> LOCKED!")) ; return true; } - bool handlePreviousButton() final { Serial.println(F("== ToddlerMode::handlePreviousButton() -> LOCKED!")); return true; } - bool handleVolumeUp () final { Serial.println(F("== ToddlerMode::handleVolumeUp() -> LOCKED!")) ; return true; } - bool handleVolumeDown () final { Serial.println(F("== ToddlerMode::handleVolumeDown() -> LOCKED!")) ; return true; } + bool handlePause () final { LOG(modifier_log, s_info, F("== ToddlerMode::handlePause() -> LOCKED!")) ; return true; } + bool handleNextButton () final { LOG(modifier_log, s_info, F("== ToddlerMode::handleNextButton() -> LOCKED!")) ; return true; } + bool handlePreviousButton() final { LOG(modifier_log, s_info, F("== ToddlerMode::handlePreviousButton() -> LOCKED!")); return true; } + bool handleVolumeUp () final { LOG(modifier_log, s_info, F("== ToddlerMode::handleVolumeUp() -> LOCKED!")) ; return true; } + bool handleVolumeDown () final { LOG(modifier_log, s_info, F("== ToddlerMode::handleVolumeDown() -> LOCKED!")) ; return true; } mode_t getActive() final { return mode_t::toddler; } }; @@ -90,9 +90,9 @@ class KindergardenMode: public Modifier { KindergardenMode(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} bool handleNext() final; - //bool handlePause () final { Serial.println(F("== KindergardenMode::handlePause() -> LOCKED!")) ; return true; } - bool handleNextButton () final { Serial.println(F("== KindergardenMode::handleNextButton() -> LOCKED!")) ; return true; } - bool handlePreviousButton() final { Serial.println(F("== KindergardenMode::handlePreviousButton() -> LOCKED!")); return true; } +//bool handlePause () final { LOG(modifier_log, s_info, F("== KindergardenMode::handlePause() -> LOCKED!")) ; return true; } + bool handleNextButton () final { LOG(modifier_log, s_info, F("== KindergardenMode::handleNextButton() -> LOCKED!")) ; return true; } + bool handlePreviousButton() final { LOG(modifier_log, s_info, F("== KindergardenMode::handlePreviousButton() -> LOCKED!")); return true; } bool handleRFID(const nfcTagObject &newCard) final; mode_t getActive () final { return mode_t::kindergarden; } diff --git a/src/mp3.cpp b/src/mp3.cpp index 18765479..d59a9f37 100644 --- a/src/mp3.cpp +++ b/src/mp3.cpp @@ -1,35 +1,25 @@ #include "mp3.hpp" #include "tonuino.hpp" - -namespace { - -const uint8_t receivePin = 2; -const uint8_t transmitPin = 3; -const uint8_t busyPin = 4; -} +#include "constants.hpp" uint16_t Mp3Notify::lastTrackFinished = 0; void Mp3Notify::OnError(uint16_t errorCode) { // see DfMp3_Error for code meaning - Serial.println(); - Serial.print(F("Com Error ")); - Serial.println(errorCode); + LOG(mp3_log, s_error, F("Com Error: "), errorCode); } void Mp3Notify::OnPlaySourceOnline (DfMp3_PlaySources source) { PrintlnSourceAction(source, F("online" )); } void Mp3Notify::OnPlaySourceInserted(DfMp3_PlaySources source) { PrintlnSourceAction(source, F("bereit" )); } void Mp3Notify::OnPlaySourceRemoved (DfMp3_PlaySources source) { PrintlnSourceAction(source, F("entfernt")); } void Mp3Notify::PrintlnSourceAction(DfMp3_PlaySources source, const __FlashStringHelper* action) { - if (source & DfMp3_PlaySources_Sd ) Serial.print(F("SD Karte ")); - if (source & DfMp3_PlaySources_Usb ) Serial.print(F("USB " )); - if (source & DfMp3_PlaySources_Flash) Serial.print(F("Flash " )); - Serial.println(action); + if (source & DfMp3_PlaySources_Sd ) LOG(mp3_log, s_info, F("SD Karte "), action); + if (source & DfMp3_PlaySources_Usb ) LOG(mp3_log, s_info, F("USB " ), action); + if (source & DfMp3_PlaySources_Flash) LOG(mp3_log, s_info, F("Flash " ), action); } void Mp3Notify::OnPlayFinished(DfMp3_PlaySources /*source*/, uint16_t track) { -// Serial.print(F("Track beendet")); -// Serial.println(track); + LOG(mp3_log, s_debug, F("Track beendet, Track: "), track); if (track == lastTrackFinished) return; else @@ -39,15 +29,15 @@ void Mp3Notify::OnPlayFinished(DfMp3_PlaySources /*source*/, uint16_t track) { Mp3::Mp3(const Settings& settings) : DFMiniMp3{softwareSerial} -, softwareSerial{receivePin, transmitPin} +, softwareSerial{dfPlayer_receivePin, dfPlayer_transmitPin} , settings{settings} { // Busy Pin - pinMode(busyPin, INPUT); + pinMode(dfPlayer_busyPin, dfPlayer_busyPinType == levelType::activeHigh ? INPUT : INPUT_PULLUP); } bool Mp3::isPlaying() const { - return !digitalRead(busyPin); + return !digitalRead(dfPlayer_busyPin); } void Mp3::waitForTrackToFinish() { @@ -99,21 +89,20 @@ void Mp3::playAdvertisement(advertTracks track, bool olnyIfIsPlaying) { void Mp3::increaseVolume() { if (volume < settings.maxVolume) { - DFMiniMp3::increaseVolume(); - ++volume; + DFMiniMp3::setVolume(++volume); } - Serial.println(volume); + LOG(mp3_log, s_info, F("Volume: "), volume); } void Mp3::decreaseVolume() { if (volume > settings.minVolume) { - DFMiniMp3::decreaseVolume(); - --volume; + DFMiniMp3::setVolume(--volume); } - Serial.println(volume); + LOG(mp3_log, s_info, F("Volume: "), volume); } void Mp3::setVolume() { volume = settings.initVolume; DFMiniMp3::setVolume(volume); + LOG(mp3_log, s_info, F("Volume: "), volume); } diff --git a/src/settings.cpp b/src/settings.cpp index d27b4bd5..d69905f9 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -2,6 +2,9 @@ #include +#include "constants.hpp" +#include "logger.hpp" + namespace { const int startAddressAdminSettings = sizeof(folderSettings::folder) * 100; @@ -17,19 +20,19 @@ uint8_t Settings::readByteFromFlash(uint16_t address) { } void Settings::clearEEPROM() { - Serial.println(F("Reset -> EEPROM wird gelöscht")); + LOG(settings_log, s_info, F("Reset -> EEPROM wird gelöscht")); for (uint16_t i = 0; i < EEPROM.length(); i++) { writeByteToFlash(i, 0); } } void Settings::writeSettingsToFlash() { - Serial.println(F("=== writeSettingsToFlash()")); + LOG(settings_log, s_info, F("=== writeSettingsToFlash()")); EEPROM.put(startAddressAdminSettings, *this); } void Settings::resetSettings() { - Serial.println(F("=== resetSettings()")); + LOG(settings_log, s_info, F("=== resetSettings()")); cookie = cardCookie; version = 2; maxVolume = 25; @@ -55,8 +58,7 @@ void Settings::resetSettings() { void Settings::migrateSettings(int oldVersion) { if (oldVersion == 1) { - Serial.println(F("=== resetSettings()")); - Serial.println(F("1 -> 2")); + LOG(settings_log, s_info, F("=== migradeSettings() 1 -> 2")); version = 2; adminMenuLocked = 0; adminMenuPin[0] = 1; @@ -69,47 +71,23 @@ void Settings::migrateSettings(int oldVersion) { } void Settings::loadSettingsFromFlash() { - Serial.println(F("=== loadSettingsFromFlash()")); + LOG(settings_log, s_info, F("=== loadSettingsFromFlash()")); EEPROM.get(startAddressAdminSettings, *this); if (cookie != cardCookie) resetSettings(); migrateSettings(version); - Serial.print(F("Version: ")); - Serial.println(version); - - Serial.print(F("Maximal Volume: ")); - Serial.println(maxVolume); - - Serial.print(F("Minimal Volume: ")); - Serial.println(minVolume); - - Serial.print(F("Initial Volume: ")); - Serial.println(initVolume); - - Serial.print(F("EQ: ")); - Serial.println(eq); - - Serial.print(F("Locked: ")); - Serial.println(locked); - - Serial.print(F("Sleep Timer: ")); - Serial.println(standbyTimer); - - Serial.print(F("Inverted Volume Buttons: ")); - Serial.println(invertVolumeButtons); - - Serial.print(F("Admin Menu locked: ")); - Serial.println(adminMenuLocked); - - Serial.print(F("Admin Menu Pin: ")); - Serial.print (adminMenuPin[0]); - Serial.print (adminMenuPin[1]); - Serial.print (adminMenuPin[2]); - Serial.println(adminMenuPin[3]); - - Serial.print(F("Pause when card removed: ")); - Serial.println(pauseWhenCardRemoved); + LOG(settings_log, s_info, F("Version: " ), version); + LOG(settings_log, s_info, F("Maximal Volume: " ), maxVolume); + LOG(settings_log, s_info, F("Minimal Volume: " ), minVolume); + LOG(settings_log, s_info, F("Initial Volume: " ), initVolume); + LOG(settings_log, s_info, F("EQ: " ), eq); + LOG(settings_log, s_info, F("Locked: " ), locked); + LOG(settings_log, s_info, F("Sleep Timer: " ), standbyTimer); + LOG(settings_log, s_info, F("Inverted Volume Buttons: "), invertVolumeButtons); + LOG(settings_log, s_info, F("Admin Menu locked: " ), adminMenuLocked); + LOG(settings_log, s_info, F("Admin Menu Pin: " ), adminMenuPin[0], adminMenuPin[1], adminMenuPin[2], adminMenuPin[3]); + LOG(settings_log, s_info, F("Pause when card removed: "), pauseWhenCardRemoved); } void Settings::writeFolderSettingToFlash(uint8_t folder, uint16_t track) { diff --git a/src/tonuino.cpp b/src/tonuino.cpp index 7107d4a6..d892301a 100644 --- a/src/tonuino.cpp +++ b/src/tonuino.cpp @@ -6,17 +6,14 @@ #include "array.hpp" #include "chip_card.hpp" -Tonuino tonuino; - -namespace { - -const uint8_t shutdownPin = 7; +#include "constants.hpp" +#include "logger.hpp" -} +Tonuino tonuino; void Tonuino::setup() { pinMode(shutdownPin , OUTPUT); - digitalWrite(shutdownPin, LOW); + digitalWrite(shutdownPin, getLevel(shutdownPinType, level::inactive)); // load Settings from EEPROM settings.loadSettingsFromFlash(); @@ -46,6 +43,7 @@ void Tonuino::setup() { void Tonuino::loop() { + unsigned long start = millis(); checkStandbyAtMillis(); mp3.loop(); @@ -55,6 +53,11 @@ void Tonuino::loop() { handleButtons(); handleChipCard(); + + unsigned long stop = millis(); + + if (stop-start < cycleTime) + delay(cycleTime - (stop - start)); } void Tonuino::handleButtons() { @@ -136,7 +139,7 @@ void Tonuino::handleChipCard(bool no_action) { if (chip_card.cardRemoved()) { if (not cardRemoved) { - Serial.println(F("Card Removed")); + LOG(card_log, s_info, F("Card Removed")); cardRemoved = true; chip_card.stopCard(); if (settings.pauseWhenCardRemoved) { @@ -150,7 +153,7 @@ void Tonuino::handleChipCard(bool no_action) { } else { if (cardRemoved) { - Serial.println(F("Card in")); + LOG(card_log, s_info, F("Card in")); cardRemoved = false; } else { @@ -175,7 +178,7 @@ void Tonuino::handleChipCard(bool no_action) { } setCard(tempCard); - Serial.println(myCard.nfcFolderSettings.folder); + LOG(card_log, s_info, F("folder: "), myCard.nfcFolderSettings.folder); if (myCard.cookie == cardCookie && myCard.nfcFolderSettings.mode != mode_t::none) { knownCard = false; // prevent nextTrack() when calling Mp3Notify::OnPlayFinished() in mp3.waitForTrackToFinish(); @@ -213,7 +216,7 @@ void Tonuino::writeCard(const nfcTagObject &nfcTag) { handleChipCard(true); } while (cardRemoved); - Serial.println(F("schreibe Karte...")); + LOG(card_log, s_info, F("schreibe Karte...")); if (chip_card.writeCard(nfcTag)) mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); else @@ -224,72 +227,67 @@ void Tonuino::writeCard(const nfcTagObject &nfcTag) { } void Tonuino::playFolder() { - Serial.println(F("== playFolder()")); + LOG(play_log, s_info, F("== playFolder()")); disableStandbyTimer(); knownCard = true; numTracksInFolder = mp3.getFolderTrackCount(myFolder->folder); firstTrack = 1; - Serial.print(numTracksInFolder); - Serial.print(F(" Dateien in Ordner ")); - Serial.println(myFolder->folder); + LOG(play_log, s_info, numTracksInFolder, F(" Dateien in Ordner "), myFolder->folder); switch (myFolder->mode) { case mode_t::hoerspiel: // Hörspielmodus: eine zufällige Datei aus dem Ordner - Serial.println(F("Hörspielmodus -> zufälligen Track wiedergeben")); + LOG(play_log, s_info, F("Hörspielmodus")); currentTrack = random(1, numTracksInFolder + 1); - Serial.println(currentTrack); + LOG(play_log, s_info, F("Track: "), currentTrack); break; case mode_t::album: // Album Modus: kompletten Ordner spielen - Serial.println(F("Album Modus -> kompletten Ordner wiedergeben")); + LOG(play_log, s_info, F("Album Modus")); currentTrack = 1; break; case mode_t::party: // Party Modus: Ordner in zufälliger Reihenfolge - Serial.println( - F("Party Modus -> Ordner in zufälliger Reihenfolge wiedergeben")); + LOG(play_log, s_info, F("Party Modus")); shuffleQueue(); currentTrack = 1; break; case mode_t::einzel: // Einzel Modus: eine Datei aus dem Ordner abspielen - Serial.println(F("Einzel Modus -> eine Datei aus dem Odrdner abspielen")); + LOG(play_log, s_info, F("Einzel Modus")); currentTrack = myFolder->special; + LOG(play_log, s_info, F("Track: "), currentTrack); break; case mode_t::hoerbuch: case mode_t::hoerbuch_1: // Hörbuch Modus: kompletten Ordner spielen und Fortschritt merken (oder nur eine Datei) - Serial.println(F("Hörbuch Modus -> kompletten Ordner spielen und Fortschritt merken")); + LOG(play_log, s_info, F("Hörbuch Modus")); currentTrack = settings.readFolderSettingFromFlash(myFolder->folder); if (currentTrack == 0 || currentTrack > numTracksInFolder) { currentTrack = 1; } + LOG(play_log, s_info, F("Track: "), currentTrack); break; case mode_t::hoerspiel_vb: // Spezialmodus Von-Bin: Hörspiel: eine zufällige Datei aus dem Ordner - Serial.println(F("Spezialmodus Von-Bin: Hörspiel -> zufälligen Track wiedergeben")); - Serial.print(myFolder->special); - Serial.print(F(" bis ")); - Serial.println(myFolder->special2); + LOG(play_log, s_info, F("Spezialmodus Von-Bin: Hörspiel")); + LOG(play_log, s_info, myFolder->special, F(" bis "), myFolder->special2); firstTrack = myFolder->special; numTracksInFolder = myFolder->special2; currentTrack = random(myFolder->special, numTracksInFolder + 1); - Serial.println(currentTrack); + LOG(play_log, s_info, F("Track: "), currentTrack); break; case mode_t::album_vb: // Spezialmodus Von-Bis: Album: alle Dateien zwischen Start und Ende spielen - Serial.println(F("Spezialmodus Von-Bis: Album: alle Dateien zwischen Start- und Enddatei spielen")); - Serial.print(myFolder->special); - Serial.print(F(" bis ")); - Serial.println(myFolder->special2); + LOG(play_log, s_info, F("Spezialmodus Von-Bis: Album")); + LOG(play_log, s_info, myFolder->special, F(" bis "), myFolder->special2); firstTrack = myFolder->special; numTracksInFolder = myFolder->special2; currentTrack = myFolder->special; @@ -297,7 +295,8 @@ void Tonuino::playFolder() { case mode_t::party_vb: // Spezialmodus Von-Bis: Party Ordner in zufälliger Reihenfolge - Serial.println(F("Spezialmodus Von-Bis: Party -> Ordner in zufälliger Reihenfolge wiedergeben")); + LOG(play_log, s_info, F("Spezialmodus Von-Bis: Party")); + LOG(play_log, s_info, myFolder->special, F(" bis "), myFolder->special2); firstTrack = myFolder->special; numTracksInFolder = myFolder->special2; shuffleQueue(); @@ -314,15 +313,15 @@ void Tonuino::playFolder() { } void Tonuino::playShortCut(uint8_t shortCut) { - Serial.println(F("=== playShortCut()")); - Serial.println(shortCut); + LOG(play_log, s_info, F("=== playShortCut(): "),shortCut); if (settings.shortCuts[shortCut].folder != 0) { setFolder(&settings.shortCuts[shortCut]); playFolder(); delay(1000); } else { - Serial.println(F("Shortcut not configured!")); - mp3.playMp3FolderTrack(mp3Tracks::t_262_pling); + LOG(play_log, s_info, F("Shortcut not configured!")); + if (not knownCard) + mp3.playMp3FolderTrack(mp3Tracks::t_262_pling); } } @@ -336,13 +335,13 @@ void Tonuino::nextTrack() { // verarbeitet werden return; - Serial.println(F("=== nextTrack()")); + LOG(play_log, s_info, F("=== nextTrack()")); switch (myFolder->mode) { case mode_t::hoerspiel : case mode_t::hoerspiel_vb: if (not mp3.isPlaying()) { - Serial.println(F("Hörspielmodus ist aktiv -> keinen neuen Track spielen")); + LOG(play_log, s_info, F("Hörspielmodus ist aktiv -> keinen neuen Track")); knownCard = false; setStandbyTimer(); //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! @@ -354,8 +353,7 @@ void Tonuino::nextTrack() { if (currentTrack != numTracksInFolder) { ++currentTrack; mp3.playFolderTrack(myFolder->folder, currentTrack); - Serial.print(F("Albummodus ist aktiv -> nächster Track: ")); - Serial.println(currentTrack); + LOG(play_log, s_info, F("Albummodus ist aktiv -> nächster Track: "), currentTrack); } else { // mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! knownCard = false; @@ -366,21 +364,21 @@ void Tonuino::nextTrack() { case mode_t::party : case mode_t::party_vb: if (currentTrack != numTracksInFolder - firstTrack + 1) { - Serial.print(F("Party -> weiter in der Queue ")); + LOG(play_log, s_info, F("Party -> weiter in der Queue ")); ++currentTrack; } else { - Serial.println(F("Ende der Queue -> beginne von vorne")); + LOG(play_log, s_info, F("Ende der Queue -> beginne von vorne")); currentTrack = 1; //// Wenn am Ende der Queue neu gemischt werden soll bitte die Zeilen wieder aktivieren - //Serial.println(F("Ende der Queue -> mische neu")); + //LOG(play_log, s_info, F("Ende der Queue -> mische neu")); //shuffleQueue(); } - Serial.println(queue[currentTrack - 1]); + LOG(play_log, s_info, F("Track: "), queue[currentTrack - 1]); mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); break; case mode_t::einzel: - Serial.println(F("Einzel Modus aktiv -> Strom sparen")); + LOG(play_log, s_info, F("Einzel Modus aktiv -> stop")); //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! knownCard = false; setStandbyTimer(); @@ -389,12 +387,13 @@ void Tonuino::nextTrack() { case mode_t::hoerbuch: if (currentTrack != numTracksInFolder) { ++currentTrack; - Serial.print(F("Hörbuch Modus ist aktiv -> nächster Track und Fortschritt speichern")); - Serial.println(currentTrack); + LOG(play_log, s_info, F("Hörbuch Modus ist aktiv -> nächster Track und Fortschritt speichern")); + LOG(play_log, s_info, F("Track: "), currentTrack); mp3.playFolderTrack(myFolder->folder, currentTrack); // Fortschritt im EEPROM abspeichern settings.writeFolderSettingToFlash(myFolder->folder, currentTrack); } else { + LOG(play_log, s_info, F("Hörbuch Modus ist aktiv -> Ende")); //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! // Fortschritt zurück setzen settings.writeFolderSettingToFlash(myFolder->folder, 1); @@ -408,8 +407,8 @@ void Tonuino::nextTrack() { else currentTrack = 1; - Serial.print(F("Hörbuch Modus single ist aktiv -> Fortschritt speichern und beenden")); - Serial.println(currentTrack); + LOG(play_log, s_info, F("Hörbuch Modus single ist aktiv -> Fortschritt speichern und beenden")); + LOG(play_log, s_info, F("Track: "), currentTrack); // Fortschritt im EEPROM abspeichern settings.writeFolderSettingToFlash(myFolder->folder, currentTrack); //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! @@ -423,18 +422,18 @@ void Tonuino::nextTrack() { } void Tonuino::previousTrack() { - Serial.println(F("=== previousTrack()")); + LOG(play_log, s_info, F("=== previousTrack()")); switch (myFolder->mode) { case mode_t::hoerspiel: case mode_t::hoerspiel_vb: - Serial.println(F("Hörspielmodus ist aktiv -> Track von vorne spielen")); + LOG(play_log, s_info, F("Hörspielmodus ist aktiv -> Track von vorne spielen")); mp3.playFolderTrack(myFolder->folder, currentTrack); break; case mode_t::album: case mode_t::album_vb: - Serial.println(F("Albummodus ist aktiv -> vorheriger Track")); + LOG(play_log, s_info, F("Albummodus ist aktiv -> vorheriger Track")); if (currentTrack != firstTrack) { --currentTrack; } @@ -444,24 +443,24 @@ void Tonuino::previousTrack() { case mode_t::party: case mode_t::party_vb: if (currentTrack != 1) { - Serial.print(F("Party Modus ist aktiv -> zurück in der Qeueue ")); + LOG(play_log, s_info, F("Party Modus ist aktiv -> zurück in der Qeueue ")); --currentTrack; } else { - Serial.print(F("Anfang der Queue -> springe ans Ende ")); + LOG(play_log, s_info, F("Party Modus, Anfang der Queue -> springe ans Ende ")); currentTrack = numTracksInFolder - firstTrack + 1; } - Serial.println(queue[currentTrack - 1]); + LOG(play_log, s_info, F("Track: "), queue[currentTrack - 1]); mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); break; case mode_t::einzel: - Serial.println(F("Einzel Modus aktiv -> Track von vorne spielen")); + LOG(play_log, s_info, F("Einzel Modus aktiv -> Track von vorne spielen")); mp3.playFolderTrack(myFolder->folder, currentTrack); break; case mode_t::hoerbuch: case mode_t::hoerbuch_1: - Serial.println(F("Hörbuch Modus ist aktiv -> vorheriger Track und Fortschritt speichern")); + LOG(play_log, s_info, F("Hörbuch Modus ist aktiv -> vorheriger Track und Fortschritt speichern")); if (currentTrack != 1) { --currentTrack; } @@ -477,24 +476,24 @@ void Tonuino::previousTrack() { // Funktionen für den Standby Timer (z.B. über Pololu-Switch oder Mosfet) void Tonuino::setStandbyTimer() { - Serial.println(F("=== setStandbyTimer()")); + LOG(standby_log, s_info, F("=== setStandbyTimer()")); if (settings.standbyTimer != 0) standbyAtMillis = millis() + (settings.standbyTimer * 60 * 1000); else standbyAtMillis = 0; - Serial.println(standbyAtMillis); + LOG(standby_log, s_info, F("standbyAtMillis: "), standbyAtMillis); } void Tonuino::disableStandbyTimer() { - Serial.println(F("=== disablestandby()")); + LOG(standby_log, s_info, F("=== disablestandby()")); standbyAtMillis = 0; } void Tonuino::checkStandbyAtMillis() { if (standbyAtMillis != 0 && millis() > standbyAtMillis) { - Serial.println(F("=== power off!")); + LOG(standby_log, s_info, F("=== power off!")); // enter sleep state - digitalWrite(shutdownPin, HIGH); + digitalWrite(shutdownPin, getLevel(shutdownPinType, level::active)); delay(500); // http://discourse.voss.earth/t/intenso-s10000-powerbank-automatische-abschaltung-software-only/805 @@ -519,7 +518,7 @@ void Tonuino::volumeUpButton() { if (activeModifier->handleVolumeUp()) return; - Serial.println(F("=== volumeUp()")); + LOG(cmd_log, s_info, F("=== volumeUp()")); mp3.increaseVolume(); } @@ -527,7 +526,7 @@ void Tonuino::volumeDownButton() { if (activeModifier->handleVolumeDown()) return; - Serial.println(F("=== volumeDown()")); + LOG(cmd_log, s_info, F("=== volumeDown()")); mp3.decreaseVolume(); } @@ -535,6 +534,7 @@ void Tonuino::nextButton() { if (activeModifier->handleNextButton()) return; + LOG(cmd_log, s_info, F("=== next()")); nextTrack(); } @@ -542,6 +542,7 @@ void Tonuino::previousButton() { if (activeModifier->handlePreviousButton()) return; + LOG(cmd_log, s_info, F("=== previous()")); previousTrack(); } @@ -588,13 +589,13 @@ bool Tonuino::setupFolder(folderSettings& theFolder) { } void Tonuino::resetCard() { - Serial.print(F("Karte wird neu konfiguriert!")); + LOG(card_log, s_info, F("Karte wird neu konfiguriert!")); setupCard(); } void Tonuino::setupCard() { + LOG(card_log, s_info, F("=== setupCard()")); mp3.pause(); - Serial.println(F("=== setupCard()")); nfcTagObject newCard; if (setupFolder(newCard.nfcFolderSettings)) { @@ -614,11 +615,10 @@ bool Tonuino::specialCard(const nfcTagObject &nfcTag) { if (nfcTag.nfcFolderSettings.folder != 0) return false; - //Serial.print(F("special card, mode = ")); - //Serial.println(static_cast(nfcTag.nfcFolderSettings.mode)); + //LOG(card_log, s_info, F("special card, mode = "), static_cast(nfcTag.nfcFolderSettings.mode)); if (activeModifier->getActive() == nfcTag.nfcFolderSettings.mode) { resetActiveModifier(); - Serial.println(F("modifier removed")); + LOG(card_log, s_info, F("modifier removed")); mp3.playAdvertisement(advertTracks::t_261_deactivate_mod_card, false); return true; } @@ -628,23 +628,23 @@ bool Tonuino::specialCard(const nfcTagObject &nfcTag) { case mode_t::none: case mode_t::admin_card: chip_card.stopCard(); adminMenu() ;break; - case mode_t::sleep_timer: Serial.println(F("activate sleepTimer")); + case mode_t::sleep_timer: LOG(card_log, s_info, F("activate sleepTimer")); mp3.playAdvertisement(advertTracks::t_302_sleep, false); activeModifier = &sleepTimer; sleepTimer.start(nfcTag.nfcFolderSettings.special) ;break; - case mode_t::freeze_dance: Serial.println(F("activate freezeDance")); + case mode_t::freeze_dance: LOG(card_log, s_info, F("activate freezeDance")); mp3.playAdvertisement(advertTracks::t_300_freeze_into, false); activeModifier = &freezeDance; ;break; - case mode_t::locked: Serial.println(F("activate locked")); + case mode_t::locked: LOG(card_log, s_info, F("activate locked")); mp3.playAdvertisement(advertTracks::t_303_locked, false); activeModifier = &locked ;break; - case mode_t::toddler: Serial.println(F("activate toddlerMode")); + case mode_t::toddler: LOG(card_log, s_info, F("activate toddlerMode")); mp3.playAdvertisement(advertTracks::t_304_buttonslocked, false); activeModifier = &toddlerMode ;break; - case mode_t::kindergarden: Serial.println(F("activate kindergardenMode")); + case mode_t::kindergarden: LOG(card_log, s_info, F("activate kindergardenMode")); mp3.playAdvertisement(advertTracks::t_305_kindergarden, false); activeModifier = &kindergardenMode ;break; - case mode_t::repeat_single:Serial.println(F("activate repeatSingleModifier")); + case mode_t::repeat_single:LOG(card_log, s_info, F("activate repeatSingleModifier")); mp3.playAdvertisement(advertTracks::t_260_activate_mod_card, false); activeModifier = &repeatSingleModifier ;break; default: break; @@ -689,7 +689,7 @@ bool Tonuino::adminMenuAllowed() { } mp3.waitForTrackToFinish(); mp3.playMp3FolderTrack(b); - Serial.println(c); + LOG(admin_log, s_info, c); return (voiceMenu(255, mp3Tracks::t_0, mp3Tracks::t_0, false) == c); } } @@ -699,11 +699,12 @@ bool Tonuino::adminMenuAllowed() { } void Tonuino::adminMenu() { + LOG(admin_log, s_info, F("=== adminMenu()")); + waitForCardRemoved(); disableStandbyTimer(); mp3.pause(); - Serial.println(F("=== adminMenu()")); knownCard = false; const int subMenu = voiceMenu(13, mp3Tracks::t_900_admin, mp3Tracks::t_900_admin, false, false, 0, true); @@ -795,7 +796,7 @@ void Tonuino::voiceMenuPlayOption( uint8_t returnValue , mp3Tracks messageOffset , bool preview , int previewFromFolder) { - Serial.println(returnValue); + LOG(menu_log, s_info, returnValue); //mp3.pause(); mp3.playMp3FolderTrack(messageOffset + returnValue); if (preview) { @@ -819,9 +820,7 @@ uint8_t Tonuino::voiceMenu( int numberOfOptions if (startMessage != mp3Tracks::t_0) mp3.playMp3FolderTrack(startMessage); - Serial.print(F("=== voiceMenu() (")); - Serial.print(numberOfOptions); - Serial.println(F(" Options)")); + LOG(menu_log, s_info, F("=== voiceMenu() ("), numberOfOptions, F(" Options)")); do { if (Serial.available() > 0) { @@ -840,9 +839,7 @@ uint8_t Tonuino::voiceMenu( int numberOfOptions case button::pause: if (returnValue != 0) { - Serial.print(F("=== ")); - Serial.print(returnValue); - Serial.println(F(" ===")); + LOG(menu_log, s_info, F("=== "), returnValue, F(" ===")); return returnValue; } //delay(1000); @@ -913,8 +910,7 @@ void Tonuino::createCardsForFolder() { for (uint8_t x = special; x <= special2; x++) { mp3.playMp3FolderTrack(x); tempCard.nfcFolderSettings.special = x; - Serial.print(x); - Serial.println(F(" Karte auflegen")); + LOG(card_log, s_info, x, F(" Karte auflegen")); writeCard(tempCard); } @@ -936,9 +932,9 @@ void Tonuino::shuffleQueue() { queue[i] = queue[j]; queue[j] = t; } -// Serial.println(F("Queue :")); +// LOG(menu_log, s_info, F("Queue :")); // for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1 ; x++) -// Serial.println(queue[x]); +// LOG(menu_log, s_info, queue[x]); } From 212121a33872d19ccc7a1486c1ff339af70f5276 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Sun, 19 Dec 2021 15:50:48 +0100 Subject: [PATCH 10/32] Improvements * refactor handling chip card (do more in class Chip_card) --- src/chip_card.cpp | 52 +++++++++++++++++++++++++++++++++++++------ src/chip_card.hpp | 18 ++++++++++++--- src/tonuino.cpp | 56 ++++++++++------------------------------------- src/tonuino.hpp | 6 ++--- 4 files changed, 74 insertions(+), 58 deletions(-) diff --git a/src/chip_card.cpp b/src/chip_card.cpp index 66de8a3f..ef247147 100644 --- a/src/chip_card.cpp +++ b/src/chip_card.cpp @@ -4,6 +4,8 @@ #include #include +#include "mp3.hpp" +#include "buttons.hpp" #include "constants.hpp" #include "logger.hpp" @@ -26,8 +28,10 @@ const byte trailerBlock = 7; const unsigned int removeDelay = 3; } // namespace -Chip_card::Chip_card() +Chip_card::Chip_card(Mp3 &mp3, Buttons &buttons) : mfrc522(*(new MFRC522(mfrc522_SSPin, mfrc522_RSTPin))) +, mp3(mp3) +, buttons(buttons) , cardRemovedSwitch(removeDelay) {} @@ -232,19 +236,53 @@ void Chip_card::stopCrypto1() { mfrc522.PCD_StopCrypto1(); } -bool Chip_card::newCardPresent() { +cardEvent Chip_card::getCardEvent() { byte bufferATQA[2]; byte bufferSize = sizeof(bufferATQA); MFRC522::StatusCode result = mfrc522.PICC_RequestA(bufferATQA, &bufferSize); - if(result != mfrc522.STATUS_OK) + if(result != mfrc522.STATUS_OK) { ++cardRemovedSwitch; - else + } else { cardRemovedSwitch.reset(); + mfrc522.PICC_ReadCardSerial(); + } + + if (cardRemovedSwitch.on()) { + if (not cardRemoved) { + LOG(card_log, s_info, F("Card Removed")); + cardRemoved = true; + stopCard(); + return cardEvent::removed; + } + } + else { + if (cardRemoved) { + LOG(card_log, s_info, F("Card in")); + cardRemoved = false; + return cardEvent::inserted; + } + } + return cardEvent::none; +} - return result == mfrc522.STATUS_OK && mfrc522.PICC_ReadCardSerial(); +void Chip_card::waitForCardRemoved() { + stopCrypto1(); + while (!cardRemoved) { + getCardEvent(); + } } -bool Chip_card::cardRemoved() { - return cardRemovedSwitch.on(); +void Chip_card::waitForCardInserted() { + mp3.playMp3FolderTrack(mp3Tracks::t_800_waiting_for_card); + do { + if (buttons.isBreak()) { + mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); + return; + } + getCardEvent(); + } while (cardRemoved); + } + + diff --git a/src/chip_card.hpp b/src/chip_card.hpp index dd736d69..b4051193 100644 --- a/src/chip_card.hpp +++ b/src/chip_card.hpp @@ -54,7 +54,15 @@ struct nfcTagObject { } }; +enum class cardEvent { + none, + removed, + inserted, +}; + class MFRC522; // forward declaration to not have to include it here +class Mp3; +class Buttons; class delayedSwitchOn { public: @@ -72,7 +80,7 @@ class delayedSwitchOn { class Chip_card { public: - Chip_card(); + Chip_card(Mp3 &mp3, Buttons &buttons); bool readCard ( nfcTagObject &nfcTag); bool writeCard(const nfcTagObject &nfcTag); @@ -80,13 +88,17 @@ class Chip_card { void initCard (); void stopCard (); void stopCrypto1(); - bool newCardPresent(); - bool cardRemoved(); + cardEvent getCardEvent(); + void waitForCardRemoved(); + void waitForCardInserted(); private: MFRC522 &mfrc522; + Mp3 &mp3; + Buttons &buttons; delayedSwitchOn cardRemovedSwitch; + bool cardRemoved = true; }; diff --git a/src/tonuino.cpp b/src/tonuino.cpp index d892301a..35c76a69 100644 --- a/src/tonuino.cpp +++ b/src/tonuino.cpp @@ -133,35 +133,18 @@ void Tonuino::handleButtons() { } } -void Tonuino::handleChipCard(bool no_action) { - - const bool newCardPresent = chip_card.newCardPresent(); - - if (chip_card.cardRemoved()) { - if (not cardRemoved) { - LOG(card_log, s_info, F("Card Removed")); - cardRemoved = true; - chip_card.stopCard(); - if (settings.pauseWhenCardRemoved) { - if (not activeModifier->handlePause() && mp3.isPlaying()) { - mp3.pause(); - setStandbyTimer(); - } - } - } - return; - } - else { - if (cardRemoved) { - LOG(card_log, s_info, F("Card in")); - cardRemoved = false; - } - else { - return; +void Tonuino::handleChipCard() { + + const cardEvent ce = chip_card.getCardEvent(); + + if (settings.pauseWhenCardRemoved && (ce == cardEvent::removed)) { + if (not activeModifier->handlePause() && mp3.isPlaying()) { + mp3.pause(); + setStandbyTimer(); } } - if (no_action || !newCardPresent) + if (ce != cardEvent::inserted) return; // RFID Karte wurde aufgelegt @@ -198,23 +181,8 @@ void Tonuino::handleChipCard(bool no_action) { chip_card.stopCrypto1(); } -void Tonuino::waitForCardRemoved() { - chip_card.stopCrypto1(); - while (!cardRemoved) { - handleChipCard(true); - } -} - void Tonuino::writeCard(const nfcTagObject &nfcTag) { - - mp3.playMp3FolderTrack(mp3Tracks::t_800_waiting_for_card); - do { - if (buttons.isBreak()) { - mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); - return; - } - handleChipCard(true); - } while (cardRemoved); + chip_card.waitForCardInserted(); LOG(card_log, s_info, F("schreibe Karte...")); if (chip_card.writeCard(nfcTag)) @@ -223,7 +191,7 @@ void Tonuino::writeCard(const nfcTagObject &nfcTag) { mp3.playMp3FolderTrack(mp3Tracks::t_401_error); mp3.waitForTrackToFinish(); - waitForCardRemoved(); + chip_card.waitForCardRemoved(); } void Tonuino::playFolder() { @@ -701,7 +669,7 @@ bool Tonuino::adminMenuAllowed() { void Tonuino::adminMenu() { LOG(admin_log, s_info, F("=== adminMenu()")); - waitForCardRemoved(); + chip_card.waitForCardRemoved(); disableStandbyTimer(); mp3.pause(); diff --git a/src/tonuino.hpp b/src/tonuino.hpp index 02697796..9eb02c9e 100644 --- a/src/tonuino.hpp +++ b/src/tonuino.hpp @@ -32,8 +32,7 @@ class Tonuino { uint8_t getCurrentTrack() const; void handleButtons (); - void handleChipCard(bool no_action = false); - void waitForCardRemoved(); + void handleChipCard(); void writeCard(const nfcTagObject &nfcTag); void checkStandbyAtMillis(); @@ -75,7 +74,7 @@ class Tonuino { Settings settings {}; Mp3 mp3 {settings}; Buttons buttons {settings}; - Chip_card chip_card {}; + Chip_card chip_card {mp3, buttons}; Modifier noneModifier {*this, mp3, settings}; @@ -97,7 +96,6 @@ class Tonuino { unsigned long standbyAtMillis = 0; bool knownCard = false; - bool cardRemoved = true; nfcTagObject myCard; const folderSettings *myFolder = &myCard.nfcFolderSettings; From 2f18600fa416757851e45caed071ad5e652e7a40 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Sun, 19 Dec 2021 19:37:24 +0100 Subject: [PATCH 11/32] Improvements * change some loggings to save program space --- src/chip_card.cpp | 33 +++++++------ src/logger.hpp | 2 +- src/modifier.cpp | 23 +++++---- src/modifier.hpp | 28 +++++------ src/settings.cpp | 6 +-- src/tonuino.cpp | 119 ++++++++++++++++++++++++---------------------- src/tonuino.hpp | 1 - 7 files changed, 106 insertions(+), 106 deletions(-) diff --git a/src/chip_card.cpp b/src/chip_card.cpp index ef247147..cc6b361c 100644 --- a/src/chip_card.cpp +++ b/src/chip_card.cpp @@ -13,12 +13,14 @@ namespace { /** Helper routine to dump a byte array as hex values to Serial. */ -void dump_byte_array(byte * buffer, byte bufferSize) { +String dump_byte_array(byte * buffer, byte bufferSize) { + String res((char *)0); + res.reserve(3*bufferSize); for (byte i = 0; i < bufferSize; i++) { - Serial.print(buffer[i] < 0x10 ? " 0" : " "); - Serial.print(buffer[i], HEX); + res += String(buffer[i] < 0x10 ? " 0" : " "); + res += String(buffer[i], HEX); } - Serial.println(); + return res; } MFRC522::MIFARE_Key key{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; @@ -37,8 +39,7 @@ Chip_card::Chip_card(Mp3 &mp3, Buttons &buttons) bool Chip_card::readCard(nfcTagObject &nfcTag) { // Show some details of the PICC (that is: the tag/card) - LOG(card_log, s_info, F("Card UID:")); - dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); + LOG(card_log, s_info, F("Card UID: "), dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size)); MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); LOG(card_log, s_info, F("PICC type: "), mfrc522.PICC_GetTypeName(piccType)); @@ -50,7 +51,7 @@ bool Chip_card::readCard(nfcTagObject &nfcTag) { (piccType == MFRC522::PICC_TYPE_MIFARE_1K ) || (piccType == MFRC522::PICC_TYPE_MIFARE_4K ) ) { - LOG(card_log, s_info, F("Authenticating Classic using key A...")); + LOG(card_log, s_info, F("Auth Classic using key A...")); status = mfrc522.PCD_Authenticate( MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); } @@ -59,12 +60,12 @@ bool Chip_card::readCard(nfcTagObject &nfcTag) { byte pACK[] = {0, 0}; //16 bit PassWord ACK returned by the tempCard // Authenticate using key A - LOG(card_log, s_info, F("Authenticating MIFARE UL...")); + LOG(card_log, s_info, F("Auth MIFARE UL...")); status = mfrc522.PCD_NTAG216_AUTH(key.keyByte, pACK); } if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, F("PCD_Authenticate() failed: "), mfrc522.GetStatusCodeName(status)); + LOG(card_log, s_error, F("PCD_Auth() failed: "), mfrc522.GetStatusCodeName(status)); return false; } @@ -119,8 +120,7 @@ bool Chip_card::readCard(nfcTagObject &nfcTag) { memcpy(buffer + 12, buffer2, 4); } - LOG(card_log, s_info, F("Data on Card: ")); - dump_byte_array(buffer, 16); + LOG(card_log, s_info, F("Data on Card: "), dump_byte_array(buffer, 16)); uint32_t tempCookie; tempCookie = (uint32_t)buffer[0] << 24; @@ -160,7 +160,7 @@ bool Chip_card::writeCard(const nfcTagObject &nfcTag) { (mifareType == MFRC522::PICC_TYPE_MIFARE_1K ) || (mifareType == MFRC522::PICC_TYPE_MIFARE_4K ) ) { - LOG(card_log, s_info, F("Authenticating again using key A...")); + LOG(card_log, s_info, F("Auth again using key A...")); status = mfrc522.PCD_Authenticate( MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); } @@ -169,18 +169,17 @@ bool Chip_card::writeCard(const nfcTagObject &nfcTag) { byte pACK[] = {0, 0}; //16 bit PassWord ACK returned by the NFCtag // Authenticate using key A - LOG(card_log, s_info, F("Authenticating UL...")); + LOG(card_log, s_info, F("Auth UL...")); status = mfrc522.PCD_NTAG216_AUTH(key.keyByte, pACK); } if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, F("PCD_Authenticate() failed: "), mfrc522.GetStatusCodeName(status)); + LOG(card_log, s_error, F("PCD_Auth() failed: "), mfrc522.GetStatusCodeName(status)); return false; } // Write data to the block - LOG(card_log, s_info, F("Writing data ...")); - dump_byte_array(buffer, 16); + LOG(card_log, s_info, F("Writing data: "), dump_byte_array(buffer, 16)); if ((mifareType == MFRC522::PICC_TYPE_MIFARE_MINI ) || (mifareType == MFRC522::PICC_TYPE_MIFARE_1K ) || @@ -258,7 +257,7 @@ cardEvent Chip_card::getCardEvent() { } else { if (cardRemoved) { - LOG(card_log, s_info, F("Card in")); + LOG(card_log, s_info, F("Card Inserted")); cardRemoved = false; return cardEvent::inserted; } diff --git a/src/logger.hpp b/src/logger.hpp index 6eaaf005..fbbd3199 100644 --- a/src/logger.hpp +++ b/src/logger.hpp @@ -3,7 +3,7 @@ #include "log.hpp" -DEFINE_LOGGER(tonuino_log , s_info, void); +DEFINE_LOGGER(tonuino_log , s_debug, void); DEFINE_LOGGER(init_log , s_info, tonuino_log); DEFINE_LOGGER(card_log , s_info, tonuino_log); diff --git a/src/modifier.cpp b/src/modifier.cpp index 28e9a2ff..bb5fc768 100644 --- a/src/modifier.cpp +++ b/src/modifier.cpp @@ -7,7 +7,7 @@ void SleepTimer::loop() { if (sleepAtMillis != 0 && millis() > sleepAtMillis) { - LOG(modifier_log, s_info, F("=== SleepTimer::loop() -> SLEEP!")); + LOG(modifier_log, s_info, F("= SleepTimer::loop() -> SLEEP!")); mp3.pause(); tonuino.setStandbyTimer(); tonuino.resetActiveModifier(); @@ -15,14 +15,14 @@ void SleepTimer::loop() { } void SleepTimer::start(uint8_t minutes) { - LOG(modifier_log, s_info, F("=== SleepTimer(), minutes: "), minutes); + LOG(modifier_log, s_info, F("= SleepTimer(), minutes: "), minutes); sleepAtMillis = millis() + minutes * 60000; //playAdvertisement(302); } void FreezeDance::loop() { if (nextStopAtMillis != 0 && millis() > nextStopAtMillis) { - LOG(modifier_log, s_info, F("== FreezeDance::loop() -> FREEZE!")); + LOG(modifier_log, s_info, F("= FreezeDance::loop() -> FREEZE!")); if (mp3.isPlaying()) { mp3.playAdvertisement(301); } @@ -32,13 +32,13 @@ void FreezeDance::loop() { void FreezeDance::setNextStopAtMillis() { const uint16_t seconds = random(minSecondsBetweenStops, maxSecondsBetweenStops + 1); - LOG(modifier_log, s_info, F("=== FreezeDance::setNextStopAtMillis(), seconds: "), seconds); + LOG(modifier_log, s_info, F("= FreezeDance::setNextStopAtMillis(), seconds: "), seconds); nextStopAtMillis = millis() + seconds * 1000; } bool KindergardenMode::handleNext() { - LOG(modifier_log, s_info, F("== KindergardenMode::handleNext() -> NEXT")); if (cardQueued) { + LOG(modifier_log, s_info, F("= KindergardenMode::handleNext() -> NEXT")); cardQueued = false; tonuino.setCard(nextCard); @@ -48,8 +48,8 @@ bool KindergardenMode::handleNext() { } return false; } -bool KindergardenMode::handleRFID(const nfcTagObject &newCard) { // lot of work to do! - LOG(modifier_log, s_info, F("== KindergardenMode::handleRFID() -> queued!")); +bool KindergardenMode::handleRFID(const nfcTagObject &newCard) { + LOG(modifier_log, s_info, F("= KindergardenMode::handleRFID() -> queued!")); nextCard = newCard; cardQueued = true; if (!mp3.isPlaying()) { @@ -59,14 +59,13 @@ bool KindergardenMode::handleRFID(const nfcTagObject &newCard) { // lot of work } bool RepeatSingleModifier::handleNext() { - LOG(modifier_log, s_info, F("== RepeatSingleModifier::handleNext() -> REPEAT CURRENT TRACK")); + LOG(modifier_log, s_debug, F("= RepeatSingleModifier::handleNext() -> REPEAT CURRENT TRACK")); delay(50); if (!mp3.isPlaying()) { mp3.loop(); // this will call Mp3Notify::OnPlayFinished() but will be blocked by lastTrackFinished Mp3Notify::ResetLastTrackFinished(); tonuino.playCurrentTrack(); } - return true; } @@ -76,7 +75,7 @@ bool RepeatSingleModifier::handleNext() { // } else { // playAdvertisement(volume, false); // } -// LOG(modifier_log, s_info, F("== FeedbackModifier::handleVolumeDown()!")); +// LOG(modifier_log, s_info, F("= FeedbackModifier::handleVolumeDown()!")); // return false; //} //bool FeedbackModifier::handleVolumeUp() { @@ -85,11 +84,11 @@ bool RepeatSingleModifier::handleNext() { // } else { // playAdvertisement(volume, false); // } -// LOG(modifier_log, s_info, F("== FeedbackModifier::handleVolumeUp()!")); +// LOG(modifier_log, s_info, F("= FeedbackModifier::handleVolumeUp()!")); // return false; //} //bool FeedbackModifier::handleRFID(const nfcTagObject &/*newCard*/) { -// LOG(modifier_log, s_info, F("== FeedbackModifier::handleRFID()")); +// LOG(modifier_log, s_info, F("= FeedbackModifier::handleRFID()")); // return false; //} diff --git a/src/modifier.hpp b/src/modifier.hpp index afa87633..6a242e39 100644 --- a/src/modifier.hpp +++ b/src/modifier.hpp @@ -62,13 +62,13 @@ class FreezeDance: public Modifier { class Locked: public Modifier { public: Locked(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} - bool handlePause () final { LOG(modifier_log, s_info, F("== Locked::handlePause() -> LOCKED!")) ; return true; } - bool handleNextButton () final { LOG(modifier_log, s_info, F("== Locked::handleNextButton() -> LOCKED!")) ; return true; } - bool handlePreviousButton() final { LOG(modifier_log, s_info, F("== Locked::handlePreviousButton() -> LOCKED!")); return true; } - bool handleVolumeUp () final { LOG(modifier_log, s_info, F("== Locked::handleVolumeUp() -> LOCKED!")) ; return true; } - bool handleVolumeDown () final { LOG(modifier_log, s_info, F("== Locked::handleVolumeDown() -> LOCKED!")) ; return true; } + bool handlePause () final { LOG(modifier_log, s_debug, F("= Locked::handlePause() -> LOCKED!")) ; return true; } + bool handleNextButton () final { LOG(modifier_log, s_debug, F("= Locked::handleNextButton() -> LOCKED!")) ; return true; } + bool handlePreviousButton() final { LOG(modifier_log, s_debug, F("= Locked::handlePreviousButton() -> LOCKED!")); return true; } + bool handleVolumeUp () final { LOG(modifier_log, s_debug, F("= Locked::handleVolumeUp() -> LOCKED!")) ; return true; } + bool handleVolumeDown () final { LOG(modifier_log, s_debug, F("= Locked::handleVolumeDown() -> LOCKED!")) ; return true; } bool handleRFID(const nfcTagObject&) - final { LOG(modifier_log, s_info, F("== Locked::handleRFID() -> LOCKED!")) ; return true; } + final { LOG(modifier_log, s_debug, F("= Locked::handleRFID() -> LOCKED!")) ; return true; } mode_t getActive() final { return mode_t::locked; } }; @@ -76,11 +76,11 @@ class Locked: public Modifier { class ToddlerMode: public Modifier { public: ToddlerMode(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} - bool handlePause () final { LOG(modifier_log, s_info, F("== ToddlerMode::handlePause() -> LOCKED!")) ; return true; } - bool handleNextButton () final { LOG(modifier_log, s_info, F("== ToddlerMode::handleNextButton() -> LOCKED!")) ; return true; } - bool handlePreviousButton() final { LOG(modifier_log, s_info, F("== ToddlerMode::handlePreviousButton() -> LOCKED!")); return true; } - bool handleVolumeUp () final { LOG(modifier_log, s_info, F("== ToddlerMode::handleVolumeUp() -> LOCKED!")) ; return true; } - bool handleVolumeDown () final { LOG(modifier_log, s_info, F("== ToddlerMode::handleVolumeDown() -> LOCKED!")) ; return true; } + bool handlePause () final { LOG(modifier_log, s_debug, F("= ToddlerMode::handlePause() -> LOCKED!")) ; return true; } + bool handleNextButton () final { LOG(modifier_log, s_debug, F("= ToddlerMode::handleNextButton() -> LOCKED!")) ; return true; } + bool handlePreviousButton() final { LOG(modifier_log, s_debug, F("= ToddlerMode::handlePreviousButton() -> LOCKED!")); return true; } + bool handleVolumeUp () final { LOG(modifier_log, s_debug, F("= ToddlerMode::handleVolumeUp() -> LOCKED!")) ; return true; } + bool handleVolumeDown () final { LOG(modifier_log, s_debug, F("= ToddlerMode::handleVolumeDown() -> LOCKED!")) ; return true; } mode_t getActive() final { return mode_t::toddler; } }; @@ -90,9 +90,9 @@ class KindergardenMode: public Modifier { KindergardenMode(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} bool handleNext() final; -//bool handlePause () final { LOG(modifier_log, s_info, F("== KindergardenMode::handlePause() -> LOCKED!")) ; return true; } - bool handleNextButton () final { LOG(modifier_log, s_info, F("== KindergardenMode::handleNextButton() -> LOCKED!")) ; return true; } - bool handlePreviousButton() final { LOG(modifier_log, s_info, F("== KindergardenMode::handlePreviousButton() -> LOCKED!")); return true; } +//bool handlePause () final { LOG(modifier_log, s_debug, F("= KindergardenMode::handlePause() -> LOCKED!")) ; return true; } + bool handleNextButton () final { LOG(modifier_log, s_debug, F("= KindergardenMode::handleNextButton() -> LOCKED!")) ; return true; } + bool handlePreviousButton() final { LOG(modifier_log, s_debug, F("= KindergardenMode::handlePreviousButton() -> LOCKED!")); return true; } bool handleRFID(const nfcTagObject &newCard) final; mode_t getActive () final { return mode_t::kindergarden; } diff --git a/src/settings.cpp b/src/settings.cpp index d69905f9..c878dcf7 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -27,12 +27,12 @@ void Settings::clearEEPROM() { } void Settings::writeSettingsToFlash() { - LOG(settings_log, s_info, F("=== writeSettingsToFlash()")); + LOG(settings_log, s_debug, F("=== writeSettingsToFlash()")); EEPROM.put(startAddressAdminSettings, *this); } void Settings::resetSettings() { - LOG(settings_log, s_info, F("=== resetSettings()")); + LOG(settings_log, s_debug, F("=== resetSettings()")); cookie = cardCookie; version = 2; maxVolume = 25; @@ -71,7 +71,7 @@ void Settings::migrateSettings(int oldVersion) { } void Settings::loadSettingsFromFlash() { - LOG(settings_log, s_info, F("=== loadSettingsFromFlash()")); + LOG(settings_log, s_debug, F("=== loadSettingsFromFlash()")); EEPROM.get(startAddressAdminSettings, *this); if (cookie != cardCookie) resetSettings(); diff --git a/src/tonuino.cpp b/src/tonuino.cpp index 35c76a69..fbf81599 100644 --- a/src/tonuino.cpp +++ b/src/tonuino.cpp @@ -9,6 +9,15 @@ #include "constants.hpp" #include "logger.hpp" +namespace { +template +void swap(T &lhs, T &rhs) { + const T t = lhs; + lhs = rhs; + rhs = t; +} +} + Tonuino tonuino; void Tonuino::setup() { @@ -161,7 +170,7 @@ void Tonuino::handleChipCard() { } setCard(tempCard); - LOG(card_log, s_info, F("folder: "), myCard.nfcFolderSettings.folder); + LOG(card_log, s_info, F("Folder: "), myCard.nfcFolderSettings.folder); if (myCard.cookie == cardCookie && myCard.nfcFolderSettings.mode != mode_t::none) { knownCard = false; // prevent nextTrack() when calling Mp3Notify::OnPlayFinished() in mp3.waitForTrackToFinish(); @@ -195,38 +204,38 @@ void Tonuino::writeCard(const nfcTagObject &nfcTag) { } void Tonuino::playFolder() { - LOG(play_log, s_info, F("== playFolder()")); + LOG(play_log, s_debug, F("= playFolder()")); disableStandbyTimer(); knownCard = true; numTracksInFolder = mp3.getFolderTrackCount(myFolder->folder); firstTrack = 1; - LOG(play_log, s_info, numTracksInFolder, F(" Dateien in Ordner "), myFolder->folder); + LOG(play_log, s_info, numTracksInFolder, F(" Dateien im Ordner "), myFolder->folder); switch (myFolder->mode) { case mode_t::hoerspiel: // Hörspielmodus: eine zufällige Datei aus dem Ordner - LOG(play_log, s_info, F("Hörspielmodus")); + LOG(play_log, s_info, F("Hörspiel")); currentTrack = random(1, numTracksInFolder + 1); LOG(play_log, s_info, F("Track: "), currentTrack); break; case mode_t::album: // Album Modus: kompletten Ordner spielen - LOG(play_log, s_info, F("Album Modus")); + LOG(play_log, s_info, F("Album")); currentTrack = 1; break; case mode_t::party: // Party Modus: Ordner in zufälliger Reihenfolge - LOG(play_log, s_info, F("Party Modus")); + LOG(play_log, s_info, F("Party")); shuffleQueue(); currentTrack = 1; break; case mode_t::einzel: // Einzel Modus: eine Datei aus dem Ordner abspielen - LOG(play_log, s_info, F("Einzel Modus")); + LOG(play_log, s_info, F("Einzel")); currentTrack = myFolder->special; LOG(play_log, s_info, F("Track: "), currentTrack); break; @@ -234,7 +243,7 @@ void Tonuino::playFolder() { case mode_t::hoerbuch: case mode_t::hoerbuch_1: // Hörbuch Modus: kompletten Ordner spielen und Fortschritt merken (oder nur eine Datei) - LOG(play_log, s_info, F("Hörbuch Modus")); + LOG(play_log, s_info, F("Hörbuch")); currentTrack = settings.readFolderSettingFromFlash(myFolder->folder); if (currentTrack == 0 || currentTrack > numTracksInFolder) { currentTrack = 1; @@ -244,7 +253,7 @@ void Tonuino::playFolder() { case mode_t::hoerspiel_vb: // Spezialmodus Von-Bin: Hörspiel: eine zufällige Datei aus dem Ordner - LOG(play_log, s_info, F("Spezialmodus Von-Bin: Hörspiel")); + LOG(play_log, s_info, F("Von-Bis Hörspiel")); LOG(play_log, s_info, myFolder->special, F(" bis "), myFolder->special2); firstTrack = myFolder->special; numTracksInFolder = myFolder->special2; @@ -254,7 +263,7 @@ void Tonuino::playFolder() { case mode_t::album_vb: // Spezialmodus Von-Bis: Album: alle Dateien zwischen Start und Ende spielen - LOG(play_log, s_info, F("Spezialmodus Von-Bis: Album")); + LOG(play_log, s_info, F("Von-Bis Album")); LOG(play_log, s_info, myFolder->special, F(" bis "), myFolder->special2); firstTrack = myFolder->special; numTracksInFolder = myFolder->special2; @@ -263,7 +272,7 @@ void Tonuino::playFolder() { case mode_t::party_vb: // Spezialmodus Von-Bis: Party Ordner in zufälliger Reihenfolge - LOG(play_log, s_info, F("Spezialmodus Von-Bis: Party")); + LOG(play_log, s_info, F("Von-Bis Party")); LOG(play_log, s_info, myFolder->special, F(" bis "), myFolder->special2); firstTrack = myFolder->special; numTracksInFolder = myFolder->special2; @@ -281,13 +290,13 @@ void Tonuino::playFolder() { } void Tonuino::playShortCut(uint8_t shortCut) { - LOG(play_log, s_info, F("=== playShortCut(): "),shortCut); + LOG(play_log, s_info, F("= playShortCut(): "),shortCut); if (settings.shortCuts[shortCut].folder != 0) { setFolder(&settings.shortCuts[shortCut]); playFolder(); delay(1000); } else { - LOG(play_log, s_info, F("Shortcut not configured!")); + LOG(play_log, s_info, F("No shortcut")); if (not knownCard) mp3.playMp3FolderTrack(mp3Tracks::t_262_pling); } @@ -303,13 +312,13 @@ void Tonuino::nextTrack() { // verarbeitet werden return; - LOG(play_log, s_info, F("=== nextTrack()")); + LOG(play_log, s_info, F("= nextTrack()")); switch (myFolder->mode) { case mode_t::hoerspiel : case mode_t::hoerspiel_vb: if (not mp3.isPlaying()) { - LOG(play_log, s_info, F("Hörspielmodus ist aktiv -> keinen neuen Track")); + LOG(play_log, s_info, F("Hörspiel -> stop")); knownCard = false; setStandbyTimer(); //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! @@ -321,7 +330,7 @@ void Tonuino::nextTrack() { if (currentTrack != numTracksInFolder) { ++currentTrack; mp3.playFolderTrack(myFolder->folder, currentTrack); - LOG(play_log, s_info, F("Albummodus ist aktiv -> nächster Track: "), currentTrack); + LOG(play_log, s_info, F("Album -> nächster Track: "), currentTrack); } else { // mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! knownCard = false; @@ -335,7 +344,7 @@ void Tonuino::nextTrack() { LOG(play_log, s_info, F("Party -> weiter in der Queue ")); ++currentTrack; } else { - LOG(play_log, s_info, F("Ende der Queue -> beginne von vorne")); + LOG(play_log, s_info, F("Party, Ende der Queue -> beginne von vorne")); currentTrack = 1; //// Wenn am Ende der Queue neu gemischt werden soll bitte die Zeilen wieder aktivieren //LOG(play_log, s_info, F("Ende der Queue -> mische neu")); @@ -346,7 +355,7 @@ void Tonuino::nextTrack() { break; case mode_t::einzel: - LOG(play_log, s_info, F("Einzel Modus aktiv -> stop")); + LOG(play_log, s_info, F("Einzel -> stop")); //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! knownCard = false; setStandbyTimer(); @@ -355,13 +364,13 @@ void Tonuino::nextTrack() { case mode_t::hoerbuch: if (currentTrack != numTracksInFolder) { ++currentTrack; - LOG(play_log, s_info, F("Hörbuch Modus ist aktiv -> nächster Track und Fortschritt speichern")); + LOG(play_log, s_info, F("Hörbuch -> nächster Track und Fortschr. speichern")); LOG(play_log, s_info, F("Track: "), currentTrack); mp3.playFolderTrack(myFolder->folder, currentTrack); // Fortschritt im EEPROM abspeichern settings.writeFolderSettingToFlash(myFolder->folder, currentTrack); } else { - LOG(play_log, s_info, F("Hörbuch Modus ist aktiv -> Ende")); + LOG(play_log, s_info, F("Hörbuch -> Ende")); //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! // Fortschritt zurück setzen settings.writeFolderSettingToFlash(myFolder->folder, 1); @@ -375,7 +384,7 @@ void Tonuino::nextTrack() { else currentTrack = 1; - LOG(play_log, s_info, F("Hörbuch Modus single ist aktiv -> Fortschritt speichern und beenden")); + LOG(play_log, s_info, F("Hörbuch single -> Fortschritt speichern und beenden")); LOG(play_log, s_info, F("Track: "), currentTrack); // Fortschritt im EEPROM abspeichern settings.writeFolderSettingToFlash(myFolder->folder, currentTrack); @@ -390,18 +399,18 @@ void Tonuino::nextTrack() { } void Tonuino::previousTrack() { - LOG(play_log, s_info, F("=== previousTrack()")); + LOG(play_log, s_info, F("= previousTrack()")); switch (myFolder->mode) { case mode_t::hoerspiel: case mode_t::hoerspiel_vb: - LOG(play_log, s_info, F("Hörspielmodus ist aktiv -> Track von vorne spielen")); + LOG(play_log, s_info, F("Hörspiel -> Track von vorne spielen")); mp3.playFolderTrack(myFolder->folder, currentTrack); break; case mode_t::album: case mode_t::album_vb: - LOG(play_log, s_info, F("Albummodus ist aktiv -> vorheriger Track")); + LOG(play_log, s_info, F("Album -> vorheriger Track")); if (currentTrack != firstTrack) { --currentTrack; } @@ -411,10 +420,10 @@ void Tonuino::previousTrack() { case mode_t::party: case mode_t::party_vb: if (currentTrack != 1) { - LOG(play_log, s_info, F("Party Modus ist aktiv -> zurück in der Qeueue ")); + LOG(play_log, s_info, F("Party -> zurück in der Qeueue ")); --currentTrack; } else { - LOG(play_log, s_info, F("Party Modus, Anfang der Queue -> springe ans Ende ")); + LOG(play_log, s_info, F("Party, Anfang der Queue -> springe ans Ende ")); currentTrack = numTracksInFolder - firstTrack + 1; } LOG(play_log, s_info, F("Track: "), queue[currentTrack - 1]); @@ -422,13 +431,13 @@ void Tonuino::previousTrack() { break; case mode_t::einzel: - LOG(play_log, s_info, F("Einzel Modus aktiv -> Track von vorne spielen")); + LOG(play_log, s_info, F("Einzel -> Track von vorne spielen")); mp3.playFolderTrack(myFolder->folder, currentTrack); break; case mode_t::hoerbuch: case mode_t::hoerbuch_1: - LOG(play_log, s_info, F("Hörbuch Modus ist aktiv -> vorheriger Track und Fortschritt speichern")); + LOG(play_log, s_info, F("Hörbuch -> vorheriger Track und Fortschr. speichern")); if (currentTrack != 1) { --currentTrack; } @@ -444,7 +453,7 @@ void Tonuino::previousTrack() { // Funktionen für den Standby Timer (z.B. über Pololu-Switch oder Mosfet) void Tonuino::setStandbyTimer() { - LOG(standby_log, s_info, F("=== setStandbyTimer()")); + LOG(standby_log, s_info, F("= setStandbyTimer()")); if (settings.standbyTimer != 0) standbyAtMillis = millis() + (settings.standbyTimer * 60 * 1000); else @@ -453,13 +462,13 @@ void Tonuino::setStandbyTimer() { } void Tonuino::disableStandbyTimer() { - LOG(standby_log, s_info, F("=== disablestandby()")); + LOG(standby_log, s_info, F("= disablestandby()")); standbyAtMillis = 0; } void Tonuino::checkStandbyAtMillis() { if (standbyAtMillis != 0 && millis() > standbyAtMillis) { - LOG(standby_log, s_info, F("=== power off!")); + LOG(standby_log, s_info, F("power off!")); // enter sleep state digitalWrite(shutdownPin, getLevel(shutdownPinType, level::active)); delay(500); @@ -486,7 +495,7 @@ void Tonuino::volumeUpButton() { if (activeModifier->handleVolumeUp()) return; - LOG(cmd_log, s_info, F("=== volumeUp()")); + LOG(cmd_log, s_debug, F("= volumeUp()")); mp3.increaseVolume(); } @@ -494,7 +503,7 @@ void Tonuino::volumeDownButton() { if (activeModifier->handleVolumeDown()) return; - LOG(cmd_log, s_info, F("=== volumeDown()")); + LOG(cmd_log, s_debug, F("= volumeDown()")); mp3.decreaseVolume(); } @@ -502,7 +511,7 @@ void Tonuino::nextButton() { if (activeModifier->handleNextButton()) return; - LOG(cmd_log, s_info, F("=== next()")); + LOG(cmd_log, s_debug, F("= next()")); nextTrack(); } @@ -510,7 +519,7 @@ void Tonuino::previousButton() { if (activeModifier->handlePreviousButton()) return; - LOG(cmd_log, s_info, F("=== previous()")); + LOG(cmd_log, s_debug, F("= previous()")); previousTrack(); } @@ -556,13 +565,8 @@ bool Tonuino::setupFolder(folderSettings& theFolder) { return true; } -void Tonuino::resetCard() { - LOG(card_log, s_info, F("Karte wird neu konfiguriert!")); - setupCard(); -} - void Tonuino::setupCard() { - LOG(card_log, s_info, F("=== setupCard()")); + LOG(card_log, s_info, F("= setupCard()")); mp3.pause(); nfcTagObject newCard; if (setupFolder(newCard.nfcFolderSettings)) @@ -583,7 +587,7 @@ bool Tonuino::specialCard(const nfcTagObject &nfcTag) { if (nfcTag.nfcFolderSettings.folder != 0) return false; - //LOG(card_log, s_info, F("special card, mode = "), static_cast(nfcTag.nfcFolderSettings.mode)); + LOG(card_log, s_debug, F("special card, mode = "), static_cast(nfcTag.nfcFolderSettings.mode)); if (activeModifier->getActive() == nfcTag.nfcFolderSettings.mode) { resetActiveModifier(); LOG(card_log, s_info, F("modifier removed")); @@ -657,7 +661,7 @@ bool Tonuino::adminMenuAllowed() { } mp3.waitForTrackToFinish(); mp3.playMp3FolderTrack(b); - LOG(admin_log, s_info, c); + LOG(admin_log, s_info, F("Result: "), c); return (voiceMenu(255, mp3Tracks::t_0, mp3Tracks::t_0, false) == c); } } @@ -667,7 +671,7 @@ bool Tonuino::adminMenuAllowed() { } void Tonuino::adminMenu() { - LOG(admin_log, s_info, F("=== adminMenu()")); + LOG(admin_log, s_info, F("= adminMenu()")); chip_card.waitForCardRemoved(); @@ -678,9 +682,11 @@ void Tonuino::adminMenu() { const int subMenu = voiceMenu(13, mp3Tracks::t_900_admin, mp3Tracks::t_900_admin, false, false, 0, true); switch (subMenu) { - case 0: setStandbyTimer(); + case 0: //break + setStandbyTimer(); return; - case 1: resetCard(); + case 1: // create new card + setupCard(); chip_card.stopCard(); break; case 2: // Maximum Volume @@ -764,8 +770,7 @@ void Tonuino::voiceMenuPlayOption( uint8_t returnValue , mp3Tracks messageOffset , bool preview , int previewFromFolder) { - LOG(menu_log, s_info, returnValue); - //mp3.pause(); + LOG(menu_log, s_info, F("playOption for: "), returnValue); mp3.playMp3FolderTrack(messageOffset + returnValue); if (preview) { mp3.waitForTrackToFinish(); @@ -788,7 +793,7 @@ uint8_t Tonuino::voiceMenu( int numberOfOptions if (startMessage != mp3Tracks::t_0) mp3.playMp3FolderTrack(startMessage); - LOG(menu_log, s_info, F("=== voiceMenu() ("), numberOfOptions, F(" Options)")); + LOG(menu_log, s_info, F("= voiceMenu() ("), numberOfOptions, F(" Options)")); do { if (Serial.available() > 0) { @@ -807,7 +812,7 @@ uint8_t Tonuino::voiceMenu( int numberOfOptions case button::pause: if (returnValue != 0) { - LOG(menu_log, s_info, F("=== "), returnValue, F(" ===")); + LOG(menu_log, s_info, F("voiceMenu return: "), returnValue); return returnValue; } //delay(1000); @@ -878,7 +883,7 @@ void Tonuino::createCardsForFolder() { for (uint8_t x = special; x <= special2; x++) { mp3.playMp3FolderTrack(x); tempCard.nfcFolderSettings.special = x; - LOG(card_log, s_info, x, F(" Karte auflegen")); + LOG(card_log, s_info, x, F("-te Karte auflegen")); writeCard(tempCard); } @@ -886,22 +891,20 @@ void Tonuino::createCardsForFolder() { void Tonuino::shuffleQueue() { // Queue für die Zufallswiedergabe erstellen - for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1; x++) + for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1; ++x) queue[x] = x + firstTrack; // Rest mit 0 auffüllen - for (uint8_t x = numTracksInFolder - firstTrack + 1; x < maxTracksInFolder; x++) + for (uint8_t x = numTracksInFolder - firstTrack + 1; x < maxTracksInFolder; ++x) queue[x] = 0; // Queue mischen - for (uint8_t i = 0; i < numTracksInFolder - firstTrack + 1; i++) { + for (uint8_t i = 0; i < numTracksInFolder - firstTrack + 1; ++i) { const uint8_t j = random(0, numTracksInFolder - firstTrack + 1); - const uint8_t t = queue[i]; - queue[i] = queue[j]; - queue[j] = t; + swap(queue[i], queue[j]); } // LOG(menu_log, s_info, F("Queue :")); -// for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1 ; x++) +// for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1 ; ++x) // LOG(menu_log, s_info, queue[x]); } diff --git a/src/tonuino.hpp b/src/tonuino.hpp index 9eb02c9e..411eba2d 100644 --- a/src/tonuino.hpp +++ b/src/tonuino.hpp @@ -45,7 +45,6 @@ class Tonuino { bool setupFolder(folderSettings& theFolder); - void resetCard (); void setupCard (); bool specialCard(const nfcTagObject &nfcTag); From 87101222c9ce2d6ca3d06cdbb8444ea4186a1b7f Mon Sep 17 00:00:00 2001 From: boerge1 Date: Tue, 21 Dec 2021 17:08:30 +0100 Subject: [PATCH 12/32] Improvements * simplify logging (use constexpr function) --- src/chip_card.hpp | 2 +- src/log.hpp | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/chip_card.hpp b/src/chip_card.hpp index b4051193..9480d228 100644 --- a/src/chip_card.hpp +++ b/src/chip_card.hpp @@ -98,7 +98,7 @@ class Chip_card { Buttons &buttons; delayedSwitchOn cardRemovedSwitch; - bool cardRemoved = true; + bool cardRemoved = false; }; diff --git a/src/log.hpp b/src/log.hpp index 877cd390..d06e1568 100644 --- a/src/log.hpp +++ b/src/log.hpp @@ -8,7 +8,7 @@ { static __FlashStringHelper const* name() {return F(#Logger_);} } #define LOG(Logger_, Severity_, Expression_...) \ - if constexpr ( Logger_::will_forward::result ) \ + if constexpr ( Logger_::will_log(Severity_) ) \ Logger_::template log< Severity_ >(Logger_::name(), Expression_) enum severity { @@ -31,10 +31,9 @@ template struct is_same_type : bool_ {}; class logger { public: - template - struct will_forward { - static const bool result = true; - }; + constexpr static bool will_log(severity) { + return true; + } static void log() { Serial.println(); @@ -61,11 +60,9 @@ class logger_base { public: typedef typename if_::result, logger, FwdLogger>::result_type forward_logger_type; - template - struct will_forward { - static const bool result = Severity >= MinSeverity - && forward_logger_type::template will_forward::result; - }; + static constexpr bool will_log(severity s) { + return (s >= MinSeverity) && forward_logger_type::will_log(s); + } template static void log(const __FlashStringHelper* logname, Types ... types) { From 3f6df35838d4a287de5ae916b854739956e20b5c Mon Sep 17 00:00:00 2001 From: boerge1 Date: Wed, 22 Dec 2021 16:21:50 +0100 Subject: [PATCH 13/32] Improvements * fix buttons in voice menu (if !settings.invertVolumeButtons) --- src/buttons.cpp | 73 +++++++++++++++++++++++++------------------------ src/buttons.hpp | 20 ++++++++++++-- src/log.hpp | 2 ++ src/tonuino.cpp | 30 ++++++++++---------- 4 files changed, 73 insertions(+), 52 deletions(-) diff --git a/src/buttons.cpp b/src/buttons.cpp index 59f1016a..71e0e15c 100644 --- a/src/buttons.cpp +++ b/src/buttons.cpp @@ -27,8 +27,8 @@ Buttons::Buttons(const Settings& settings) #endif } -button Buttons::getButton() { - button ret = button::none; +buttonRaw Buttons::getButtonRaw() { + buttonRaw ret = buttonRaw::none; readButtons(); if (( buttonPause.pressedFor(buttonLongPress) || buttonUp .pressedFor(buttonLongPress) @@ -37,80 +37,83 @@ button Buttons::getButton() { && buttonPause.isPressed() && buttonUp .isPressed() && buttonDown .isPressed()) - ret = button::admin; + ret = buttonRaw::allLong; else if (buttonPause.wasReleased()) { if (not ignorePauseButton) - ret = button::pause; + ret = buttonRaw::pause; else ignorePauseButton = false; } else if (buttonPause.pressedFor(buttonLongPress) && not ignorePauseButton) { - ret = button::track; + ret = buttonRaw::pauseLong; ignorePauseButton = true; } else if (buttonUp.wasReleased()) { if (!ignoreUpButton) { - if (!settings.invertVolumeButtons) - ret = button::next; - else - ret = button::volume_up; + ret = buttonRaw::up; } else ignoreUpButton = false; } else if (buttonUp.pressedFor(buttonLongPress) && not ignoreUpButton) { -#ifndef FIVEBUTTONS - if (!settings.invertVolumeButtons) - ret = button::volume_up; - else - ret = button::next; + ret = buttonRaw::upLong; ignoreUpButton = true; -#endif } else if (buttonDown.wasReleased()) { if (!ignoreDownButton) { - if (!settings.invertVolumeButtons) - ret = button::previous; - else - ret = button::volume_down; + ret = buttonRaw::down; } else ignoreDownButton = false; } else if (buttonDown.pressedFor(buttonLongPress) && not ignoreDownButton) { -#ifndef FIVEBUTTONS - if (!settings.invertVolumeButtons) - ret = button::volume_down; - else - ret = button::previous; + ret = buttonRaw::downLong; ignoreDownButton = true; -#endif } #ifdef FIVEBUTTONS else if (buttonFour.wasReleased()) { - if (!settings.invertVolumeButtons) - ret = button::volume_up; - else - ret = button::next; + ret = buttonRaw::four; } else if (buttonFive.wasReleased()) { - if (!settings.invertVolumeButtons) - ret = button::volume_down; - else - ret = button::previous; + ret = buttonRaw::five; + } +#endif + + if (ret != buttonRaw::none) { + LOG(button_log, s_debug, F("Button raw: "), static_cast(ret)); } + return ret; +} + +buttonCmd Buttons::getButtonCmd() { + buttonCmd ret = buttonCmd::none; + buttonRaw b = getButtonRaw(); + + switch (b) { + case buttonRaw::none : ret = buttonCmd::none ; break; + case buttonRaw::pause : ret = buttonCmd::pause ; break; + case buttonRaw::pauseLong: ret = buttonCmd::track ; break; + case buttonRaw::up : ret = (!settings.invertVolumeButtons) ? buttonCmd::next : buttonCmd::volume_up ; break; + case buttonRaw::upLong : ret = (!settings.invertVolumeButtons) ? buttonCmd::volume_up : buttonCmd::next ; break; + case buttonRaw::down : ret = (!settings.invertVolumeButtons) ? buttonCmd::previous : buttonCmd::volume_down; break; + case buttonRaw::downLong : ret = (!settings.invertVolumeButtons) ? buttonCmd::volume_down : buttonCmd::previous ; break; + case buttonRaw::allLong : ret = buttonCmd::admin ; break; +#ifdef FIVEBUTTONS + case buttonRaw::four : ret = (!settings.invertVolumeButtons) ? buttonCmd::volume_up : buttonCmd::next ; break; + case buttonRaw::five : ret = (!settings.invertVolumeButtons) ? buttonCmd::volume_down : buttonCmd::previous ; break; #endif + } - if (ret != button::none) { - LOG(button_log, s_debug, F("Button: "), static_cast(ret)); + if (ret != buttonCmd::none) { + LOG(button_log, s_debug, F("Button cmd: "), static_cast(ret)); } return ret; } diff --git a/src/buttons.hpp b/src/buttons.hpp index e51a7fc2..6ec23b46 100644 --- a/src/buttons.hpp +++ b/src/buttons.hpp @@ -7,7 +7,22 @@ #include "settings.hpp" #include "constants.hpp" -enum class button { +enum class buttonRaw { + none, + pause, + pauseLong, + up, + upLong, + down, + downLong, + allLong, +#ifdef FIVEBUTTONS + four, + five, +#endif +}; + +enum class buttonCmd { none, admin, pause, @@ -22,7 +37,8 @@ class Buttons { public: Buttons(const Settings& settings); - button getButton(); + buttonRaw getButtonRaw(); + buttonCmd getButtonCmd(); void waitForNoButton(); bool isReset(); bool isBreak(); diff --git a/src/log.hpp b/src/log.hpp index d06e1568..07eb8aec 100644 --- a/src/log.hpp +++ b/src/log.hpp @@ -46,6 +46,8 @@ class logger { template static void log(const __FlashStringHelper* /*logname*/, Types ... types) { +// Serial.print(millis()); +// Serial.print(F("-")); // Serial.print(getSeverityName(Severity)); // Serial.print(F("-")); // Serial.print(logname); diff --git a/src/tonuino.cpp b/src/tonuino.cpp index fbf81599..cbb04389 100644 --- a/src/tonuino.cpp +++ b/src/tonuino.cpp @@ -71,9 +71,9 @@ void Tonuino::loop() { void Tonuino::handleButtons() { - switch (buttons.getButton()) { + switch (buttons.getButtonCmd()) { - case button::admin: + case buttonCmd::admin: mp3.pause(); buttons.waitForNoButton(); if (not adminMenuAllowed()) { @@ -83,7 +83,7 @@ void Tonuino::handleButtons() { adminMenu(); break; - case button::pause: + case buttonCmd::pause: if (activeModifier->handlePause()) break; if (mp3.isPlaying()) { @@ -95,7 +95,7 @@ void Tonuino::handleButtons() { } break; - case button::track: + case buttonCmd::track: if (activeModifier->handlePause()) break; if (mp3.isPlaying()) { @@ -110,28 +110,28 @@ void Tonuino::handleButtons() { } break; - case button::volume_up: + case buttonCmd::volume_up: if (mp3.isPlaying()) volumeUpButton(); else playShortCut(1); break; - case button::next: + case buttonCmd::next: if (mp3.isPlaying()) nextButton(); else playShortCut(1); break; - case button::volume_down: + case buttonCmd::volume_down: if (mp3.isPlaying()) volumeDownButton(); else playShortCut(2); break; - case button::previous: + case buttonCmd::previous: if (mp3.isPlaying()) previousButton(); else @@ -802,15 +802,15 @@ uint8_t Tonuino::voiceMenu( int numberOfOptions return optionSerial; } mp3.loop(); - switch(buttons.getButton()) { - case button::track: + switch(buttons.getButtonRaw()) { + case buttonRaw::pauseLong: if (exitWithLongPress) { mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); return defaultValue; } break; - case button::pause: + case buttonRaw::pause: if (returnValue != 0) { LOG(menu_log, s_info, F("voiceMenu return: "), returnValue); return returnValue; @@ -818,22 +818,22 @@ uint8_t Tonuino::voiceMenu( int numberOfOptions //delay(1000); break; - case button::next: + case buttonRaw::upLong: returnValue = min(returnValue + 10, numberOfOptions); voiceMenuPlayOption(returnValue, messageOffset, preview, previewFromFolder); break; - case button::volume_up: + case buttonRaw::up: returnValue = min(returnValue + 1, numberOfOptions); voiceMenuPlayOption(returnValue, messageOffset, preview, previewFromFolder); break; - case button::previous: + case buttonRaw::downLong: returnValue = max(returnValue - 10, 1); voiceMenuPlayOption(returnValue, messageOffset, preview, previewFromFolder); break; - case button::volume_down: + case buttonRaw::down: returnValue = max(returnValue - 1, 1); voiceMenuPlayOption(returnValue, messageOffset, preview, previewFromFolder); break; From 831794250fc2777a116ffb012c3fb94b20c1f5cc Mon Sep 17 00:00:00 2001 From: boerge1 Date: Sun, 9 Jan 2022 17:27:26 +0100 Subject: [PATCH 14/32] new state machine * first draft version --- Tonuino.ino | 8 +- src/buttons.cpp | 48 +- src/buttons.hpp | 7 +- src/chip_card.cpp | 172 ++-- src/chip_card.hpp | 26 +- src/constants.hpp | 39 +- src/log.hpp | 17 +- src/logger.hpp | 1 + src/modifier.cpp | 39 +- src/modifier.hpp | 3 +- src/mp3.cpp | 26 +- src/state_machine.cpp | 1752 +++++++++++++++++++++++++++++++++++++++++ src/state_machine.hpp | 104 +++ src/timer.cpp | 27 + src/timer.hpp | 15 + src/tinyfsm.hpp | 258 ++++++ src/tonuino.cpp | 649 ++------------- src/tonuino.hpp | 55 +- src/type_traits.hpp | 17 + tinyfsm-master.zip | Bin 0 -> 26813 bytes 20 files changed, 2452 insertions(+), 811 deletions(-) create mode 100644 src/state_machine.cpp create mode 100644 src/state_machine.hpp create mode 100644 src/timer.cpp create mode 100644 src/timer.hpp create mode 100644 src/tinyfsm.hpp create mode 100644 src/type_traits.hpp create mode 100644 tinyfsm-master.zip diff --git a/Tonuino.ino b/Tonuino.ino index ff321228..13f46aed 100644 --- a/Tonuino.ino +++ b/Tonuino.ino @@ -15,6 +15,7 @@ TonUINO Version 2.1 created by Thorsten Voß and licensed under GNU/GPL. + refactored by Boerge1 Information and contribution at https://tonuino.de. */ @@ -36,14 +37,15 @@ void setup() { LOG(init_log, s_debug, F("|_ _|___ ___| | | | | | |")); LOG(init_log, s_debug, F(" | | | . | | | |- -| | | | | |")); LOG(init_log, s_debug, F(" |_| |___|_|_|_____|_____|_|___|_____|\n")); - LOG(init_log, s_debug, F("TonUINO Version 2.1")); + LOG(init_log, s_debug, F("TonUINO Version 3.0")); LOG(init_log, s_debug, F("created by Thorsten Voß and licensed under GNU/GPL.")); + LOG(init_log, s_debug, F("refactored by Boerge1.")); LOG(init_log, s_debug, F("Information and contribution at https://tonuino.de.\n")); - tonuino.setup(); + Tonuino::getTonuino().setup(); } void loop() { - tonuino.loop(); + Tonuino::getTonuino().loop(); } diff --git a/src/buttons.cpp b/src/buttons.cpp index 71e0e15c..ebcf1f7d 100644 --- a/src/buttons.cpp +++ b/src/buttons.cpp @@ -36,8 +36,9 @@ buttonRaw Buttons::getButtonRaw() { ) && buttonPause.isPressed() && buttonUp .isPressed() - && buttonDown .isPressed()) + && buttonDown .isPressed()) { ret = buttonRaw::allLong; + } else if (buttonPause.wasReleased()) { if (not ignorePauseButton) @@ -93,9 +94,8 @@ buttonRaw Buttons::getButtonRaw() { return ret; } -buttonCmd Buttons::getButtonCmd() { +buttonCmd Buttons::getButtonCmd(buttonRaw b) { buttonCmd ret = buttonCmd::none; - buttonRaw b = getButtonRaw(); switch (b) { case buttonRaw::none : ret = buttonCmd::none ; break; @@ -110,6 +110,7 @@ buttonCmd Buttons::getButtonCmd() { case buttonRaw::four : ret = (!settings.invertVolumeButtons) ? buttonCmd::volume_up : buttonCmd::next ; break; case buttonRaw::five : ret = (!settings.invertVolumeButtons) ? buttonCmd::volume_down : buttonCmd::previous ; break; #endif + case buttonRaw::start : ret = buttonCmd::start; ; break; } if (ret != buttonCmd::none) { @@ -118,22 +119,6 @@ buttonCmd Buttons::getButtonCmd() { return ret; } -void Buttons::waitForNoButton() { - do { - readButtons(); - } while ( buttonPause.isPressed() - || buttonUp .isPressed() - || buttonDown .isPressed() -#ifdef FIVEBUTTONS - || buttonFour .isPressed() - || buttonFive .isPressed() -#endif - ); - ignorePauseButton = false; - ignoreUpButton = false; - ignoreDownButton = false; -} - bool Buttons::isReset() { const int buttonActiveLevel = getLevel(buttonPinType, level::active); return (digitalRead(buttonPausePin) == buttonActiveLevel && @@ -141,31 +126,6 @@ bool Buttons::isReset() { digitalRead(buttonDownPin ) == buttonActiveLevel ); } -bool Buttons::isBreak() { - readButtons(); - if (buttonUp.wasReleased() || buttonDown.wasReleased()) { - LOG(button_log, s_info, F("Abgebrochen!")); - return true; - } - return false; -} - -bool Buttons::askCode(Settings::pin_t &code) { - uint8_t x = 0; - while (x < 4) { - readButtons(); - if (buttonPause.pressedFor(buttonLongPress)) - return false; - if (buttonPause.wasReleased()) - code[x++] = 1; - if (buttonUp.wasReleased()) - code[x++] = 2; - if (buttonDown.wasReleased()) - code[x++] = 3; - } - return true; -} - void Buttons::readButtons() { buttonPause.read(); buttonUp .read(); diff --git a/src/buttons.hpp b/src/buttons.hpp index 6ec23b46..02c31d5f 100644 --- a/src/buttons.hpp +++ b/src/buttons.hpp @@ -20,6 +20,7 @@ enum class buttonRaw { four, five, #endif + start, }; enum class buttonCmd { @@ -31,6 +32,7 @@ enum class buttonCmd { volume_down, next, previous, + start, }; class Buttons { @@ -38,11 +40,8 @@ class Buttons { Buttons(const Settings& settings); buttonRaw getButtonRaw(); - buttonCmd getButtonCmd(); - void waitForNoButton(); + buttonCmd getButtonCmd(buttonRaw b); bool isReset(); - bool isBreak(); - bool askCode(Settings::pin_t &code); private: diff --git a/src/chip_card.cpp b/src/chip_card.cpp index cc6b361c..6e215786 100644 --- a/src/chip_card.cpp +++ b/src/chip_card.cpp @@ -1,7 +1,6 @@ #include "chip_card.hpp" #include -#include #include #include "mp3.hpp" @@ -9,20 +8,44 @@ #include "constants.hpp" #include "logger.hpp" +// select whether StatusCode and PiccType are printed as names +// that uses about 690 bytes or 2.2% of flash +constexpr bool verbosePrintStatusCode = false; +constexpr bool verbosePrintPiccType = false; + namespace { + +const __FlashStringHelper* str_failed () { return F("failed: ") ; } +const __FlashStringHelper* str_MIFARE_Read() { return F("MIFARE_Read ") ; } + /** Helper routine to dump a byte array as hex values to Serial. */ -String dump_byte_array(byte * buffer, byte bufferSize) { +String dump_byte_array(byte * buffer, size_t bufferSize) { String res((char *)0); res.reserve(3*bufferSize); - for (byte i = 0; i < bufferSize; i++) { + for (byte i = 0; i < bufferSize; ++i) { res += String(buffer[i] < 0x10 ? " 0" : " "); res += String(buffer[i], HEX); } return res; } +auto printStatusCode(MFRC522& mfrc522, MFRC522::StatusCode status) { + if constexpr (verbosePrintStatusCode) + return mfrc522.GetStatusCodeName(status); + else + return static_cast(status); +} + +auto printPiccType(MFRC522& mfrc522, MFRC522::PICC_Type piccType) { + if constexpr (verbosePrintPiccType) + return mfrc522.PICC_GetTypeName(piccType); + else + return static_cast(piccType); +} + + MFRC522::MIFARE_Key key{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; const byte sector = 1; const byte trailerBlock = 7; @@ -31,19 +54,13 @@ const unsigned int removeDelay = 3; } // namespace Chip_card::Chip_card(Mp3 &mp3, Buttons &buttons) -: mfrc522(*(new MFRC522(mfrc522_SSPin, mfrc522_RSTPin))) +: mfrc522(mfrc522_SSPin, mfrc522_RSTPin) , mp3(mp3) , buttons(buttons) , cardRemovedSwitch(removeDelay) {} -bool Chip_card::readCard(nfcTagObject &nfcTag) { - // Show some details of the PICC (that is: the tag/card) - LOG(card_log, s_info, F("Card UID: "), dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size)); - MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); - LOG(card_log, s_info, F("PICC type: "), mfrc522.PICC_GetTypeName(piccType)); - - byte buffer[18]; +bool Chip_card::auth(MFRC522::PICC_Type piccType) { MFRC522::StatusCode status = MFRC522::STATUS_ERROR; // Authenticate using key A @@ -51,24 +68,38 @@ bool Chip_card::readCard(nfcTagObject &nfcTag) { (piccType == MFRC522::PICC_TYPE_MIFARE_1K ) || (piccType == MFRC522::PICC_TYPE_MIFARE_4K ) ) { - LOG(card_log, s_info, F("Auth Classic using key A...")); - status = mfrc522.PCD_Authenticate( - MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); + LOG(card_log, s_info, F("Auth Classic")); + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); } else if (piccType == MFRC522::PICC_TYPE_MIFARE_UL ) { byte pACK[] = {0, 0}; //16 bit PassWord ACK returned by the tempCard // Authenticate using key A - LOG(card_log, s_info, F("Auth MIFARE UL...")); + LOG(card_log, s_info, F("Auth UL")); status = mfrc522.PCD_NTAG216_AUTH(key.keyByte, pACK); } if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, F("PCD_Auth() failed: "), mfrc522.GetStatusCodeName(status)); + LOG(card_log, s_error, F("Auth failed: "), printStatusCode(mfrc522, status)); return false; } + return true; +} + +bool Chip_card::readCard(nfcTagObject &nfcTag) { + // Show some details of the PICC (that is: the tag/card) + LOG(card_log, s_info, F("Card UID: "), dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size).c_str()); + const MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); + LOG(card_log, s_info, F("PICC type: "), printPiccType(mfrc522, piccType)); + + byte buffer[18]; + MFRC522::StatusCode status = MFRC522::STATUS_ERROR; + + if (not auth(piccType)) + return false; + // Show the whole sector as it currently is // LOG(card_log, s_info, F("Current data in sector:")); // mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); @@ -80,9 +111,9 @@ bool Chip_card::readCard(nfcTagObject &nfcTag) { (piccType == MFRC522::PICC_TYPE_MIFARE_4K ) ) { byte size = sizeof(buffer); - status = (MFRC522::StatusCode)mfrc522.MIFARE_Read(4, buffer, &size); + status = static_cast(mfrc522.MIFARE_Read(4, buffer, &size)); if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, F("MIFARE_Read() failed: "), mfrc522.GetStatusCodeName(status)); + LOG(card_log, s_error, str_MIFARE_Read(), F("4 "), str_failed(), printStatusCode(mfrc522, status)); return false; } } @@ -93,34 +124,35 @@ bool Chip_card::readCard(nfcTagObject &nfcTag) { status = static_cast(mfrc522.MIFARE_Read(8, buffer2, &size2)); if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, F("MIFARE_Read_1() failed: "), mfrc522.GetStatusCodeName(status)); + LOG(card_log, s_error, str_MIFARE_Read(), F("8 "), str_failed(), printStatusCode(mfrc522, status)); return false; } memcpy(buffer, buffer2, 4); status = static_cast(mfrc522.MIFARE_Read(9, buffer2, &size2)); if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, F("MIFARE_Read_2() failed: "), mfrc522.GetStatusCodeName(status)); + LOG(card_log, s_error, str_MIFARE_Read(), F("9 "), str_failed(), printStatusCode(mfrc522, status)); return false; } memcpy(buffer + 4, buffer2, 4); status = static_cast(mfrc522.MIFARE_Read(10, buffer2, &size2)); if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, F("MIFARE_Read_3() failed: "), mfrc522.GetStatusCodeName(status)); + LOG(card_log, s_error, str_MIFARE_Read(), F("10 "), str_failed(), printStatusCode(mfrc522, status)); return false; } memcpy(buffer + 8, buffer2, 4); status = static_cast(mfrc522.MIFARE_Read(11, buffer2, &size2)); if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, F("MIFARE_Read_4() failed: "), mfrc522.GetStatusCodeName(status)); + LOG(card_log, s_error, str_MIFARE_Read(), F("11 "), str_failed(), printStatusCode(mfrc522, status)); return false; } memcpy(buffer + 12, buffer2, 4); } + stopCrypto1(); - LOG(card_log, s_info, F("Data on Card: "), dump_byte_array(buffer, 16)); + LOG(card_log, s_info, F("Data on Card: "), dump_byte_array(buffer, 9).c_str()); uint32_t tempCookie; tempCookie = (uint32_t)buffer[0] << 24; @@ -129,20 +161,30 @@ bool Chip_card::readCard(nfcTagObject &nfcTag) { tempCookie += (uint32_t)buffer[3]; nfcTag.cookie = tempCookie; - nfcTag.version = buffer[4]; - nfcTag.nfcFolderSettings.folder = buffer[5]; - nfcTag.nfcFolderSettings.mode = static_cast(buffer[6]); - nfcTag.nfcFolderSettings.special = buffer[7]; - nfcTag.nfcFolderSettings.special2 = buffer[8]; - + uint32_t version = buffer[4]; + if (version == cardVersion) { + nfcTag.nfcFolderSettings.folder = buffer[5]; + nfcTag.nfcFolderSettings.mode = static_cast(buffer[6]); + nfcTag.nfcFolderSettings.special = buffer[7]; + nfcTag.nfcFolderSettings.special2 = buffer[8]; + } + else { + LOG(card_log, s_warning, F("Unknown version "), version); + nfcTag.nfcFolderSettings.folder = 0; + nfcTag.nfcFolderSettings.mode = mode_t::none; + } return true; } bool Chip_card::writeCard(const nfcTagObject &nfcTag) { - MFRC522::PICC_Type mifareType; - byte buffer[16] = {0x13, 0x37, 0xb3, 0x47, // 0x1337 0xb347 magic cookie to + + constexpr byte coockie_4 = (cardCookie & 0x000000ff) >> 0; + constexpr byte coockie_3 = (cardCookie & 0x0000ff00) >> 8; + constexpr byte coockie_2 = (cardCookie & 0x00ff0000) >> 16; + constexpr byte coockie_1 = (cardCookie & 0xff000000) >> 24; + byte buffer[16] = {coockie_1, coockie_2, coockie_3, coockie_4, // 0x1337 0xb347 magic cookie to // identify our nfc tags - 0x02, // version 1 + nfcTag.version, // version 1 nfcTag.nfcFolderSettings.folder, // the folder picked by the user static_cast(nfcTag.nfcFolderSettings.mode), // the playback mode picked by the user nfcTag.nfcFolderSettings.special, // track or function for admin cards @@ -150,36 +192,15 @@ bool Chip_card::writeCard(const nfcTagObject &nfcTag) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - mifareType = mfrc522.PICC_GetType(mfrc522.uid.sak); + const MFRC522::PICC_Type mifareType = mfrc522.PICC_GetType(mfrc522.uid.sak); - MFRC522::StatusCode status = MFRC522::STATUS_ERROR; - - // Authenticate using key B - //authentificate with the card and set card specific parameters - if ((mifareType == MFRC522::PICC_TYPE_MIFARE_MINI ) || - (mifareType == MFRC522::PICC_TYPE_MIFARE_1K ) || - (mifareType == MFRC522::PICC_TYPE_MIFARE_4K ) ) - { - LOG(card_log, s_info, F("Auth again using key A...")); - status = mfrc522.PCD_Authenticate( - MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); - } - else if (mifareType == MFRC522::PICC_TYPE_MIFARE_UL ) - { - byte pACK[] = {0, 0}; //16 bit PassWord ACK returned by the NFCtag - - // Authenticate using key A - LOG(card_log, s_info, F("Auth UL...")); - status = mfrc522.PCD_NTAG216_AUTH(key.keyByte, pACK); - } - - if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, F("PCD_Auth() failed: "), mfrc522.GetStatusCodeName(status)); + if (not auth(mifareType)) return false; - } + + MFRC522::StatusCode status = MFRC522::STATUS_ERROR; // Write data to the block - LOG(card_log, s_info, F("Writing data: "), dump_byte_array(buffer, 16)); + LOG(card_log, s_info, F("Writing data: "), dump_byte_array(buffer, 9).c_str()); if ((mifareType == MFRC522::PICC_TYPE_MIFARE_MINI ) || (mifareType == MFRC522::PICC_TYPE_MIFARE_1K ) || @@ -190,27 +211,28 @@ bool Chip_card::writeCard(const nfcTagObject &nfcTag) { else if (mifareType == MFRC522::PICC_TYPE_MIFARE_UL ) { byte buffer2[16]; - byte size2 = sizeof(buffer2); + memset(buffer2, 0, sizeof(buffer2)); - memset(buffer2, 0, size2); + //memset(buffer2, 0, size2); memcpy(buffer2, buffer, 4); status = static_cast(mfrc522.MIFARE_Write(8, buffer2, 16)); - memset(buffer2, 0, size2); + //memset(buffer2, 0, size2); memcpy(buffer2, buffer + 4, 4); status = static_cast(mfrc522.MIFARE_Write(9, buffer2, 16)); - memset(buffer2, 0, size2); + //memset(buffer2, 0, size2); memcpy(buffer2, buffer + 8, 4); status = static_cast(mfrc522.MIFARE_Write(10, buffer2, 16)); - memset(buffer2, 0, size2); + //memset(buffer2, 0, size2); memcpy(buffer2, buffer + 12, 4); status = static_cast(mfrc522.MIFARE_Write(11, buffer2, 16)); } + stopCrypto1(); if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, F("MIFARE_Write() failed: "), mfrc522.GetStatusCodeName(status)); + LOG(card_log, s_error, F("MIFARE_Write() failed: "), printStatusCode(mfrc522, status)); return false; } return true; @@ -224,7 +246,7 @@ void Chip_card::sleepCard() { void Chip_card::initCard() { SPI.begin(); // Init SPI bus mfrc522.PCD_Init(); // Init MFRC522 - mfrc522.PCD_DumpVersionToSerial(); // Show details of PCD - MFRC522 Card Reader + LOG_CODE(card_log, s_debug, mfrc522.PCD_DumpVersionToSerial()); // Show details of PCD - MFRC522 Card Reader } void Chip_card::stopCard() { @@ -265,23 +287,3 @@ cardEvent Chip_card::getCardEvent() { return cardEvent::none; } -void Chip_card::waitForCardRemoved() { - stopCrypto1(); - while (!cardRemoved) { - getCardEvent(); - } -} - -void Chip_card::waitForCardInserted() { - mp3.playMp3FolderTrack(mp3Tracks::t_800_waiting_for_card); - do { - if (buttons.isBreak()) { - mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); - return; - } - getCardEvent(); - } while (cardRemoved); - -} - - diff --git a/src/chip_card.hpp b/src/chip_card.hpp index 9480d228..41bd0792 100644 --- a/src/chip_card.hpp +++ b/src/chip_card.hpp @@ -3,6 +3,10 @@ #include +#include + +#include "constants.hpp" + enum class mode_t: uint8_t { none = 0, @@ -34,7 +38,7 @@ struct folderSettings { mode_t mode; uint8_t special; uint8_t special2; - bool operator==(const folderSettings& rhs) { + bool operator==(const folderSettings& rhs) const { return folder == rhs.folder && mode == rhs.mode && special == rhs.special && @@ -44,10 +48,10 @@ struct folderSettings { // this object stores nfc tag data struct nfcTagObject { - uint32_t cookie; - uint8_t version; + uint32_t cookie = cardCookie; + uint8_t version = cardVersion; folderSettings nfcFolderSettings; - bool operator==(const nfcTagObject& rhs) { + bool operator==(const nfcTagObject& rhs) const { return cookie == rhs.cookie && version == rhs.version && nfcFolderSettings == rhs.nfcFolderSettings; @@ -60,8 +64,7 @@ enum class cardEvent { inserted, }; -class MFRC522; // forward declaration to not have to include it here -class Mp3; +class Mp3; // forward declaration to not have to include it here class Buttons; class delayedSwitchOn { @@ -86,14 +89,15 @@ class Chip_card { bool writeCard(const nfcTagObject &nfcTag); void sleepCard(); void initCard (); - void stopCard (); - void stopCrypto1(); cardEvent getCardEvent(); - void waitForCardRemoved(); - void waitForCardInserted(); + bool isCardRemoved() { return cardRemoved; } private: - MFRC522 &mfrc522; + void stopCrypto1(); + void stopCard (); + bool auth(MFRC522::PICC_Type piccType); + + MFRC522 mfrc522; Mp3 &mp3; Buttons &buttons; diff --git a/src/constants.hpp b/src/constants.hpp index f9d47af5..d2e8a744 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -22,38 +22,39 @@ inline int getLevel(levelType t, level l) { return (l == level::inactive) ? (t== // uncomment the below line to enable five button support //#define FIVEBUTTONS -const uint32_t buttonLongPress = 1000; // timeout for long press button in ms -const uint8_t buttonPausePin = A0; -const uint8_t buttonUpPin = A1; -const uint8_t buttonDownPin = A2; +inline constexpr uint32_t buttonLongPress = 1000; // timeout for long press button in ms +inline constexpr uint8_t buttonPausePin = A0; +inline constexpr uint8_t buttonUpPin = A1; +inline constexpr uint8_t buttonDownPin = A2; #ifdef FIVEBUTTONS -const uint8_t buttonFourPin = A3; -const uint8_t buttonFivePin = A4; +inline constexpr uint8_t buttonFourPin = A3; +inline constexpr uint8_t buttonFivePin = A4; #endif -const levelType buttonPinType = levelType::activeLow; -const uint32_t buttonDbTime = 25; // Debounce time in milliseconds (default 25ms) +inline constexpr levelType buttonPinType = levelType::activeLow; +inline constexpr uint32_t buttonDbTime = 25; // Debounce time in milliseconds (default 25ms) // ####### chip_card ################################### -const uint32_t cardCookie = 322417479; -const byte mfrc522_RSTPin = 9; // Configurable, see typical pin layout above -const byte mfrc522_SSPin = 10; // Configurable, see typical pin layout above +inline constexpr uint32_t cardCookie = 0x1337b347; +inline constexpr uint8_t cardVersion = 0x02; +inline constexpr byte mfrc522_RSTPin = 9; // Configurable, see typical pin layout above +inline constexpr byte mfrc522_SSPin = 10; // Configurable, see typical pin layout above // ####### mp3 ######################################### -const uint8_t dfPlayer_receivePin = 2; -const uint8_t dfPlayer_transmitPin = 3; -const uint8_t dfPlayer_busyPin = 4; -const levelType dfPlayer_busyPinType = levelType::activeHigh; +inline constexpr uint8_t dfPlayer_receivePin = 2; +inline constexpr uint8_t dfPlayer_transmitPin = 3; +inline constexpr uint8_t dfPlayer_busyPin = 4; +inline constexpr levelType dfPlayer_busyPinType = levelType::activeHigh; // ####### tonuino ##################################### -const uint8_t shutdownPin = 7; -const levelType shutdownPinType = levelType::activeHigh; -const uint8_t openAnalogPin = A7; -const unsigned long cycleTime = 100; +inline constexpr uint8_t shutdownPin = 7; +inline constexpr levelType shutdownPinType = levelType::activeHigh; +inline constexpr uint8_t openAnalogPin = A7; +inline constexpr unsigned long cycleTime = 200; #endif /* SRC_CONSTANTS_HPP_ */ diff --git a/src/log.hpp b/src/log.hpp index 07eb8aec..85b91746 100644 --- a/src/log.hpp +++ b/src/log.hpp @@ -3,6 +3,8 @@ #include +#include "type_traits.hpp" + #define DEFINE_LOGGER(Logger_, MinSeverity_, Forwarder_) \ struct Logger_ : public logger_base \ { static __FlashStringHelper const* name() {return F(#Logger_);} } @@ -11,6 +13,10 @@ if constexpr ( Logger_::will_log(Severity_) ) \ Logger_::template log< Severity_ >(Logger_::name(), Expression_) +#define LOG_CODE(Logger_, Severity_, Statement) \ + if constexpr ( Logger_::will_log(Severity_) ) \ + Statement + enum severity { s_debug , s_info , @@ -20,15 +26,6 @@ enum severity { extern const __FlashStringHelper* getSeverityName(severity sev); -//! compile-time if -template struct if_ { typedef T1 result_type; }; -template< typename T1, typename T2> struct if_ { typedef T2 result_type; }; -//! bool-to-type -template struct bool_ { enum { result = b }; typedef bool_ result_type; }; -//! determines whether two types have the same type -template struct is_same_type : bool_ {}; -template struct is_same_type : bool_ {}; - class logger { public: constexpr static bool will_log(severity) { @@ -60,7 +57,7 @@ class logger { template class logger_base { public: - typedef typename if_::result, logger, FwdLogger>::result_type forward_logger_type; + typedef typename if_::value, logger, FwdLogger>::result_type forward_logger_type; static constexpr bool will_log(severity s) { return (s >= MinSeverity) && forward_logger_type::will_log(s); diff --git a/src/logger.hpp b/src/logger.hpp index fbbd3199..6c26a9a3 100644 --- a/src/logger.hpp +++ b/src/logger.hpp @@ -12,6 +12,7 @@ DEFINE_LOGGER(standby_log , s_info, tonuino_log); DEFINE_LOGGER(cmd_log , s_info, tonuino_log); DEFINE_LOGGER(admin_log , s_info, tonuino_log); DEFINE_LOGGER(menu_log , s_info, tonuino_log); +DEFINE_LOGGER(state_log , s_info, tonuino_log); DEFINE_LOGGER(button_log , s_info, tonuino_log); DEFINE_LOGGER(modifier_log, s_info, tonuino_log); DEFINE_LOGGER(mp3_log , s_info, tonuino_log); diff --git a/src/modifier.cpp b/src/modifier.cpp index bb5fc768..a683a977 100644 --- a/src/modifier.cpp +++ b/src/modifier.cpp @@ -3,28 +3,37 @@ #include "mp3.hpp" #include "tonuino.hpp" #include "logger.hpp" +#include "state_machine.hpp" +namespace { + +const __FlashStringHelper* str_SleepTimer () { return F("SleepTimer") ; } +const __FlashStringHelper* str_FreezeDance () { return F("FreezeDance") ; } +const __FlashStringHelper* str_KindergardenMode () { return F("Kita") ; } +const __FlashStringHelper* str_RepeatSingleModifier() { return F("RepeatSingle"); } + +} // anonymous namespace void SleepTimer::loop() { - if (sleepAtMillis != 0 && millis() > sleepAtMillis) { - LOG(modifier_log, s_info, F("= SleepTimer::loop() -> SLEEP!")); - mp3.pause(); - tonuino.setStandbyTimer(); + if (sleepTimer.isActive() && sleepTimer.isExpired()) { + LOG(modifier_log, s_info, str_SleepTimer(), F(" -> SLEEP!")); + if (SM_tonuino::is_in_state()) + SM_tonuino::dispatch(button_e(buttonRaw::pause)); tonuino.resetActiveModifier(); } } void SleepTimer::start(uint8_t minutes) { - LOG(modifier_log, s_info, F("= SleepTimer(), minutes: "), minutes); - sleepAtMillis = millis() + minutes * 60000; - //playAdvertisement(302); + LOG(modifier_log, s_info, str_SleepTimer(), F(" minutes: "), minutes); + sleepTimer.start(minutes * 60000); + //playAdvertisement(advertTracks::t_302_sleep); } void FreezeDance::loop() { if (nextStopAtMillis != 0 && millis() > nextStopAtMillis) { - LOG(modifier_log, s_info, F("= FreezeDance::loop() -> FREEZE!")); + LOG(modifier_log, s_info, str_FreezeDance(), F(" -> FREEZE!")); if (mp3.isPlaying()) { - mp3.playAdvertisement(301); + mp3.playAdvertisement(advertTracks::t_301_freeze_freeze); } setNextStopAtMillis(); } @@ -32,24 +41,24 @@ void FreezeDance::loop() { void FreezeDance::setNextStopAtMillis() { const uint16_t seconds = random(minSecondsBetweenStops, maxSecondsBetweenStops + 1); - LOG(modifier_log, s_info, F("= FreezeDance::setNextStopAtMillis(), seconds: "), seconds); + LOG(modifier_log, s_info, str_FreezeDance(), F(" next stop at: "), seconds); nextStopAtMillis = millis() + seconds * 1000; } bool KindergardenMode::handleNext() { if (cardQueued) { - LOG(modifier_log, s_info, F("= KindergardenMode::handleNext() -> NEXT")); + LOG(modifier_log, s_info, str_KindergardenMode(), F(" -> NEXT")); cardQueued = false; tonuino.setCard(nextCard); - LOG(modifier_log, s_info, F("Folder: "), nextCard.nfcFolderSettings.folder, F(" Mode: "), static_cast(nextCard.nfcFolderSettings.mode)); + LOG(modifier_log, s_debug, F("Folder: "), nextCard.nfcFolderSettings.folder, F(" Mode: "), static_cast(nextCard.nfcFolderSettings.mode)); tonuino.playFolder(); return true; } return false; } bool KindergardenMode::handleRFID(const nfcTagObject &newCard) { - LOG(modifier_log, s_info, F("= KindergardenMode::handleRFID() -> queued!")); + LOG(modifier_log, s_info, str_KindergardenMode(), F(" -> queued!")); nextCard = newCard; cardQueued = true; if (!mp3.isPlaying()) { @@ -59,8 +68,8 @@ bool KindergardenMode::handleRFID(const nfcTagObject &newCard) { } bool RepeatSingleModifier::handleNext() { - LOG(modifier_log, s_debug, F("= RepeatSingleModifier::handleNext() -> REPEAT CURRENT TRACK")); - delay(50); + LOG(modifier_log, s_debug, str_RepeatSingleModifier(), F(" -> REPEAT")); + delay(50); // TODO remove delay() if (!mp3.isPlaying()) { mp3.loop(); // this will call Mp3Notify::OnPlayFinished() but will be blocked by lastTrackFinished Mp3Notify::ResetLastTrackFinished(); diff --git a/src/modifier.hpp b/src/modifier.hpp index 6a242e39..92a511dc 100644 --- a/src/modifier.hpp +++ b/src/modifier.hpp @@ -5,6 +5,7 @@ #include "chip_card.hpp" #include "logger.hpp" +#include "timer.hpp" class Tonuino; class Mp3; @@ -41,7 +42,7 @@ class SleepTimer: public Modifier { void start (uint8_t minutes); private: - unsigned long sleepAtMillis = 0; + Timer sleepTimer{}; }; class FreezeDance: public Modifier { diff --git a/src/mp3.cpp b/src/mp3.cpp index d59a9f37..53284d10 100644 --- a/src/mp3.cpp +++ b/src/mp3.cpp @@ -3,6 +3,12 @@ #include "tonuino.hpp" #include "constants.hpp" +namespace { + +const __FlashStringHelper* str_Volume() { return F("Volume: ") ; } + +} + uint16_t Mp3Notify::lastTrackFinished = 0; void Mp3Notify::OnError(uint16_t errorCode) { @@ -13,18 +19,18 @@ void Mp3Notify::OnPlaySourceOnline (DfMp3_PlaySources source) { PrintlnSourceAc void Mp3Notify::OnPlaySourceInserted(DfMp3_PlaySources source) { PrintlnSourceAction(source, F("bereit" )); } void Mp3Notify::OnPlaySourceRemoved (DfMp3_PlaySources source) { PrintlnSourceAction(source, F("entfernt")); } void Mp3Notify::PrintlnSourceAction(DfMp3_PlaySources source, const __FlashStringHelper* action) { - if (source & DfMp3_PlaySources_Sd ) LOG(mp3_log, s_info, F("SD Karte "), action); - if (source & DfMp3_PlaySources_Usb ) LOG(mp3_log, s_info, F("USB " ), action); - if (source & DfMp3_PlaySources_Flash) LOG(mp3_log, s_info, F("Flash " ), action); + if (source & DfMp3_PlaySources_Sd ) LOG(mp3_log, s_debug, F("SD Karte "), action); + if (source & DfMp3_PlaySources_Usb ) LOG(mp3_log, s_debug, F("USB " ), action); + if (source & DfMp3_PlaySources_Flash) LOG(mp3_log, s_debug, F("Flash " ), action); } void Mp3Notify::OnPlayFinished(DfMp3_PlaySources /*source*/, uint16_t track) { - LOG(mp3_log, s_debug, F("Track beendet, Track: "), track); + LOG(mp3_log, s_debug, F("Track beendet: "), track); if (track == lastTrackFinished) return; else lastTrackFinished = track; - tonuino.nextTrack(); + Tonuino::getTonuino().nextTrack(); } Mp3::Mp3(const Settings& settings) @@ -73,11 +79,11 @@ void Mp3::playMp3FolderTrack(mp3Tracks track) { void Mp3::playAdvertisement(uint16_t track, bool olnyIfIsPlaying) { if (isPlaying()) { DFMiniMp3::playAdvertisement(track); - delay(500); + delay(500); // TODO remove delay() } else if (not olnyIfIsPlaying) { start(); DFMiniMp3::playAdvertisement(track); - waitForTrackToFinish(); + waitForTrackToFinish(); // TODO remove waitForTrackToFinish pause(); } } @@ -91,18 +97,18 @@ void Mp3::increaseVolume() { if (volume < settings.maxVolume) { DFMiniMp3::setVolume(++volume); } - LOG(mp3_log, s_info, F("Volume: "), volume); + LOG(mp3_log, s_info, str_Volume(), volume); } void Mp3::decreaseVolume() { if (volume > settings.minVolume) { DFMiniMp3::setVolume(--volume); } - LOG(mp3_log, s_info, F("Volume: "), volume); + LOG(mp3_log, s_info, str_Volume(), volume); } void Mp3::setVolume() { volume = settings.initVolume; DFMiniMp3::setVolume(volume); - LOG(mp3_log, s_info, F("Volume: "), volume); + LOG(mp3_log, s_info, str_Volume(), volume); } diff --git a/src/state_machine.cpp b/src/state_machine.cpp new file mode 100644 index 00000000..66eaf8bb --- /dev/null +++ b/src/state_machine.cpp @@ -0,0 +1,1752 @@ +#include "state_machine.hpp" + +#include "tonuino.hpp" +#include "logger.hpp" +#include "timer.hpp" + +namespace { + +const __FlashStringHelper* str_ChMode () { return F("ChMode") ; } +const __FlashStringHelper* str_ChFolder () { return F("ChFolder") ; } +const __FlashStringHelper* str_ChTrack () { return F("ChTrack") ; } +const __FlashStringHelper* str_ChFirstTrack () { return F("ChFirstTrack") ; } +const __FlashStringHelper* str_ChLastTrack () { return F("ChLastTrack") ; } +const __FlashStringHelper* str_WriteCard () { return F("WriteCard") ; } +const __FlashStringHelper* str_Idle () { return F("Idle") ; } +const __FlashStringHelper* str_StartPlay () { return F("StartPlay") ; } +const __FlashStringHelper* str_Play () { return F("Play") ; } +const __FlashStringHelper* str_Pause () { return F("Pause") ; } +const __FlashStringHelper* str_Admin_Allow () { return F("Admin_Allow") ; } +const __FlashStringHelper* str_Admin_Entry () { return F("Admin_Entry") ; } +const __FlashStringHelper* str_Admin_NewCard () { return F("Admin_NewCard") ; } +const __FlashStringHelper* str_Admin_MaxVolume () { return F("Admin_MaxVolume") ; } +const __FlashStringHelper* str_Admin_MinVolume () { return F("Admin_MinVolume") ; } +const __FlashStringHelper* str_Admin_InitVolume () { return F("Admin_InitVolume") ; } +const __FlashStringHelper* str_Admin_Eq () { return F("Admin_Eq") ; } +const __FlashStringHelper* str_Admin_ModCard () { return F("Admin_ModCard") ; } +const __FlashStringHelper* str_Admin_ShortCut () { return F("Admin_ShortCut") ; } +const __FlashStringHelper* str_Admin_StandbyTimer () { return F("Admin_StandbyTimer") ; } +const __FlashStringHelper* str_Admin_CardsForFolder () { return F("Admin_CardsForFolder") ; } +const __FlashStringHelper* str_Admin_InvButtons () { return F("Admin_InvButtons") ; } +const __FlashStringHelper* str_Admin_ResetEeprom () { return F("Admin_ResetEeprom") ; } +const __FlashStringHelper* str_Admin_LockAdmin () { return F("Admin_LockAdmin") ; } +const __FlashStringHelper* str_Admin_PauseIfCardRemoved() { return F("Admin_PauseIfCardRemoved") ; } +const __FlashStringHelper* str_VoiceMenu () { return F("VoiceMenu") ; } +const __FlashStringHelper* str_to () { return F(" -> ") ; } +const __FlashStringHelper* str_enter () { return F("enter ") ; } +const __FlashStringHelper* str_abort () { return F(" abort") ; } + +} + +// ---------------------------------------------------------------------------- +// State Declarations +// +template +class VoiceMenu : public SM +{ +public: + void entry() override; + void react(button_e const &) override; +protected: + void playCurrentValue(); + bool isAbort(button_e const &b); + + static int numberOfOptions ; + static mp3Tracks startMessage ; + static mp3Tracks messageOffset ; + static bool preview ; + static int previewFromFolder; + static uint8_t currentValue ; + + static Timer previewTimer ; + static bool previewStarted ; +}; + +class ChMode : public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +}; + +class ChFolder : public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +}; + +class ChTrack : public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +}; + +class ChFirstTrack : public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +}; + +class ChLastTrack : public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +}; + +class WriteCard : public SM_writeCard +{ +public: + void entry() override; + void react(button_e const &) override; +private: + enum subState { + start_waitCardInserted, + run_writeCard, + end_writeCard, + run_waitCardRemoved, + }; + subState current_subState; +}; + +class Admin_Allow: public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +private: + enum subState { + select_method, + start_pin, + get_pin, +// start_match, +// play_match_intro, +// play_match_a, +// play_match_operation, +// play_match_b, +// get_match_c, + allow, + not_allow, + }; + subState current_subState; + Settings::pin_t pin; + uint8_t pin_number; + uint8_t av, bv, cv; +}; + +class Admin_Entry: public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +}; + +class Admin_NewCard: public SM_tonuino +{ +public: + void entry() override; + void react(button_e const &) override; +private: + enum subState { + start_setupCard, + run_setupCard, + end_setupCard, + start_writeCard, + run_writeCard, + end_writeCard, + }; + subState current_subState; +}; + +class Admin_MaxVolume: public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +}; + +class Admin_MinVolume: public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +}; + +class Admin_InitVolume: public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +}; + +class Admin_Eq: public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +}; + +class Admin_ModCard: public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +private: + mode_t mode; + enum subState { + start_writeCard, + run_writeCard, + end_writeCard, + }; + subState current_subState; + bool readyToWrite; +}; + +class Admin_ShortCut: public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +private: + enum subState { + start_setupCard, + run_setupCard, + end_setupCard, + }; + subState current_subState; + size_t shortcut; +}; + +class Admin_StandbyTimer: public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +}; + +class Admin_CardsForFolder: public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +private: + enum subState { + start_getFolder, + run_getFolder, + start_getSpecial, + run_getSpecial, + start_getSpecial2, + run_getSpecial2, + prepare_writeCard, + start_writeCard, + run_writeCard, + }; + subState current_subState; + uint8_t special; + uint8_t special2; +}; + +class Admin_InvButtons: public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +}; + +class Admin_ResetEeprom: public SM_tonuino +{ +public: + void entry() override; + void react(button_e const &) override; +}; + +class Admin_LockAdmin: public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +}; + +class Admin_PauseIfCardRemoved: public VoiceMenu +{ +public: + void entry() override; + void react(button_e const &) override; +}; + + +// ####################################################### + +template +void VoiceMenu::entry() { + LOG(state_log, s_debug, str_VoiceMenu(), F("::entry() "), static_cast(startMessage)); + if (startMessage != mp3Tracks::t_0) + SM::mp3.playMp3FolderTrack(startMessage); + + currentValue = 0; +}; + +template +void VoiceMenu::playCurrentValue() { + SM::mp3.playMp3FolderTrack(messageOffset + currentValue); + previewTimer.start(1000); + previewStarted = false; +} + +template +bool VoiceMenu::isAbort(button_e const &b) { + if (b.b == buttonRaw::pauseLong) { + SM::mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); + LOG(state_log, s_info, str_VoiceMenu(), str_abort()); + return true; + } + return false; +} + +template +void VoiceMenu::react(button_e const &b) { + if ( currentValue != 0 + && preview + && not previewStarted + && previewTimer.isExpired() + && not SM::mp3.isPlaying()) { + LOG(state_log, s_debug, str_VoiceMenu(), F("::react() start preview "), currentValue); + if (previewFromFolder == 0) + SM::mp3.playFolderTrack(currentValue, 1); + else + SM::mp3.playFolderTrack(previewFromFolder, currentValue); + previewStarted = true; + } + + switch(b.b) { + case buttonRaw::upLong: + currentValue = min(currentValue + 10, numberOfOptions); + playCurrentValue(); + break; + + case buttonRaw::up: + currentValue = min(currentValue + 1, numberOfOptions); + playCurrentValue(); + break; + + case buttonRaw::downLong: + currentValue = max(currentValue - 10, 1); + playCurrentValue(); + break; + + case buttonRaw::down: + currentValue = max(currentValue - 1, 1); + playCurrentValue(); + break; + default: + break; + } + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_VoiceMenu(), F("::react() currentValue: "), currentValue); + } +}; + +// ####################################################### + +void ChMode::entry() { + LOG(state_log, s_info, str_enter(), str_ChMode()); + + folder = folderSettings{}; + + numberOfOptions = 10; + startMessage = mp3Tracks::t_310_select_mode; + messageOffset = mp3Tracks::t_310_select_mode; + preview = false; + previewFromFolder = 0; + + VoiceMenu::entry(); +}; + +void ChMode::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_ChMode(), F("::react() "), static_cast(b.b)); + } + VoiceMenu::react(b); + + if (isAbort(b)) { + transit(); + return; + } + + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + folder.mode = static_cast(currentValue); + LOG(state_log, s_info, str_ChMode(), F(": "), currentValue); + if (folder.mode == mode_t::admin) { + folder.folder = 0; + folder.mode = mode_t::admin_card; + transit(); + return; + } + transit(); + return; + } +}; + +// ####################################################### + +void ChFolder::entry() { + LOG(state_log, s_info, str_enter(), str_ChFolder()); + + numberOfOptions = 10; + startMessage = mp3Tracks::t_301_select_folder; + messageOffset = mp3Tracks::t_0; + preview = true; + previewFromFolder = 0; + + VoiceMenu::entry(); +}; +void ChFolder::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_ChFolder(), F("::react() "), static_cast(b.b)); + } + VoiceMenu::react(b); + + if (isAbort(b)) { + transit(); + return; + } + + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + folder.folder = currentValue; + LOG(state_log, s_info, str_ChFolder(), F(": "), currentValue); + if (folder.mode == mode_t::einzel) { + transit(); + return; + } + if ( ( folder.mode == mode_t::hoerspiel_vb) + ||( folder.mode == mode_t::album_vb ) + ||( folder.mode == mode_t::party_vb )) { + transit(); + return; + } + transit(); + return; + } +}; + +// ####################################################### + +void ChTrack::entry() { + LOG(state_log, s_info, str_enter(), str_ChTrack()); + + numberOfOptions = mp3.getFolderTrackCount(folder.folder); + startMessage = mp3Tracks::t_327_select_file; + messageOffset = mp3Tracks::t_0; + preview = true; + previewFromFolder = folder.folder; + + VoiceMenu::entry(); +}; +void ChTrack::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_ChTrack(), F("::react() "), static_cast(b.b)); + } + VoiceMenu::react(b); + + if (isAbort(b)) { + transit(); + return; + } + + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + folder.special = currentValue; + LOG(state_log, s_info, str_ChTrack(), F(": "), currentValue); + transit(); + return; + } +}; + +// ####################################################### + +void ChFirstTrack::entry() { + LOG(state_log, s_info, str_enter(), str_ChFirstTrack()); + + numberOfOptions = mp3.getFolderTrackCount(folder.folder); + startMessage = mp3Tracks::t_328_select_first_file; + messageOffset = mp3Tracks::t_0; + preview = true; + previewFromFolder = folder.folder; + + VoiceMenu::entry(); +}; +void ChFirstTrack::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_ChFirstTrack(), F("::react() "), static_cast(b.b)); + } + VoiceMenu::react(b); + + if (isAbort(b)) { + transit(); + return; + } + + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + folder.special = currentValue; + LOG(state_log, s_info, str_ChFirstTrack(), F(": "), currentValue); + transit(); + return; + } +}; + +// ####################################################### + +void ChLastTrack::entry() { + LOG(state_log, s_info, str_enter(), str_ChLastTrack()); + + numberOfOptions = mp3.getFolderTrackCount(folder.folder); + startMessage = mp3Tracks::t_329_select_last_file; + messageOffset = mp3Tracks::t_0; + preview = true; + previewFromFolder = folder.folder; + currentValue = folder.special; + + VoiceMenu::entry(); +}; + +void ChLastTrack::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_ChLastTrack(), F("::react() "), static_cast(b.b)); + } + VoiceMenu::react(b); + + if (isAbort(b)) { + transit(); + return; + } + + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + folder.special2 = currentValue; + LOG(state_log, s_info, str_ChLastTrack(), F(": "), currentValue); + transit(); + return; + } +}; + +// ####################################################### + +void WriteCard::entry() { + LOG(state_log, s_info, str_enter(), str_WriteCard()); + current_subState = start_waitCardInserted; +}; + +void WriteCard::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_WriteCard(), F("::react() "), static_cast(b.b)); + } + + if (b.b == buttonRaw::pauseLong) { + mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); + LOG(state_log, s_info, str_WriteCard(), str_to(), F("finished_abort")); + transit(); + return; + } + + switch (current_subState) { + case start_waitCardInserted: + mp3.playMp3FolderTrack(mp3Tracks::t_800_waiting_for_card); + current_subState = run_writeCard; + break; + case run_writeCard: + if (not chip_card.isCardRemoved()) { + nfcTagObject newCard; + newCard.nfcFolderSettings = folder; + if (chip_card.writeCard(newCard)) + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + else + mp3.playMp3FolderTrack(mp3Tracks::t_401_error); + current_subState = end_writeCard; + } + break; + case end_writeCard: + if (not mp3.isPlaying()) + current_subState = run_waitCardRemoved; + break; + case run_waitCardRemoved: + if (chip_card.isCardRemoved()) { + LOG(state_log, s_info, str_WriteCard(), str_to(), F("finished")); + transit(); + } + break; + default: + break; + } +} + +// ####################################################### + +bool Base::readCard() { + if (not chip_card.readCard(tempCard)) + return false; + + if (tempCard.cookie != cardCookie) + return false; + + if (tempCard.nfcFolderSettings.folder != 0) { + tonuino.setCard(tempCard); + return true; + } + + if (tempCard.nfcFolderSettings.mode == mode_t::admin_card) { + LOG(state_log, s_debug, F("Base"), str_to(), str_Admin_Entry()); + transit(); + return false; + } + + tonuino.specialCard(tempCard); + + return false; +} + +// ####################################################### + +void Idle::entry() { + LOG(state_log, s_info, str_enter(), str_Idle()); + tonuino.setStandbyTimer(); + mp3.stop(); +}; + +void Idle::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_Idle(), F("::react(b) "), static_cast(b.b)); + } + + buttonCmd cmd = buttons.getButtonCmd(b.b); + uint8_t shortCut = 99; + + switch (cmd) { + case buttonCmd::admin: + LOG(state_log, s_debug, str_Idle(), str_to(), str_Admin_Allow()); + // adminMenuAllowed() + transit(); + return; + case buttonCmd::pause: + case buttonCmd::track: + if (tonuino.getActiveModifier().handlePause()) + break; + shortCut = 0; + break; + case buttonCmd::volume_up: + if (tonuino.getActiveModifier().handleVolumeUp()) + break; + shortCut = 1; + break; + case buttonCmd::next: + if (tonuino.getActiveModifier().handleNextButton()) + break; + shortCut = 1; + break; + case buttonCmd::volume_down: + if (tonuino.getActiveModifier().handleVolumeDown()) + break; + shortCut = 2; + break; + case buttonCmd::previous: + if (tonuino.getActiveModifier().handlePreviousButton()) + break; + shortCut = 2; + break; + case buttonCmd::start: + shortCut = 3; + break; + default: + break; + } + + if (shortCut <= 3 && settings.shortCuts[shortCut].folder != 0) { + tonuino.setFolder(&settings.shortCuts[shortCut]); + LOG(state_log, s_debug, str_Idle(), str_to(), str_StartPlay()); + transit(); + } + else if (shortCut == 3) { + mp3.playMp3FolderTrack(mp3Tracks::t_262_pling); + } +}; + +void Idle::react(card_e const &c) { + if (c.c != cardEvent::none) { + LOG(state_log, s_debug, str_Idle(), F("::react(c) "), static_cast(c.c)); + } + switch (c.c) { + case cardEvent::inserted: + if (readCard()) { + LOG(state_log, s_debug, str_Idle(), str_to(), str_StartPlay()); + transit(); + } + return; + case cardEvent::removed: + break; + default: + break; + } +}; + +// ####################################################### + +void Play::entry() { + LOG(state_log, s_info, str_enter(), str_Play()); + tonuino.disableStandbyTimer(); + mp3.start(); +}; + +void Play::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_Play(), F("::react(b) "), static_cast(b.b)); + } + + buttonCmd cmd = buttons.getButtonCmd(b.b); + + switch (cmd) { + case buttonCmd::admin: + LOG(state_log, s_debug, str_Play(), str_to(), str_Admin_Allow()); + // adminMenuAllowed() + transit(); + return; + case buttonCmd::pause: + if (tonuino.getActiveModifier().handlePause()) + break; + LOG(state_log, s_debug, str_Play(), str_to(), str_Pause()); + transit(); + return; + case buttonCmd::track: + if (tonuino.getActiveModifier().handlePause()) + break; + tonuino.playTrackNumber(); + break; + case buttonCmd::volume_up: + if (tonuino.getActiveModifier().handleVolumeUp()) + break; + mp3.increaseVolume(); + break; + case buttonCmd::next: + if (tonuino.getActiveModifier().handleNextButton()) + break; + tonuino.nextTrack(); + break; + case buttonCmd::volume_down: + if (tonuino.getActiveModifier().handleVolumeDown()) + break; + mp3.decreaseVolume(); + break; + case buttonCmd::previous: + if (tonuino.getActiveModifier().handlePreviousButton()) + break; + tonuino.previousTrack(); + break; + default: + break; + } + if (not tonuino.knownCard) + transit(); +}; + +void Play::react(card_e const &c) { + if (c.c != cardEvent::none) { + LOG(state_log, s_debug, str_Play(), F("::react(c) "), static_cast(c.c)); + } + switch (c.c) { + case cardEvent::inserted: + if (readCard()) { + LOG(state_log, s_debug, str_Play(), str_to(), str_StartPlay()); + transit(); + } + return; + case cardEvent::removed: + if (settings.pauseWhenCardRemoved && not tonuino.getActiveModifier().handlePause()) { + transit(); + return; + } + break; + default: + break; + } +}; + +// ####################################################### + +void Pause::entry() { + LOG(state_log, s_info, str_enter(), str_Pause()); + tonuino.setStandbyTimer(); + mp3.pause(); +}; + +void Pause::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_Pause(), F("::react(b) "), static_cast(b.b)); + } + + buttonCmd cmd = buttons.getButtonCmd(b.b); + uint8_t shortCut = 99; + + switch (cmd) { + case buttonCmd::admin: + LOG(state_log, s_debug, str_Pause(), str_to(), str_Admin_Allow()); + transit(); + return; + case buttonCmd::pause: + if (tonuino.getActiveModifier().handlePause()) + break; + LOG(state_log, s_debug, str_Pause(), str_to(), str_Play()); + transit(); + return; + case buttonCmd::track: + if (tonuino.getActiveModifier().handlePause()) + break; + shortCut = 0; + break; + case buttonCmd::volume_up: + if (tonuino.getActiveModifier().handleVolumeUp()) + break; + shortCut = 1; + break; + case buttonCmd::next: + if (tonuino.getActiveModifier().handleNextButton()) + break; + shortCut = 1; + break; + case buttonCmd::volume_down: + if (tonuino.getActiveModifier().handleVolumeDown()) + break; + shortCut = 2; + break; + case buttonCmd::previous: + if (tonuino.getActiveModifier().handlePreviousButton()) + break; + shortCut = 2; + break; + default: + break; + } + + if (shortCut < 3 && settings.shortCuts[shortCut].folder != 0) { + tonuino.setFolder(&settings.shortCuts[shortCut]); + LOG(state_log, s_debug, str_Idle(), str_to(), str_StartPlay()); + transit(); + } +}; + +void Pause::react(card_e const &c) { + if (c.c != cardEvent::none) { + LOG(state_log, s_debug, str_Pause(), F("::react(c) "), static_cast(c.c)); + } + switch (c.c) { + case cardEvent::inserted: + if (readCard()) { + if (settings.pauseWhenCardRemoved && tonuino.getCard() == tempCard && not tonuino.getActiveModifier().handlePause()) { + transit(); + return; + } + LOG(state_log, s_debug, str_Pause(), str_to(), str_StartPlay()); + transit(); + } + return; + case cardEvent::removed: + break; + default: + break; + } +}; + +// ####################################################### + +void StartPlay::entry() { + LOG(state_log, s_info, str_enter(), str_StartPlay()); + tonuino.playFolder(); +}; + +void StartPlay::react(button_e const &/*b*/) { + LOG(state_log, s_debug, str_StartPlay(), str_to(), str_Play()); + transit(); +}; + +// ####################################################### + +void Admin_Allow::entry() { + LOG(state_log, s_info, str_enter(), str_Admin_Allow()); + current_subState = select_method; +}; + +void Admin_Allow::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_Admin_Allow(), F("::react() "), static_cast(b.b)); + } + + if (isAbort(b)) { + transit(); + return; + } + + switch (current_subState) { + case select_method : + if (settings.adminMenuLocked == 0) { + current_subState = allow; + } + else if (settings.adminMenuLocked == 1) { + current_subState = not_allow; + } + else if (settings.adminMenuLocked == 2) { + mp3.playMp3FolderTrack(mp3Tracks::t_991_admin_pin); + pin_number = 0; + current_subState = start_pin; + } +// else if (settings.adminMenuLocked == 3) { +// current_subState = start_match; +// } + else { + current_subState = not_allow; + } + break; + case start_pin : + if (not mp3.isPlaying()) + current_subState = get_pin; + break; + case get_pin : + if (b.b == buttonRaw::pause) + pin[pin_number++] = 1; + else if (b.b == buttonRaw::up) + pin[pin_number++] = 2; + else if (b.b == buttonRaw::down) + pin[pin_number++] = 3; + if (pin_number == 4) { + if (pin == settings.adminMenuPin) { + current_subState = allow; + } + else { + current_subState = not_allow; + } + } + break; +// case start_match : +// av = random(10, 20); +// bv = random(1, av); +// mp3.playMp3FolderTrack(mp3Tracks::t_992_admin_calc); +// current_subState = play_match_intro; +// break; +// case play_match_intro : +// if (not mp3.isPlaying()) { +// mp3.playMp3FolderTrack(av); +// current_subState = play_match_a; +// } +// break; +// case play_match_a : +// if (not mp3.isPlaying()) { +// if (random(1, 3) == 2) { +// cv = av + bv; +// mp3.playMp3FolderTrack(mp3Tracks::t_993_admin_calc); +// } else { +// cv = av - bv; +// mp3.playMp3FolderTrack(mp3Tracks::t_994_admin_calc); +// } +// LOG(admin_log, s_info, F("Result: "), cv); +// current_subState = play_match_operation; +// } +// break; +// case play_match_operation: +// if (not mp3.isPlaying()) { +// mp3.playMp3FolderTrack(bv); +// current_subState = play_match_b; +// } +// break; +// case play_match_b : +// if (not mp3.isPlaying()) { +// numberOfOptions = 255; +// startMessage = mp3Tracks::t_0; +// messageOffset = mp3Tracks::t_0; +// preview = false; +// previewFromFolder = 0; +// +// tonuino.knownCard = false; +// +// VoiceMenu::entry(); +// current_subState = get_match_c; +// } +// break; +// case get_match_c : +// VoiceMenu::react(b); +// if ((b.b == buttonRaw::pause) && (currentValue != 0)) { +// if (current_subState == cv) +// current_subState = allow; +// else +// current_subState = not_allow; +// } +// break; + case allow: + LOG(state_log, s_debug, str_Admin_Allow(), str_to(), str_Admin_Entry()); + transit(); + return; + case not_allow: + LOG(state_log, s_debug, str_Admin_Allow(), str_abort()); + transit(); + return; + } +}; + +// ####################################################### + +void Admin_Entry::entry() { + LOG(state_log, s_info, str_enter(), str_Admin_Entry()); + tonuino.disableStandbyTimer(); + + numberOfOptions = 13; + startMessage = mp3Tracks::t_900_admin; + messageOffset = mp3Tracks::t_900_admin; + preview = false; + previewFromFolder = 0; + + tonuino.knownCard = false; + + VoiceMenu::entry(); +}; + +void Admin_Entry::react(button_e const &b) { + if (not chip_card.isCardRemoved()) + return; + + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_Admin_Entry(), F("::react() "), static_cast(b.b)); + } + VoiceMenu::react(b); + + if (isAbort(b)) { + transit(); + return; + } + + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + switch (currentValue) { + case 0: break; + case 1: // create new card + LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_NewCard()); + transit(); + return; + case 2: // Maximum Volume + LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_MaxVolume()); + transit(); + return; + case 3: // Minimum Volume + LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_MinVolume()); + transit(); + return; + case 4: // Initial Volume + LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_InitVolume()); + transit(); + return; + case 5: // EQ + LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_Eq()); + transit(); + return; + case 6: // create modifier card + LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_ModCard()); + transit(); + return; + case 7: // shortcut + LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_ShortCut()); + transit(); + return; + case 8: // standby timer + LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_StandbyTimer()); + transit(); + return; + case 9: // Create Cards for Folder + LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_CardsForFolder()); + transit(); + return; + case 10: // Invert Functions for Up/Down Buttons + LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_InvButtons()); + transit(); + return; + case 11: // reset EEPROM + LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_ResetEeprom()); + transit(); + return; + case 12: // lock admin menu + LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_LockAdmin()); + transit(); + return; + case 13: // Pause, wenn Karte entfernt wird + LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_PauseIfCardRemoved()); + transit(); + return; + } + } +}; + +// ####################################################### + +void Admin_NewCard::entry() { + LOG(state_log, s_info, str_enter(), str_Admin_NewCard()); + current_subState = start_setupCard; +}; + +void Admin_NewCard::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_Admin_NewCard(), F("::react() "), static_cast(b.b)); + } + switch (current_subState) { + case start_setupCard: + SM_setupCard::start(); + current_subState = run_setupCard; + break; + case run_setupCard: + SM_setupCard::dispatch(b); + if (SM_setupCard::is_in_state()) + current_subState = end_setupCard; + if (SM_setupCard::is_in_state()) { + LOG(state_log, s_info, str_Admin_NewCard(), str_abort()); + transit(); + return; + } + break; + case end_setupCard: + SM_writeCard::folder = SM_setupCard::folder; + current_subState = start_writeCard; + break; + case start_writeCard: + SM_writeCard::start(); + current_subState = run_writeCard; + break; + case run_writeCard: + SM_writeCard::dispatch(b); + if (SM_writeCard::is_in_state()) + current_subState = end_writeCard; + if (SM_writeCard::is_in_state()) { + LOG(state_log, s_info, str_Admin_NewCard(), str_abort()); + transit(); + return; + } + break; + case end_writeCard: + LOG(state_log, s_debug, str_Admin_NewCard(), str_to(), str_Idle()); + transit(); + return; + default: + break; + } +}; + +// ####################################################### + +void Admin_MaxVolume::entry() { + LOG(state_log, s_info, str_enter(), str_Admin_MaxVolume()); + + numberOfOptions = 30 - settings.minVolume; + startMessage = mp3Tracks::t_930_max_volume_intro; + messageOffset = static_cast(settings.minVolume); + preview = false; + previewFromFolder = 0; + + VoiceMenu::entry(); + + currentValue = settings.maxVolume - settings.minVolume; +}; + +void Admin_MaxVolume::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_Admin_MaxVolume(), F("::react() "), static_cast(b.b)); + } + VoiceMenu::react(b); + + if (isAbort(b)) { + transit(); + return; + } + + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + settings.maxVolume = currentValue + settings.minVolume; + settings.writeSettingsToFlash(); + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + LOG(state_log, s_debug, str_Admin_MaxVolume(), str_to(), str_Idle()); + transit(); + } +}; + +// ####################################################### + +void Admin_MinVolume::entry() { + LOG(state_log, s_info, str_enter(), str_Admin_MinVolume()); + + numberOfOptions = settings.maxVolume - 1; + startMessage = mp3Tracks::t_931_min_volume_into; + messageOffset = mp3Tracks::t_0; + preview = false; + previewFromFolder = settings.maxVolume - settings.minVolume; + + VoiceMenu::entry(); + + currentValue = settings.minVolume; +}; + +void Admin_MinVolume::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_Admin_MinVolume(), F("::react() "), static_cast(b.b)); + } + VoiceMenu::react(b); + + if (isAbort(b)) { + transit(); + return; + } + + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + settings.minVolume = currentValue; + settings.writeSettingsToFlash(); + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + LOG(state_log, s_debug, str_Admin_MinVolume(), str_to(), str_Idle()); + transit(); + } +}; + +// ####################################################### + +void Admin_InitVolume::entry() { + LOG(state_log, s_info, str_enter(), str_Admin_InitVolume()); + + numberOfOptions = settings.maxVolume - settings.minVolume + 1; + startMessage = mp3Tracks::t_932_init_volume_into; + messageOffset = static_cast(settings.minVolume - 1); + preview = false; + previewFromFolder = 0; + + VoiceMenu::entry(); + + currentValue = settings.initVolume - settings.minVolume + 1; +}; + +void Admin_InitVolume::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_Admin_InitVolume(), F("::react() "), static_cast(b.b)); + } + VoiceMenu::react(b); + + if (isAbort(b)) { + transit(); + return; + } + + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + settings.initVolume = currentValue + settings.minVolume - 1; + settings.writeSettingsToFlash(); + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + LOG(state_log, s_debug, str_Admin_InitVolume(), str_to(), str_Idle()); + transit(); + } +}; + +// ####################################################### + +void Admin_Eq::entry() { + LOG(state_log, s_info, str_enter(), str_Admin_Eq()); + + numberOfOptions = 6; + startMessage = mp3Tracks::t_920_eq_intro; + messageOffset = mp3Tracks::t_920_eq_intro; + preview = false; + previewFromFolder = 0; + + VoiceMenu::entry(); + + currentValue = settings.eq; +}; + +void Admin_Eq::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_Admin_Eq(), F("::react() "), static_cast(b.b)); + } + VoiceMenu::react(b); + + if (isAbort(b)) { + transit(); + return; + } + + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + settings.eq = currentValue; + mp3.setEq(static_cast(settings.eq - 1)); + settings.writeSettingsToFlash(); + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + LOG(state_log, s_debug, str_Admin_InitVolume(), str_to(), str_Idle()); + transit(); + } +}; + +// ####################################################### + +void Admin_ModCard::entry() { + LOG(state_log, s_info, str_enter(), str_Admin_ModCard()); + + numberOfOptions = 6; + startMessage = mp3Tracks::t_970_modifier_Intro; + messageOffset = mp3Tracks::t_970_modifier_Intro; + preview = false; + previewFromFolder = 0; + + VoiceMenu::entry(); + + mode = mode_t::none; + current_subState = start_writeCard; + readyToWrite = false; +}; + +void Admin_ModCard::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_Admin_ModCard(), F("::react() "), static_cast(b.b)); + } + VoiceMenu::react(b); + + if (isAbort(b)) { + transit(); + return; + } + + if (readyToWrite) { + switch (current_subState) { + case start_writeCard: + folder.folder = 0; + folder.special = 0; + folder.special2 = 0; + folder.mode = mode; + if (mode == mode_t::sleep_timer) + switch (currentValue) { + case 1: + folder.special = 5; + break; + case 2: + folder.special = 15; + break; + case 3: + folder.special = 30; + break; + case 4: + folder.special = 60; + break; + } + SM_writeCard::folder = folder; + SM_writeCard::start(); + current_subState = run_writeCard; + break; + case run_writeCard: + SM_writeCard::dispatch(b); + if (SM_writeCard::is_in_state()) + current_subState = end_writeCard; + if (SM_writeCard::is_in_state()) { + LOG(state_log, s_info, str_Admin_ModCard(), str_abort()); + transit(); + return; + } + break; + case end_writeCard: + LOG(state_log, s_debug, str_Admin_ModCard(), str_to(), str_Idle()); + transit(); + return; + default: + break; + } + return; + } + + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + if (mode == mode_t::none) { + mode = static_cast(currentValue); + if (mode != mode_t::sleep_timer) { + readyToWrite = true; + } + else { + numberOfOptions = 4; + startMessage = mp3Tracks::t_960_timer_intro; + messageOffset = mp3Tracks::t_960_timer_intro; + VoiceMenu::entry(); + } + } + else { + readyToWrite = true; + } + } +}; + +// ####################################################### + +void Admin_ShortCut::entry() { + LOG(state_log, s_info, str_enter(), str_Admin_ShortCut()); + + numberOfOptions = 4; + startMessage = mp3Tracks::t_940_shortcut_into; + messageOffset = mp3Tracks::t_940_shortcut_into; + preview = false; + previewFromFolder = 0; + + VoiceMenu::entry(); + + shortcut = 0; +}; + +void Admin_ShortCut::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_Admin_ShortCut(), F("::react() "), static_cast(b.b)); + } + VoiceMenu::react(b); + + if (isAbort(b)) { + transit(); + return; + } + + if (shortcut > 0) { + switch (current_subState) { + case start_setupCard: + SM_setupCard::start(); + current_subState = run_setupCard; + break; + case run_setupCard: + SM_setupCard::dispatch(b); + if (SM_setupCard::is_in_state()) + current_subState = end_setupCard; + if (SM_setupCard::is_in_state()) { + LOG(state_log, s_info, str_Admin_ShortCut(), str_abort()); + transit(); + return; + } + break; + case end_setupCard: + settings.shortCuts[shortcut] = SM_setupCard::folder; + LOG(state_log, s_debug, str_Admin_ShortCut(), str_to(), str_Idle()); + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + transit(); + return; + default: + break; + } + return; + } + + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + shortcut = currentValue; + } +}; + +// ####################################################### + +void Admin_StandbyTimer::entry() { + LOG(state_log, s_info, str_enter(), str_Admin_StandbyTimer()); + + numberOfOptions = 5; + startMessage = mp3Tracks::t_960_timer_intro; + messageOffset = mp3Tracks::t_960_timer_intro; + preview = false; + previewFromFolder = 0; + + VoiceMenu::entry(); +}; + +void Admin_StandbyTimer::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_Admin_StandbyTimer(), F("::react() "), static_cast(b.b)); + } + VoiceMenu::react(b); + + if (isAbort(b)) { + transit(); + return; + } + + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + switch (currentValue) { + case 1: settings.standbyTimer = 5; break; + case 2: settings.standbyTimer = 15; break; + case 3: settings.standbyTimer = 30; break; + case 4: settings.standbyTimer = 60; break; + case 5: settings.standbyTimer = 0; break; + } + settings.writeSettingsToFlash(); + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + LOG(state_log, s_debug, str_Admin_StandbyTimer(), str_to(), str_Idle()); + transit(); + } +}; + +// ####################################################### + +void Admin_CardsForFolder::entry() { + LOG(state_log, s_info, str_enter(), str_Admin_CardsForFolder()); + + folder.mode = mode_t::einzel; + + current_subState = start_getFolder; +}; + +void Admin_CardsForFolder::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_Admin_CardsForFolder(), F("::react() "), static_cast(b.b)); + } + + if (isAbort(b)) { + transit(); + return; + } + + switch (current_subState) { + case start_getFolder: + numberOfOptions = 99; + startMessage = mp3Tracks::t_301_select_folder; + messageOffset = mp3Tracks::t_0; + preview = true; + previewFromFolder = 0; + + VoiceMenu::entry(); + current_subState = run_getFolder; + break; + case run_getFolder: + VoiceMenu::react(b); + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + folder.folder = currentValue; + current_subState = start_getSpecial; + } + break; + case start_getSpecial: + numberOfOptions = mp3.getFolderTrackCount(folder.folder); + startMessage = mp3Tracks::t_328_select_first_file; + messageOffset = mp3Tracks::t_0; + preview = true; + previewFromFolder = folder.folder; + + VoiceMenu::entry(); + current_subState = run_getSpecial; + break; + case run_getSpecial: + VoiceMenu::react(b); + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + special = currentValue; + current_subState = start_getSpecial2; + } + break; + case start_getSpecial2: + numberOfOptions = mp3.getFolderTrackCount(folder.folder); + startMessage = mp3Tracks::t_329_select_last_file; + messageOffset = mp3Tracks::t_0; + preview = true; + previewFromFolder = folder.folder; + + VoiceMenu::entry(); + currentValue = special; + + current_subState = run_getSpecial2; + break; + case run_getSpecial2: + VoiceMenu::react(b); + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + special2 = currentValue; + mp3.playMp3FolderTrack(mp3Tracks::t_936_batch_cards_intro); + current_subState = prepare_writeCard; + } + break; + case prepare_writeCard: + if (not mp3.isPlaying() && chip_card.isCardRemoved()) { + if (special > special2) { + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + LOG(state_log, s_debug, str_Admin_CardsForFolder(), str_to(), str_Idle()); + transit(); + return; + } + folder.special = special; + mp3.playMp3FolderTrack(special); + LOG(card_log, s_info, special, F("-te Karte auflegen")); + current_subState = start_writeCard; + } + break; + case start_writeCard: + if (not mp3.isPlaying() && chip_card.isCardRemoved()) { + SM_writeCard::folder = folder; + SM_writeCard::start(); + current_subState = run_writeCard; + } + break; + case run_writeCard: + if (not mp3.isPlaying()) { + SM_writeCard::dispatch(b); + if (SM_writeCard::is_in_state()) { + ++special; + current_subState = prepare_writeCard; + } + if (SM_writeCard::is_in_state()) { + LOG(state_log, s_info, str_Admin_CardsForFolder(), str_abort()); + transit(); + return; + } + } + break; + default: + break; + } +}; + +// ####################################################### + +void Admin_InvButtons::entry() { + LOG(state_log, s_info, str_enter(), str_Admin_InvButtons()); + + numberOfOptions = 2; + startMessage = mp3Tracks::t_933_switch_volume_intro; + messageOffset = mp3Tracks::t_933_switch_volume_intro; + preview = false; + previewFromFolder = 0; + + VoiceMenu::entry(); +}; + +void Admin_InvButtons::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_Admin_InvButtons(), F("::react() "), static_cast(b.b)); + } + VoiceMenu::react(b); + + if (isAbort(b)) { + transit(); + return; + } + + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + switch (currentValue) { + case 1: settings.invertVolumeButtons = false; break; + case 2: settings.invertVolumeButtons = true ; break; + } + settings.writeSettingsToFlash(); + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + LOG(state_log, s_debug, str_Admin_InvButtons(), str_to(), str_Idle()); + transit(); + } +}; + +// ####################################################### + +void Admin_ResetEeprom::entry() { + LOG(state_log, s_info, str_enter(), str_Admin_ResetEeprom()); + settings.clearEEPROM(); + settings.resetSettings(); + mp3.playMp3FolderTrack(mp3Tracks::t_999_reset_ok); +}; + +void Admin_ResetEeprom::react(button_e const &/*b*/) { + LOG(state_log, s_debug, str_Admin_ResetEeprom(), str_to(), str_Idle()); + transit(); +}; + +// ####################################################### + +void Admin_LockAdmin::entry() { + LOG(state_log, s_info, str_enter(), str_Admin_LockAdmin()); + + numberOfOptions = 3; + startMessage = mp3Tracks::t_980_admin_lock_intro; + messageOffset = mp3Tracks::t_980_admin_lock_intro; + preview = false; + previewFromFolder = 0; + + VoiceMenu::entry(); +}; + +void Admin_LockAdmin::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_Admin_LockAdmin(), F("::react() "), static_cast(b.b)); + } + VoiceMenu::react(b); + + if (isAbort(b)) { + transit(); + return; + } + + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + switch (currentValue) { + case 1: + settings.adminMenuLocked = 0; + break; + case 2: + settings.adminMenuLocked = 1; + break; + case 3: + settings.adminMenuLocked = 2; + settings.adminMenuPin = { 1, 2, 1, 3 }; // TODO implement get key + break; +// case 4: +// settings.adminMenuLocked = 3; +// break; + } + settings.writeSettingsToFlash(); + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + LOG(state_log, s_debug, str_Admin_LockAdmin(), str_to(), str_Idle()); + transit(); + } +}; + +// ####################################################### + +void Admin_PauseIfCardRemoved::entry() { + LOG(state_log, s_info, str_enter(), str_Admin_PauseIfCardRemoved()); +}; + +void Admin_PauseIfCardRemoved::react(button_e const &b) { + if (b.b != buttonRaw::none) { + LOG(state_log, s_debug, str_Admin_PauseIfCardRemoved(), F("::react() "), static_cast(b.b)); + } + VoiceMenu::react(b); + + if (isAbort(b)) { + transit(); + return; + } + + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + switch (currentValue) { + case 1: settings.pauseWhenCardRemoved = false; break; + case 2: settings.pauseWhenCardRemoved = true ; break; + } + settings.writeSettingsToFlash(); + mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + LOG(state_log, s_debug, str_Admin_PauseIfCardRemoved(), str_to(), str_Idle()); + transit(); + } +}; + +// ####################################################### + +FSM_INITIAL_STATE(SM_setupCard, ChMode) +FSM_INITIAL_STATE(SM_writeCard, WriteCard) +FSM_INITIAL_STATE(SM_tonuino , Idle) + +template +folderSettings SM::folder = folderSettings{}; +template +Tonuino &SM::tonuino = Tonuino::getTonuino(); +template +Mp3 &SM::mp3 = Tonuino::getTonuino().getMp3(); +template +Buttons &SM::buttons = Tonuino::getTonuino().getButtons(); +template +Settings &SM::settings = Tonuino::getTonuino().getSettings(); +template +Chip_card &SM::chip_card = Tonuino::getTonuino().getChipCard(); + +template +int VoiceMenu::numberOfOptions ; +template +mp3Tracks VoiceMenu::startMessage ; +template +mp3Tracks VoiceMenu::messageOffset ; +template +bool VoiceMenu::preview ; +template +int VoiceMenu::previewFromFolder; +template +uint8_t VoiceMenu::currentValue ; + +template +Timer VoiceMenu::previewTimer ; +template +bool VoiceMenu::previewStarted ; + diff --git a/src/state_machine.hpp b/src/state_machine.hpp new file mode 100644 index 00000000..663f3526 --- /dev/null +++ b/src/state_machine.hpp @@ -0,0 +1,104 @@ +#ifndef SRC_STATE_MACHINE_HPP_ +#define SRC_STATE_MACHINE_HPP_ + +#include "tinyfsm.hpp" +#include "buttons.hpp" +#include "chip_card.hpp" +#include "mp3.hpp" + +struct ChMode; +class Tonuino; +class Mp3; + +// ---------------------------------------------------------------------------- +// Event Declarations +// +struct button_e: tinyfsm::Event { + button_e(buttonRaw b):b{b} {} + buttonRaw b; +}; +struct card_e : tinyfsm::Event { + card_e(cardEvent c): c{c} {} + cardEvent c; +}; + +// ---------------------------------------------------------------------------- +// State Machine Base Class Declaration +// +enum class SM_type { + tonuino, + setupCard, + writeCard, +}; +template +class SM: public tinyfsm::Fsm> +{ +public: + virtual void react(button_e const &) { }; + virtual void react(card_e const &) { }; + + virtual void entry(void) { }; + void exit(void) { }; + + static folderSettings folder; // TODO: put this into base of setupCard states +protected: + static Tonuino &tonuino; + static Mp3 &mp3; + static Buttons &buttons; + static Settings &settings; + static Chip_card &chip_card; +}; + +typedef SM SM_tonuino; +typedef SM SM_setupCard; +typedef SM SM_writeCard; + +class Base: public SM_tonuino +{ +protected: + bool readCard(); + nfcTagObject tempCard; +}; + +class Idle: public Base +{ +public: + void entry() override; + void react(button_e const &) override; + void react(card_e const &) override; +}; + +class StartPlay: public Base +{ +public: + void entry() override; + void react(button_e const &) override; +}; + +class Play: public Base +{ +public: + void entry() override; + void react(button_e const &) override; + void react(card_e const &) override; +}; + +class Pause: public Base +{ +public: + void entry() override; + void react(button_e const &) override; + void react(card_e const &) override; +}; + +// ---------------------------------------------------------------------------- +// State Machine end states +// +class finished_setupCard : public SM_setupCard{}; +class finished_abort_setupCard: public SM_setupCard{}; +class finished_writeCard : public SM_writeCard{}; +class finished_abort_writeCard: public SM_writeCard{}; + + + +#endif /* SRC_STATE_MACHINE_HPP_ */ diff --git a/src/timer.cpp b/src/timer.cpp new file mode 100644 index 00000000..886c7987 --- /dev/null +++ b/src/timer.cpp @@ -0,0 +1,27 @@ + +#include "timer.hpp" + +#include + +void Timer::start(unsigned long timeout) { + expireTime = millis() + timeout; + active = true; +} +bool Timer::isExpired() { + if (not active) + return true; + if (expireTime <= millis()) { + active = false; + return true; + } + return false; +} + +void Timer::stop() { + active = false; +} + + + + + diff --git a/src/timer.hpp b/src/timer.hpp new file mode 100644 index 00000000..aab516c4 --- /dev/null +++ b/src/timer.hpp @@ -0,0 +1,15 @@ +#ifndef SRC_TIMER_HPP_ +#define SRC_TIMER_HPP_ + +class Timer { +public: + void start(unsigned long timeout); + bool isExpired(); + void stop(); + bool isActive() { return active; } +private: + unsigned long expireTime{0 }; + bool active {false}; +}; + +#endif /* SRC_TIMER_HPP_ */ diff --git a/src/tinyfsm.hpp b/src/tinyfsm.hpp new file mode 100644 index 00000000..c046ae6b --- /dev/null +++ b/src/tinyfsm.hpp @@ -0,0 +1,258 @@ +/* + * TinyFSM - Tiny Finite State Machine Processor + * + * Copyright (c) 2012-2018 Axel Burri + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* --------------------------------------------------------------------- + * Version: 0.3.2 + * + * API documentation: see "../doc/50-API.md" + * + * The official TinyFSM website is located at: + * https://digint.ch/tinyfsm/ + * + * Author: + * Axel Burri + * --------------------------------------------------------------------- + */ + +#ifndef TINYFSM_HPP_INCLUDED +#define TINYFSM_HPP_INCLUDED + +#define TINYFSM_NOSTDLIB + +#ifndef TINYFSM_NOSTDLIB +#include +#else +#include "type_traits.hpp" +#endif + +// #include +// #define DBG(str) do { std::cerr << str << std::endl; } while( false ) +// DBG("*** dbg_example *** " << __PRETTY_FUNCTION__); + +namespace tinyfsm +{ + + // -------------------------------------------------------------------------- + + struct Event { }; + + // -------------------------------------------------------------------------- + +#ifdef TINYFSM_NOSTDLIB + // remove dependency on standard library + // useful in conjunction with -nostdlib option, e.g. if your compiler + // does not provide a standard library. + // replace with own implementation + + // check if both fsm and state class share same fsmtype + template + struct is_same_fsm : is_same_type< typename F::fsmtype, typename S::fsmtype > { }; +#else + // check if both fsm and state class share same fsmtype + template + struct is_same_fsm : std::is_same< typename F::fsmtype, typename S::fsmtype > { }; +#endif + + template + struct _state_instance + { + using value_type = S; + using type = _state_instance; + static S value; + }; + + template + typename _state_instance::value_type _state_instance::value; + + // -------------------------------------------------------------------------- + + template + class Fsm + { + public: + + using fsmtype = Fsm; + using state_ptr_t = F *; + + static state_ptr_t current_state_ptr; + + // public, leaving ability to access state instance (e.g. on reset) + template + static constexpr S & state(void) { + static_assert(is_same_fsm::value, "accessing state of different state machine"); + return _state_instance::value; + } + + template + static constexpr bool is_in_state(void) { + static_assert(is_same_fsm::value, "accessing state of different state machine"); + return current_state_ptr == &_state_instance::value; + } + + /// state machine functions + public: + + // explicitely specialized in FSM_INITIAL_STATE macro + static void set_initial_state(); + + static void reset() { }; + + static void enter() { + current_state_ptr->entry(); + } + + static void start() { + set_initial_state(); + enter(); + } + + template + static void dispatch(E const & event) { + current_state_ptr->react(event); + } + + + /// state transition functions + protected: + + template + void transit(void) { + static_assert(is_same_fsm::value, "transit to different state machine"); + current_state_ptr->exit(); + current_state_ptr = &_state_instance::value; + current_state_ptr->entry(); + } + + template + void transit(ActionFunction action_function) { + static_assert(is_same_fsm::value, "transit to different state machine"); + current_state_ptr->exit(); + // NOTE: we get into deep trouble if the action_function sends a new event. + // TODO: implement a mechanism to check for reentrancy + action_function(); + current_state_ptr = &_state_instance::value; + current_state_ptr->entry(); + } + + template + void transit(ActionFunction action_function, ConditionFunction condition_function) { + if(condition_function()) { + transit(action_function); + } + } + }; + + template + typename Fsm::state_ptr_t Fsm::current_state_ptr; + + // -------------------------------------------------------------------------- + + template + struct FsmList; + + template<> struct FsmList<> { + static void set_initial_state() { } + static void reset() { } + static void enter() { } + template + static void dispatch(E const &) { } + }; + + template + struct FsmList + { + using fsmtype = Fsm; + + static void set_initial_state() { + fsmtype::set_initial_state(); + FsmList::set_initial_state(); + } + + static void reset() { + F::reset(); + FsmList::reset(); + } + + static void enter() { + fsmtype::enter(); + FsmList::enter(); + } + + static void start() { + set_initial_state(); + enter(); + } + + template + static void dispatch(E const & event) { + fsmtype::template dispatch(event); + FsmList::template dispatch(event); + } + }; + + // -------------------------------------------------------------------------- + + template struct StateList; + template<> struct StateList<> { + static void reset() { } + }; + template + struct StateList + { + static void reset() { + _state_instance::value = S(); + StateList::reset(); + } + }; + + // -------------------------------------------------------------------------- + + template + struct MooreMachine : tinyfsm::Fsm + { + virtual void entry(void) { }; /* entry actions in some states */ + void exit(void) { }; /* no exit actions */ + }; + + template + struct MealyMachine : tinyfsm::Fsm + { + // input actions are modeled in react(): + // - conditional dependent of event type or payload + // - transit<>(ActionFunction) + void entry(void) { }; /* no entry actions */ + void exit(void) { }; /* no exit actions */ + }; + +} /* namespace tinyfsm */ + + +#define FSM_INITIAL_STATE(_FSM, _STATE) \ +namespace tinyfsm { \ + template<> void Fsm< _FSM >::set_initial_state(void) { \ + current_state_ptr = &_state_instance< _STATE >::value; \ + } \ +} + +#endif /* TINYFSM_HPP_INCLUDED */ diff --git a/src/tonuino.cpp b/src/tonuino.cpp index cbb04389..0b71b360 100644 --- a/src/tonuino.cpp +++ b/src/tonuino.cpp @@ -8,17 +8,27 @@ #include "constants.hpp" #include "logger.hpp" +#include "state_machine.hpp" namespace { + template void swap(T &lhs, T &rhs) { const T t = lhs; lhs = rhs; rhs = t; } -} -Tonuino tonuino; +const __FlashStringHelper* str_Track () { return F("Track: ") ; } +const __FlashStringHelper* str_Hoerspiel() { return F("Hörspiel"); } +const __FlashStringHelper* str_Album () { return F("Album"); } +const __FlashStringHelper* str_Party () { return F("Party"); } +const __FlashStringHelper* str_Einzel () { return F("Einzel"); } +const __FlashStringHelper* str_Hoerbuch () { return F("Hörbuch"); } +const __FlashStringHelper* str_Von_Bis () { return F("Von-Bis "); } +const __FlashStringHelper* str_bis () { return F(" bis "); } + +} // anonymous namespace void Tonuino::setup() { pinMode(shutdownPin , OUTPUT); @@ -27,9 +37,6 @@ void Tonuino::setup() { // load Settings from EEPROM settings.loadSettingsFromFlash(); - // activate standby timer - tonuino.setStandbyTimer(); - // DFPlayer Mini initialisieren mp3.begin(); // Zwei Sekunden warten bis der DFPlayer Mini initialisiert ist @@ -47,7 +54,10 @@ void Tonuino::setup() { } // Start Shortcut "at Startup" - e.g. Welcome Sound - tonuino.playShortCut(3); + //playShortCut(3); + + SM_tonuino::start(); + SM_tonuino::dispatch(button_e(buttonRaw::start)); } void Tonuino::loop() { @@ -60,8 +70,8 @@ void Tonuino::loop() { // Modifier : WIP! activeModifier->loop(); - handleButtons(); - handleChipCard(); + SM_tonuino::dispatch(button_e(buttons.getButtonRaw())); + SM_tonuino::dispatch(card_e(chip_card.getCardEvent())); unsigned long stop = millis(); @@ -69,202 +79,67 @@ void Tonuino::loop() { delay(cycleTime - (stop - start)); } -void Tonuino::handleButtons() { - - switch (buttons.getButtonCmd()) { - - case buttonCmd::admin: - mp3.pause(); - buttons.waitForNoButton(); - if (not adminMenuAllowed()) { - mp3.start(); - break; - } - adminMenu(); - break; - - case buttonCmd::pause: - if (activeModifier->handlePause()) - break; - if (mp3.isPlaying()) { - mp3.pause(); - setStandbyTimer(); - } else if (knownCard) { - mp3.start(); - disableStandbyTimer(); - } - break; - - case buttonCmd::track: - if (activeModifier->handlePause()) - break; - if (mp3.isPlaying()) { - uint8_t advertTrack = getCurrentTrack(); - // Spezialmodus Von-Bis für Album und Party gibt die Dateinummer relativ zur Startposition wieder - if (myFolder->mode == mode_t::album_vb || myFolder->mode == mode_t::party_vb) { - advertTrack = advertTrack - myFolder->special + 1; - } - mp3.playAdvertisement(advertTrack); - } else { - playShortCut(0); - } - break; - - case buttonCmd::volume_up: - if (mp3.isPlaying()) - volumeUpButton(); - else - playShortCut(1); - break; - - case buttonCmd::next: - if (mp3.isPlaying()) - nextButton(); - else - playShortCut(1); - break; - - case buttonCmd::volume_down: - if (mp3.isPlaying()) - volumeDownButton(); - else - playShortCut(2); - break; - - case buttonCmd::previous: - if (mp3.isPlaying()) - previousButton(); - else - playShortCut(2); - break; - default: - break; - } -} - -void Tonuino::handleChipCard() { - - const cardEvent ce = chip_card.getCardEvent(); - - if (settings.pauseWhenCardRemoved && (ce == cardEvent::removed)) { - if (not activeModifier->handlePause() && mp3.isPlaying()) { - mp3.pause(); - setStandbyTimer(); - } - } - - if (ce != cardEvent::inserted) - return; - - // RFID Karte wurde aufgelegt - nfcTagObject tempCard; - if (chip_card.readCard(tempCard) && !specialCard(tempCard) && !activeModifier->handleRFID(tempCard)) { - - if (settings.pauseWhenCardRemoved && knownCard && myCard == tempCard) { - if (not mp3.isPlaying()) { - mp3.start(); - disableStandbyTimer(); - } - chip_card.stopCrypto1(); - return; - } - - setCard(tempCard); - LOG(card_log, s_info, F("Folder: "), myCard.nfcFolderSettings.folder); - - if (myCard.cookie == cardCookie && myCard.nfcFolderSettings.mode != mode_t::none) { - knownCard = false; // prevent nextTrack() when calling Mp3Notify::OnPlayFinished() in mp3.waitForTrackToFinish(); - mp3.playMp3FolderTrack(mp3Tracks::t_262_pling); - mp3.waitForTrackToFinish(); - playFolder(); - } - - // Neue Karte konfigurieren - else { - knownCard = false; - mp3.playMp3FolderTrack(mp3Tracks::t_300_new_tag); - mp3.waitForTrackToFinish(); - setupCard(); - } - } - chip_card.stopCrypto1(); -} - -void Tonuino::writeCard(const nfcTagObject &nfcTag) { - chip_card.waitForCardInserted(); - - LOG(card_log, s_info, F("schreibe Karte...")); - if (chip_card.writeCard(nfcTag)) - mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); - else - mp3.playMp3FolderTrack(mp3Tracks::t_401_error); - mp3.waitForTrackToFinish(); - - chip_card.waitForCardRemoved(); -} - void Tonuino::playFolder() { LOG(play_log, s_debug, F("= playFolder()")); - disableStandbyTimer(); knownCard = true; numTracksInFolder = mp3.getFolderTrackCount(myFolder->folder); firstTrack = 1; - LOG(play_log, s_info, numTracksInFolder, F(" Dateien im Ordner "), myFolder->folder); + LOG(play_log, s_info, numTracksInFolder, F(" files in folder "), myFolder->folder); switch (myFolder->mode) { case mode_t::hoerspiel: // Hörspielmodus: eine zufällige Datei aus dem Ordner - LOG(play_log, s_info, F("Hörspiel")); + LOG(play_log, s_info, str_Hoerspiel()); currentTrack = random(1, numTracksInFolder + 1); - LOG(play_log, s_info, F("Track: "), currentTrack); + LOG(play_log, s_info, str_Track(), currentTrack); break; case mode_t::album: // Album Modus: kompletten Ordner spielen - LOG(play_log, s_info, F("Album")); + LOG(play_log, s_info, str_Album()); currentTrack = 1; break; case mode_t::party: // Party Modus: Ordner in zufälliger Reihenfolge - LOG(play_log, s_info, F("Party")); + LOG(play_log, s_info, str_Party()); shuffleQueue(); currentTrack = 1; break; case mode_t::einzel: // Einzel Modus: eine Datei aus dem Ordner abspielen - LOG(play_log, s_info, F("Einzel")); + LOG(play_log, s_info, str_Einzel()); currentTrack = myFolder->special; - LOG(play_log, s_info, F("Track: "), currentTrack); + LOG(play_log, s_info, str_Track(), currentTrack); break; case mode_t::hoerbuch: case mode_t::hoerbuch_1: // Hörbuch Modus: kompletten Ordner spielen und Fortschritt merken (oder nur eine Datei) - LOG(play_log, s_info, F("Hörbuch")); + LOG(play_log, s_info, str_Hoerbuch()); currentTrack = settings.readFolderSettingFromFlash(myFolder->folder); if (currentTrack == 0 || currentTrack > numTracksInFolder) { currentTrack = 1; } - LOG(play_log, s_info, F("Track: "), currentTrack); + LOG(play_log, s_info, str_Track(), currentTrack); break; case mode_t::hoerspiel_vb: // Spezialmodus Von-Bin: Hörspiel: eine zufällige Datei aus dem Ordner - LOG(play_log, s_info, F("Von-Bis Hörspiel")); + LOG(play_log, s_info, str_Von_Bis(), str_Hoerspiel()); LOG(play_log, s_info, myFolder->special, F(" bis "), myFolder->special2); firstTrack = myFolder->special; numTracksInFolder = myFolder->special2; currentTrack = random(myFolder->special, numTracksInFolder + 1); - LOG(play_log, s_info, F("Track: "), currentTrack); + LOG(play_log, s_info, str_Track(), currentTrack); break; case mode_t::album_vb: // Spezialmodus Von-Bis: Album: alle Dateien zwischen Start und Ende spielen - LOG(play_log, s_info, F("Von-Bis Album")); - LOG(play_log, s_info, myFolder->special, F(" bis "), myFolder->special2); + LOG(play_log, s_info, str_Von_Bis(), str_Album()); + LOG(play_log, s_info, myFolder->special, str_bis() , myFolder->special2); firstTrack = myFolder->special; numTracksInFolder = myFolder->special2; currentTrack = myFolder->special; @@ -272,8 +147,8 @@ void Tonuino::playFolder() { case mode_t::party_vb: // Spezialmodus Von-Bis: Party Ordner in zufälliger Reihenfolge - LOG(play_log, s_info, F("Von-Bis Party")); - LOG(play_log, s_info, myFolder->special, F(" bis "), myFolder->special2); + LOG(play_log, s_info, str_Von_Bis(), str_Party()); + LOG(play_log, s_info, myFolder->special, str_bis(), myFolder->special2); firstTrack = myFolder->special; numTracksInFolder = myFolder->special2; shuffleQueue(); @@ -281,27 +156,23 @@ void Tonuino::playFolder() { break; default: knownCard = false; - setStandbyTimer(); return; } playCurrentTrack(); if (knownCard && settings.pauseWhenCardRemoved) - mp3.waitForTrackToStart(); + mp3.waitForTrackToStart(); // TODO remove waitForTrackToStart } -void Tonuino::playShortCut(uint8_t shortCut) { - LOG(play_log, s_info, F("= playShortCut(): "),shortCut); - if (settings.shortCuts[shortCut].folder != 0) { - setFolder(&settings.shortCuts[shortCut]); - playFolder(); - delay(1000); - } else { - LOG(play_log, s_info, F("No shortcut")); - if (not knownCard) - mp3.playMp3FolderTrack(mp3Tracks::t_262_pling); +void Tonuino::playTrackNumber () { + uint8_t advertTrack = getCurrentTrack(); + // Spezialmodus Von-Bis für Album und Party gibt die Dateinummer relativ zur Startposition wieder + if (myFolder->mode == mode_t::album_vb || myFolder->mode == mode_t::party_vb) { + advertTrack = advertTrack - myFolder->special + 1; } + mp3.playAdvertisement(advertTrack); } + // Leider kann das Modul selbst keine Queue abspielen, daher müssen wir selbst die Queue verwalten void Tonuino::nextTrack() { if (activeModifier->handleNext()) @@ -318,9 +189,8 @@ void Tonuino::nextTrack() { case mode_t::hoerspiel : case mode_t::hoerspiel_vb: if (not mp3.isPlaying()) { - LOG(play_log, s_info, F("Hörspiel -> stop")); + LOG(play_log, s_info, str_Hoerspiel(), F(" -> stop")); knownCard = false; - setStandbyTimer(); //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! } break; @@ -330,52 +200,49 @@ void Tonuino::nextTrack() { if (currentTrack != numTracksInFolder) { ++currentTrack; mp3.playFolderTrack(myFolder->folder, currentTrack); - LOG(play_log, s_info, F("Album -> nächster Track: "), currentTrack); + LOG(play_log, s_info, str_Album(), F(" -> nächster Track: "), currentTrack); } else { // mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! knownCard = false; - setStandbyTimer(); } break; case mode_t::party : case mode_t::party_vb: if (currentTrack != numTracksInFolder - firstTrack + 1) { - LOG(play_log, s_info, F("Party -> weiter in der Queue ")); + LOG(play_log, s_info, str_Party(), F(" -> weiter in der Queue ")); ++currentTrack; } else { - LOG(play_log, s_info, F("Party, Ende der Queue -> beginne von vorne")); + LOG(play_log, s_info, str_Party(), F(", Ende der Queue -> beginne von vorne")); currentTrack = 1; //// Wenn am Ende der Queue neu gemischt werden soll bitte die Zeilen wieder aktivieren //LOG(play_log, s_info, F("Ende der Queue -> mische neu")); //shuffleQueue(); } - LOG(play_log, s_info, F("Track: "), queue[currentTrack - 1]); + LOG(play_log, s_info, str_Track(), queue[currentTrack - 1]); mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); break; case mode_t::einzel: - LOG(play_log, s_info, F("Einzel -> stop")); + LOG(play_log, s_info, str_Einzel(), F(" -> stop")); //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! knownCard = false; - setStandbyTimer(); break; case mode_t::hoerbuch: if (currentTrack != numTracksInFolder) { ++currentTrack; - LOG(play_log, s_info, F("Hörbuch -> nächster Track und Fortschr. speichern")); - LOG(play_log, s_info, F("Track: "), currentTrack); + LOG(play_log, s_info, str_Hoerbuch(), F(" -> nächster Track und Fortschr. speichern")); + LOG(play_log, s_info, str_Track(), currentTrack); mp3.playFolderTrack(myFolder->folder, currentTrack); // Fortschritt im EEPROM abspeichern settings.writeFolderSettingToFlash(myFolder->folder, currentTrack); } else { - LOG(play_log, s_info, F("Hörbuch -> Ende")); + LOG(play_log, s_info, str_Hoerbuch(), F(" -> Ende")); //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! // Fortschritt zurück setzen settings.writeFolderSettingToFlash(myFolder->folder, 1); knownCard = false; - setStandbyTimer(); } break; case mode_t::hoerbuch_1: @@ -384,18 +251,17 @@ void Tonuino::nextTrack() { else currentTrack = 1; - LOG(play_log, s_info, F("Hörbuch single -> Fortschritt speichern und beenden")); - LOG(play_log, s_info, F("Track: "), currentTrack); + LOG(play_log, s_info, str_Hoerbuch(), F(" single -> Fortschritt speichern und beenden")); + LOG(play_log, s_info, str_Track(), currentTrack); // Fortschritt im EEPROM abspeichern settings.writeFolderSettingToFlash(myFolder->folder, currentTrack); //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! knownCard = false; - setStandbyTimer(); break; default: break; } - delay(500); + delay(500); // TODO remove delay } void Tonuino::previousTrack() { @@ -404,13 +270,13 @@ void Tonuino::previousTrack() { switch (myFolder->mode) { case mode_t::hoerspiel: case mode_t::hoerspiel_vb: - LOG(play_log, s_info, F("Hörspiel -> Track von vorne spielen")); + LOG(play_log, s_info, str_Hoerspiel(), F(" -> Track von vorne spielen")); mp3.playFolderTrack(myFolder->folder, currentTrack); break; case mode_t::album: case mode_t::album_vb: - LOG(play_log, s_info, F("Album -> vorheriger Track")); + LOG(play_log, s_info, str_Album(), F(" -> vorheriger Track")); if (currentTrack != firstTrack) { --currentTrack; } @@ -420,27 +286,28 @@ void Tonuino::previousTrack() { case mode_t::party: case mode_t::party_vb: if (currentTrack != 1) { - LOG(play_log, s_info, F("Party -> zurück in der Qeueue ")); + LOG(play_log, s_info, str_Party(), F(" -> zurück in der Qeueue ")); --currentTrack; } else { - LOG(play_log, s_info, F("Party, Anfang der Queue -> springe ans Ende ")); + LOG(play_log, s_info, str_Party(), F(", Anfang der Queue -> springe ans Ende ")); currentTrack = numTracksInFolder - firstTrack + 1; } - LOG(play_log, s_info, F("Track: "), queue[currentTrack - 1]); + LOG(play_log, s_info, str_Track(), queue[currentTrack - 1]); mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); break; case mode_t::einzel: - LOG(play_log, s_info, F("Einzel -> Track von vorne spielen")); + LOG(play_log, s_info, str_Einzel(), F(" -> Track von vorne spielen")); mp3.playFolderTrack(myFolder->folder, currentTrack); break; case mode_t::hoerbuch: case mode_t::hoerbuch_1: - LOG(play_log, s_info, F("Hörbuch -> vorheriger Track und Fortschr. speichern")); + LOG(play_log, s_info, str_Hoerbuch(), F(" -> vorheriger Track und Fortschr. speichern")); if (currentTrack != 1) { --currentTrack; } + LOG(play_log, s_info, str_Track(), currentTrack); mp3.playFolderTrack(myFolder->folder, currentTrack); // Fortschritt im EEPROM abspeichern settings.writeFolderSettingToFlash(myFolder->folder, currentTrack); @@ -448,26 +315,28 @@ void Tonuino::previousTrack() { default: break; } - delay(500); + delay(500); // TODO remove delay } // Funktionen für den Standby Timer (z.B. über Pololu-Switch oder Mosfet) void Tonuino::setStandbyTimer() { LOG(standby_log, s_info, F("= setStandbyTimer()")); - if (settings.standbyTimer != 0) - standbyAtMillis = millis() + (settings.standbyTimer * 60 * 1000); - else - standbyAtMillis = 0; - LOG(standby_log, s_info, F("standbyAtMillis: "), standbyAtMillis); + if (settings.standbyTimer != 0 && not standbyTimer.isActive()) { + standbyTimer.start(settings.standbyTimer * 60 * 1000); + LOG(standby_log, s_info, F("timer started")); + } } void Tonuino::disableStandbyTimer() { LOG(standby_log, s_info, F("= disablestandby()")); - standbyAtMillis = 0; + if (settings.standbyTimer != 0) { + standbyTimer.stop(); + LOG(standby_log, s_info, F("timer stopped")); + } } void Tonuino::checkStandbyAtMillis() { - if (standbyAtMillis != 0 && millis() > standbyAtMillis) { + if (standbyTimer.isActive() && standbyTimer.isExpired()) { LOG(standby_log, s_info, F("power off!")); // enter sleep state digitalWrite(shutdownPin, getLevel(shutdownPinType, level::active)); @@ -491,102 +360,7 @@ uint8_t Tonuino::getCurrentTrack() const { return currentTrack; } -void Tonuino::volumeUpButton() { - if (activeModifier->handleVolumeUp()) - return; - - LOG(cmd_log, s_debug, F("= volumeUp()")); - mp3.increaseVolume(); -} - -void Tonuino::volumeDownButton() { - if (activeModifier->handleVolumeDown()) - return; - - LOG(cmd_log, s_debug, F("= volumeDown()")); - mp3.decreaseVolume(); -} - -void Tonuino::nextButton() { - if (activeModifier->handleNextButton()) - return; - - LOG(cmd_log, s_debug, F("= next()")); - nextTrack(); -} - -void Tonuino::previousButton() { - if (activeModifier->handlePreviousButton()) - return; - - LOG(cmd_log, s_debug, F("= previous()")); - previousTrack(); -} - -bool Tonuino::setupFolder(folderSettings& theFolder) { - // Ordner abfragen - theFolder.folder = voiceMenu(99, mp3Tracks::t_301_select_folder, mp3Tracks::t_0, true, 0, 0, true); - if (theFolder.folder == 0) return false; - - // Wiedergabemodus abfragen - theFolder.mode = static_cast(voiceMenu(10, mp3Tracks::t_310_select_mode, mp3Tracks::t_310_select_mode, false, 0, 0, true)); - if (theFolder.mode == mode_t::none) return false; - - //// Hörbuchmodus -> Fortschritt im EEPROM auf 1 setzen - //writeFolderSettingToFlash(theFolder.folder, 1); - - switch (theFolder.mode) { - - // Einzelmodus -> Datei abfragen - case mode_t::einzel: - theFolder.special = voiceMenu(mp3.getFolderTrackCount(theFolder.folder), mp3Tracks::t_327_select_file, mp3Tracks::t_0, - true, theFolder.folder); - break; - - // Admin Funktionen - case mode_t::admin: - theFolder.folder = 0; - theFolder.mode = mode_t::admin_card; - break; - - // Spezialmodus Von-Bis - case mode_t::hoerspiel_vb: - case mode_t::album_vb: - case mode_t::party_vb: - theFolder.special = voiceMenu(mp3.getFolderTrackCount(theFolder.folder), mp3Tracks::t_328_select_first_file, mp3Tracks::t_0, - true, theFolder.folder); - theFolder.special2 = voiceMenu(mp3.getFolderTrackCount(theFolder.folder), mp3Tracks::t_329_select_last_file, mp3Tracks::t_0, - true, theFolder.folder, theFolder.special); - break; - - default: - break; - } - return true; -} - -void Tonuino::setupCard() { - LOG(card_log, s_info, F("= setupCard()")); - mp3.pause(); - nfcTagObject newCard; - if (setupFolder(newCard.nfcFolderSettings)) - { - // Karte ist konfiguriert -> speichern - mp3.pause(); - do { - } while (mp3.isPlaying()); - - writeCard(newCard); - } -} - bool Tonuino::specialCard(const nfcTagObject &nfcTag) { - if (nfcTag.cookie != cardCookie) - return false; - - if (nfcTag.nfcFolderSettings.folder != 0) - return false; - LOG(card_log, s_debug, F("special card, mode = "), static_cast(nfcTag.nfcFolderSettings.mode)); if (activeModifier->getActive() == nfcTag.nfcFolderSettings.mode) { resetActiveModifier(); @@ -597,298 +371,33 @@ bool Tonuino::specialCard(const nfcTagObject &nfcTag) { const Modifier *oldModifier = activeModifier; switch (nfcTag.nfcFolderSettings.mode) { - case mode_t::none: - case mode_t::admin_card: chip_card.stopCard(); - adminMenu() ;break; - case mode_t::sleep_timer: LOG(card_log, s_info, F("activate sleepTimer")); + case mode_t::sleep_timer: LOG(card_log, s_info, F("act. sleepTimer")); mp3.playAdvertisement(advertTracks::t_302_sleep, false); activeModifier = &sleepTimer; sleepTimer.start(nfcTag.nfcFolderSettings.special) ;break; - case mode_t::freeze_dance: LOG(card_log, s_info, F("activate freezeDance")); + case mode_t::freeze_dance: LOG(card_log, s_info, F("act. freezeDance")); mp3.playAdvertisement(advertTracks::t_300_freeze_into, false); activeModifier = &freezeDance; ;break; - case mode_t::locked: LOG(card_log, s_info, F("activate locked")); + case mode_t::locked: LOG(card_log, s_info, F("act. locked")); mp3.playAdvertisement(advertTracks::t_303_locked, false); activeModifier = &locked ;break; - case mode_t::toddler: LOG(card_log, s_info, F("activate toddlerMode")); + case mode_t::toddler: LOG(card_log, s_info, F("act. toddlerMode")); mp3.playAdvertisement(advertTracks::t_304_buttonslocked, false); activeModifier = &toddlerMode ;break; - case mode_t::kindergarden: LOG(card_log, s_info, F("activate kindergardenMode")); + case mode_t::kindergarden: LOG(card_log, s_info, F("act. kindergardenMode")); mp3.playAdvertisement(advertTracks::t_305_kindergarden, false); activeModifier = &kindergardenMode ;break; - case mode_t::repeat_single:LOG(card_log, s_info, F("activate repeatSingleModifier")); + case mode_t::repeat_single:LOG(card_log, s_info, F("act. repeatSingleModifier")); mp3.playAdvertisement(advertTracks::t_260_activate_mod_card, false); activeModifier = &repeatSingleModifier ;break; - default: break; + default: return false; } if (oldModifier != activeModifier) activeModifier->init(); - delay(2000); - return true; -} - -bool Tonuino::adminMenuAllowed() { - // Admin menu has been locked - it still can be trigged via admin card - switch (settings.adminMenuLocked) { - case 1: - return false; - - // Pin check - case 2: - Settings::pin_t pin; - mp3.playMp3FolderTrack(mp3Tracks::t_991_admin_pin); - return (buttons.askCode(pin) && pin == settings.adminMenuPin); - - // Match check - case 3: - { - const uint8_t a = random(10, 20); - const uint8_t b = random(1, a); - uint8_t c; - mp3.playMp3FolderTrack(mp3Tracks::t_992_admin_calc); - mp3.waitForTrackToFinish(); - mp3.playMp3FolderTrack(a); - mp3.waitForTrackToFinish(); - - if (random(1, 3) == 2) { - // a + b - c = a + b; - mp3.playMp3FolderTrack(mp3Tracks::t_993_admin_calc); - } else { - // a - b - c = a - b; - mp3.playMp3FolderTrack(mp3Tracks::t_994_admin_calc); - } - mp3.waitForTrackToFinish(); - mp3.playMp3FolderTrack(b); - LOG(admin_log, s_info, F("Result: "), c); - return (voiceMenu(255, mp3Tracks::t_0, mp3Tracks::t_0, false) == c); - } - } - - // not locked + delay(2000); // TODO remove delay return true; } -void Tonuino::adminMenu() { - LOG(admin_log, s_info, F("= adminMenu()")); - - chip_card.waitForCardRemoved(); - - disableStandbyTimer(); - mp3.pause(); - knownCard = false; - - const int subMenu = voiceMenu(13, mp3Tracks::t_900_admin, mp3Tracks::t_900_admin, false, false, 0, true); - - switch (subMenu) { - case 0: //break - setStandbyTimer(); - return; - case 1: // create new card - setupCard(); - chip_card.stopCard(); - break; - case 2: // Maximum Volume - settings.maxVolume = voiceMenu(30 - settings.minVolume, mp3Tracks::t_930_max_volume_intro, static_cast(settings.minVolume), false, false, settings.maxVolume - settings.minVolume) + settings.minVolume; - break; - case 3: // Minimum Volume - settings.minVolume = voiceMenu(settings.maxVolume - 1, mp3Tracks::t_931_min_volume_into, mp3Tracks::t_0, false, false, settings.minVolume); - break; - case 4: // Initial Volume - settings.initVolume = voiceMenu(settings.maxVolume - settings.minVolume + 1, mp3Tracks::t_932_init_volume_into, static_cast(settings.minVolume - 1), false, false, settings.initVolume - settings.minVolume + 1) + settings.minVolume - 1; - break; - case 5: // EQ - settings.eq = voiceMenu(6, mp3Tracks::t_920_eq_intro, mp3Tracks::t_920_eq_intro, false, false, settings.eq); - mp3.setEq(static_cast(settings.eq - 1)); - break; - case 6: // create modifier card - createModifierCard(); - break; - case 7: // shortcut - setupFolder(settings.shortCuts[voiceMenu(4, mp3Tracks::t_940_shortcut_into, mp3Tracks::t_940_shortcut_into) - 1]); - mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); - break; - case 8: // standby timer - switch (voiceMenu(5, mp3Tracks::t_960_timer_intro, mp3Tracks::t_960_timer_intro)) { - case 1: settings.standbyTimer = 5; break; - case 2: settings.standbyTimer = 15; break; - case 3: settings.standbyTimer = 30; break; - case 4: settings.standbyTimer = 60; break; - case 5: settings.standbyTimer = 0; break; - } - break; - case 9: // Create Cards for Folder - createCardsForFolder(); - break; - case 10: // Invert Functions for Up/Down Buttons - if (voiceMenu(2, mp3Tracks::t_933_switch_volume_intro, mp3Tracks::t_933_switch_volume_intro, false) == 2) { - settings.invertVolumeButtons = true; - } - else { - settings.invertVolumeButtons = false; - } - break; - case 11: // reset EEPROM - settings.clearEEPROM(); - settings.resetSettings(); - mp3.playMp3FolderTrack(mp3Tracks::t_999_reset_ok); - break; - case 12: // lock admin menu - switch (voiceMenu(4, mp3Tracks::t_980_admin_lock_intro, mp3Tracks::t_980_admin_lock_intro, false)) { - case 1: settings.adminMenuLocked = 0; - break; - case 2: settings.adminMenuLocked = 1; - break; - case 3: { - Settings::pin_t pin; - mp3.playMp3FolderTrack(mp3Tracks::t_991_admin_pin); - if (buttons.askCode(pin)) - settings.adminMenuPin = pin; - else - break; - } - settings.adminMenuLocked = 2; - break; - } - break; - case 13: // Pause, wenn Karte entfernt wird - if (voiceMenu(2, mp3Tracks::t_913_pause_on_card_removed, mp3Tracks::t_933_switch_volume_intro, false) == 2) { - settings.pauseWhenCardRemoved = true; - } - else { - settings.pauseWhenCardRemoved = false; - } - break; - } - settings.writeSettingsToFlash(); - mp3.playMp3FolderTrack(mp3Tracks::t_262_pling); - setStandbyTimer(); -} - -void Tonuino::voiceMenuPlayOption( uint8_t returnValue - , mp3Tracks messageOffset - , bool preview - , int previewFromFolder) { - LOG(menu_log, s_info, F("playOption for: "), returnValue); - mp3.playMp3FolderTrack(messageOffset + returnValue); - if (preview) { - mp3.waitForTrackToFinish(); - if (previewFromFolder == 0) - mp3.playFolderTrack(returnValue, 1); - else - mp3.playFolderTrack(previewFromFolder, returnValue); - } -} - -uint8_t Tonuino::voiceMenu( int numberOfOptions - , mp3Tracks startMessage - , mp3Tracks messageOffset - , bool preview - , int previewFromFolder - , int defaultValue - , bool exitWithLongPress) { - uint8_t returnValue = defaultValue; - - if (startMessage != mp3Tracks::t_0) - mp3.playMp3FolderTrack(startMessage); - - LOG(menu_log, s_info, F("= voiceMenu() ("), numberOfOptions, F(" Options)")); - - do { - if (Serial.available() > 0) { - int optionSerial = Serial.parseInt(); - if (optionSerial != 0 && optionSerial <= numberOfOptions) - return optionSerial; - } - mp3.loop(); - switch(buttons.getButtonRaw()) { - case buttonRaw::pauseLong: - if (exitWithLongPress) { - mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); - return defaultValue; - } - break; - - case buttonRaw::pause: - if (returnValue != 0) { - LOG(menu_log, s_info, F("voiceMenu return: "), returnValue); - return returnValue; - } - //delay(1000); - break; - - case buttonRaw::upLong: - returnValue = min(returnValue + 10, numberOfOptions); - voiceMenuPlayOption(returnValue, messageOffset, preview, previewFromFolder); - break; - - case buttonRaw::up: - returnValue = min(returnValue + 1, numberOfOptions); - voiceMenuPlayOption(returnValue, messageOffset, preview, previewFromFolder); - break; - - case buttonRaw::downLong: - returnValue = max(returnValue - 10, 1); - voiceMenuPlayOption(returnValue, messageOffset, preview, previewFromFolder); - break; - - case buttonRaw::down: - returnValue = max(returnValue - 1, 1); - voiceMenuPlayOption(returnValue, messageOffset, preview, previewFromFolder); - break; - default: - break; - } - } while (true); -} - -void Tonuino::createModifierCard() { - nfcTagObject tempCard; - tempCard.cookie = cardCookie; - tempCard.version = 1; - tempCard.nfcFolderSettings.folder = 0; - tempCard.nfcFolderSettings.special = 0; - tempCard.nfcFolderSettings.special2 = 0; - tempCard.nfcFolderSettings.mode = static_cast(voiceMenu(6, mp3Tracks::t_970_modifier_Intro, mp3Tracks::t_970_modifier_Intro, false, false, 0, true)); - - if (tempCard.nfcFolderSettings.mode != mode_t::none) { - if (tempCard.nfcFolderSettings.mode == mode_t::sleep_timer) { - switch (voiceMenu(4, mp3Tracks::t_960_timer_intro, mp3Tracks::t_960_timer_intro)) { - case 1: tempCard.nfcFolderSettings.special = 5; break; - case 2: tempCard.nfcFolderSettings.special = 15; break; - case 3: tempCard.nfcFolderSettings.special = 30; break; - case 4: tempCard.nfcFolderSettings.special = 60; break; - } - } - - writeCard(tempCard); - } -} - -void Tonuino::createCardsForFolder() { - // Ordner abfragen - nfcTagObject tempCard; - tempCard.cookie = cardCookie; - tempCard.version = 1; - tempCard.nfcFolderSettings.mode = mode_t::einzel; - tempCard.nfcFolderSettings.folder = voiceMenu(99, mp3Tracks::t_301_select_folder, mp3Tracks::t_0, true); - uint8_t special = voiceMenu(mp3.getFolderTrackCount(tempCard.nfcFolderSettings.folder), mp3Tracks::t_328_select_first_file, mp3Tracks::t_0, - true, tempCard.nfcFolderSettings.folder); - uint8_t special2 = voiceMenu(mp3.getFolderTrackCount(tempCard.nfcFolderSettings.folder), mp3Tracks::t_329_select_last_file, mp3Tracks::t_0, - true, tempCard.nfcFolderSettings.folder, special); - - mp3.playMp3FolderTrack(mp3Tracks::t_936_batch_cards_intro); - mp3.waitForTrackToFinish(); - for (uint8_t x = special; x <= special2; x++) { - mp3.playMp3FolderTrack(x); - tempCard.nfcFolderSettings.special = x; - LOG(card_log, s_info, x, F("-te Karte auflegen")); - - writeCard(tempCard); - } -} - void Tonuino::shuffleQueue() { // Queue für die Zufallswiedergabe erstellen for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1; ++x) diff --git a/src/tonuino.hpp b/src/tonuino.hpp index 411eba2d..aae82e59 100644 --- a/src/tonuino.hpp +++ b/src/tonuino.hpp @@ -5,66 +5,48 @@ #include "buttons.hpp" #include "mp3.hpp" #include "modifier.hpp" +#include "timer.hpp" class Tonuino { public: Tonuino() {} + static Tonuino& getTonuino() { static Tonuino tonuino; return tonuino; } void setup (); void loop (); void playFolder (); - void playShortCut (uint8_t shortCut); void playCurrentTrack() { if (knownCard) mp3.playFolderTrack(myFolder->folder, getCurrentTrack()); } + void playTrackNumber (); void nextTrack(); void previousTrack(); - void resetActiveModifier() { activeModifier = &noneModifier; } + void resetActiveModifier () { activeModifier = &noneModifier; } + Modifier& getActiveModifier() { return *activeModifier; } void setStandbyTimer(); + void disableStandbyTimer (); void setCard (const nfcTagObject &newCard ) { myCard = newCard; setFolder(&myCard.nfcFolderSettings); } + const nfcTagObject& getCard() const { return myCard; } void setFolder(const folderSettings *newFolder) { myFolder = newFolder; } + Mp3& getMp3 () { return mp3 ; } + Buttons& getButtons () { return buttons ; } + Settings& getSettings () { return settings ; } + Chip_card& getChipCard() { return chip_card; } + + bool knownCard = false; + private: uint8_t getCurrentTrack() const; - void handleButtons (); - void handleChipCard(); - void writeCard(const nfcTagObject &nfcTag); - void checkStandbyAtMillis(); - void disableStandbyTimer (); - - void volumeUpButton (); - void volumeDownButton(); - void nextButton (); - void previousButton (); - bool setupFolder(folderSettings& theFolder); - - void setupCard (); bool specialCard(const nfcTagObject &nfcTag); - - bool adminMenuAllowed(); - void adminMenu (); - - void voiceMenuPlayOption( uint8_t returnValue - , mp3Tracks messageOffset - , bool preview - , int previewFromFolder); - uint8_t voiceMenu( int numberOfOptions - , mp3Tracks startMessage - , mp3Tracks messageOffset - , bool preview = false - , int previewFromFolder = 0 - , int defaultValue = 0 - , bool exitWithLongPress = false); - void createModifierCard (); - void createCardsForFolder(); void shuffleQueue (); static const size_t maxTracksInFolder = 255; @@ -75,6 +57,7 @@ class Tonuino { Buttons buttons {settings}; Chip_card chip_card {mp3, buttons}; + friend class Base; Modifier noneModifier {*this, mp3, settings}; SleepTimer sleepTimer {*this, mp3, settings}; @@ -92,17 +75,11 @@ class Tonuino { uint16_t firstTrack = 0; queue_t queue{}; - unsigned long standbyAtMillis = 0; - - bool knownCard = false; + Timer standbyTimer{}; nfcTagObject myCard; const folderSettings *myFolder = &myCard.nfcFolderSettings; }; -extern Tonuino tonuino; - - - #endif /* SRC_TONUINO_HPP_ */ diff --git a/src/type_traits.hpp b/src/type_traits.hpp new file mode 100644 index 00000000..466856c4 --- /dev/null +++ b/src/type_traits.hpp @@ -0,0 +1,17 @@ +#ifndef SRC_TYPE_TRAITS_HPP_ +#define SRC_TYPE_TRAITS_HPP_ + + +//! compile-time if +template struct if_ { typedef T1 result_type; }; +template< typename T1, typename T2> struct if_ { typedef T2 result_type; }; +//! bool-to-type +template struct bool_ { enum { value = b }; typedef bool_ result_type; }; +//! determines whether two types have the same type +template struct is_same_type : bool_ {}; +template struct is_same_type : bool_ {}; + + + + +#endif /* SRC_TYPE_TRAITS_HPP_ */ diff --git a/tinyfsm-master.zip b/tinyfsm-master.zip new file mode 100644 index 0000000000000000000000000000000000000000..2ba7971ffe3ff097b19c6d9cd321041851cd8cbb GIT binary patch literal 26813 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 literal 0 HcmV?d00001 From 13d5dc4d6789789231e4451a2be43fab83c41390 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Wed, 26 Jan 2022 19:09:30 +0100 Subject: [PATCH 15/32] new state machine * improvements and fixes --- sd-card/mp3/0402_ok_settings.mp3 | Bin 0 -> 18069 bytes sd-card/mp3/0919_continue_admin.mp3 | Bin 0 -> 10076 bytes src/array.hpp | 68 +++---- src/buttons.cpp | 15 ++ src/buttons.hpp | 2 + src/constants.hpp | 11 +- src/logger.hpp | 3 - src/mp3.hpp | 2 + src/state_machine.cpp | 301 +++++++++++++++++++--------- src/state_machine.hpp | 16 +- src/tonuino.cpp | 93 +++++---- 11 files changed, 326 insertions(+), 185 deletions(-) create mode 100644 sd-card/mp3/0402_ok_settings.mp3 create mode 100644 sd-card/mp3/0919_continue_admin.mp3 diff --git a/sd-card/mp3/0402_ok_settings.mp3 b/sd-card/mp3/0402_ok_settings.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..b3a38179e8c202e4fd5f4e7821095d5b2b8ab8a4 GIT binary patch literal 18069 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 j?ufj2l7ZVB-1)!$ struct array { - // Storage - T data[N]; - - static size_t length() { return N; } - using type = T; - - // Item access - T &operator[](size_t index) { return data[index]; } - const T &operator[](size_t index) const { return data[index]; } - - // Iterators - T *begin() { return &data[0]; } - const T *begin() const { return &data[0]; } - T *end() { return &data[N]; } - const T *end() const { return &data[N]; } - - // Comparisons - bool operator==(const array &rhs) const { - if (this == &rhs) - return true; - for (size_t i = 0; i < N; i++) - if ((*this)[i] != rhs[i]) - return false; - return true; - } - bool operator!=(const array &rhs) const { - return !(*this == rhs); - } - array& operator=(const array& rhs) { - if (this != &rhs) - for (size_t i = 0; i < N; i++) - (*this)[i] = rhs[i]; - return *this; - } + // Storage + T data[N]; + + static constexpr size_t length() { return N; } + using type = T; + + // Item access + T &operator[](size_t index) { return data[index]; } + const T &operator[](size_t index) const { return data[index]; } + + // Iterators + T *begin() { return &data[0]; } + const T *begin() const { return &data[0]; } + T *end() { return &data[N]; } + const T *end() const { return &data[N]; } + + // Comparisons + bool operator==(const array &rhs) const { + if (this == &rhs) + return true; + for (size_t i = 0; i < N; i++) + if ((*this)[i] != rhs[i]) + return false; + return true; + } + bool operator!=(const array &rhs) const { + return !(*this == rhs); + } + array& operator=(const array &rhs) { + if (this != &rhs) + for (size_t i = 0; i < N; i++) + (*this)[i] = rhs[i]; + return *this; + } }; diff --git a/src/buttons.cpp b/src/buttons.cpp index ebcf1f7d..f5291b4c 100644 --- a/src/buttons.cpp +++ b/src/buttons.cpp @@ -119,6 +119,21 @@ buttonCmd Buttons::getButtonCmd(buttonRaw b) { return ret; } +uint8_t Buttons::getButtonCode(buttonRaw b) { + switch (b) { + case buttonRaw::pause: return 1; + case buttonRaw::up : return 2; + case buttonRaw::down : return 3; + default : return 0; + } +} + +bool Buttons::isNoButton() { + return not buttonPause.isPressed() + && not buttonUp .isPressed() + && not buttonDown .isPressed(); +} + bool Buttons::isReset() { const int buttonActiveLevel = getLevel(buttonPinType, level::active); return (digitalRead(buttonPausePin) == buttonActiveLevel && diff --git a/src/buttons.hpp b/src/buttons.hpp index 02c31d5f..18b0b4f0 100644 --- a/src/buttons.hpp +++ b/src/buttons.hpp @@ -41,7 +41,9 @@ class Buttons { buttonRaw getButtonRaw(); buttonCmd getButtonCmd(buttonRaw b); + static uint8_t getButtonCode(buttonRaw b); bool isReset(); + bool isNoButton(); private: diff --git a/src/constants.hpp b/src/constants.hpp index d2e8a744..3c32a3f2 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -44,10 +44,11 @@ inline constexpr byte mfrc522_SSPin = 10; // Configurable, s // ####### mp3 ######################################### -inline constexpr uint8_t dfPlayer_receivePin = 2; -inline constexpr uint8_t dfPlayer_transmitPin = 3; -inline constexpr uint8_t dfPlayer_busyPin = 4; -inline constexpr levelType dfPlayer_busyPinType = levelType::activeHigh; +inline constexpr uint8_t dfPlayer_receivePin = 2; +inline constexpr uint8_t dfPlayer_transmitPin = 3; +inline constexpr uint8_t dfPlayer_busyPin = 4; +inline constexpr levelType dfPlayer_busyPinType = levelType::activeHigh; +inline constexpr unsigned long dfPlayer_timeUntilStarts = 300; // ####### tonuino ##################################### @@ -55,6 +56,6 @@ inline constexpr levelType dfPlayer_busyPinType = levelType::activeHigh; inline constexpr uint8_t shutdownPin = 7; inline constexpr levelType shutdownPinType = levelType::activeHigh; inline constexpr uint8_t openAnalogPin = A7; -inline constexpr unsigned long cycleTime = 200; +inline constexpr unsigned long cycleTime = 50; #endif /* SRC_CONSTANTS_HPP_ */ diff --git a/src/logger.hpp b/src/logger.hpp index 6c26a9a3..148f5a7c 100644 --- a/src/logger.hpp +++ b/src/logger.hpp @@ -9,9 +9,6 @@ DEFINE_LOGGER(init_log , s_info, tonuino_log); DEFINE_LOGGER(card_log , s_info, tonuino_log); DEFINE_LOGGER(play_log , s_info, tonuino_log); DEFINE_LOGGER(standby_log , s_info, tonuino_log); -DEFINE_LOGGER(cmd_log , s_info, tonuino_log); -DEFINE_LOGGER(admin_log , s_info, tonuino_log); -DEFINE_LOGGER(menu_log , s_info, tonuino_log); DEFINE_LOGGER(state_log , s_info, tonuino_log); DEFINE_LOGGER(button_log , s_info, tonuino_log); DEFINE_LOGGER(modifier_log, s_info, tonuino_log); diff --git a/src/mp3.hpp b/src/mp3.hpp index 47a69621..4a21ea1c 100644 --- a/src/mp3.hpp +++ b/src/mp3.hpp @@ -31,6 +31,7 @@ enum class mp3Tracks: uint16_t { t_332_say_number = 332, t_400_ok = 400, t_401_error = 401, + t_402_ok_settings = 402, t_800_waiting_for_card = 800, t_802_reset_aborted = 802, t_900_admin = 900, @@ -47,6 +48,7 @@ enum class mp3Tracks: uint16_t { t_911_reset = 911, t_912_admin_lock = 912, t_913_pause_on_card_removed = 913, + t_919_continue_admin = 919, t_920_eq_intro = 920, t_921_normal = 921, t_922_pop = 922, diff --git a/src/state_machine.cpp b/src/state_machine.cpp index 66eaf8bb..c57dd7c5 100644 --- a/src/state_machine.cpp +++ b/src/state_machine.cpp @@ -2,7 +2,6 @@ #include "tonuino.hpp" #include "logger.hpp" -#include "timer.hpp" namespace { @@ -49,7 +48,6 @@ class VoiceMenu : public SM void react(button_e const &) override; protected: void playCurrentValue(); - bool isAbort(button_e const &b); static int numberOfOptions ; static mp3Tracks startMessage ; @@ -120,7 +118,7 @@ class Admin_Allow: public VoiceMenu private: enum subState { select_method, - start_pin, + wait_for_no_button, get_pin, // start_match, // play_match_intro, @@ -134,7 +132,7 @@ class Admin_Allow: public VoiceMenu subState current_subState; Settings::pin_t pin; uint8_t pin_number; - uint8_t av, bv, cv; +// uint8_t av, bv, cv; }; class Admin_Entry: public VoiceMenu @@ -142,6 +140,7 @@ class Admin_Entry: public VoiceMenu public: void entry() override; void react(button_e const &) override; + static uint8_t lastCurrentValue ; }; class Admin_NewCard: public SM_tonuino @@ -268,6 +267,15 @@ class Admin_LockAdmin: public VoiceMenu public: void entry() override; void react(button_e const &) override; +private: + enum subState { + get_mode, + get_pin, + finished, + }; + subState current_subState; + Settings::pin_t pin; + size_t pin_number; }; class Admin_PauseIfCardRemoved: public VoiceMenu @@ -278,6 +286,33 @@ class Admin_PauseIfCardRemoved: public VoiceMenu }; +// ####################################################### + +template +bool SM::isAbort(button_e const &b) { + if (b.b == buttonRaw::pauseLong) { + SM::mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); + LOG(state_log, s_info, F("SM"), str_abort()); + return true; + } + return false; +} + +template +bool SM::isWaitForPlayFinish() { + if (waitForPlayFinish && timer.isExpired() && not mp3.isPlaying()) { + waitForPlayFinish = false; + return true; + } + return false; +} + +template +void SM::startWaitForPlayFinish() { + timer.start(dfPlayer_timeUntilStarts); + waitForPlayFinish = true; +} + // ####################################################### template @@ -296,16 +331,6 @@ void VoiceMenu::playCurrentValue() { previewStarted = false; } -template -bool VoiceMenu::isAbort(button_e const &b) { - if (b.b == buttonRaw::pauseLong) { - SM::mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); - LOG(state_log, s_info, str_VoiceMenu(), str_abort()); - return true; - } - return false; -} - template void VoiceMenu::react(button_e const &b) { if ( currentValue != 0 @@ -562,11 +587,12 @@ void WriteCard::react(button_e const &b) { mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); else mp3.playMp3FolderTrack(mp3Tracks::t_401_error); + timer.start(dfPlayer_timeUntilStarts); current_subState = end_writeCard; } break; case end_writeCard: - if (not mp3.isPlaying()) + if (timer.isExpired() && not mp3.isPlaying()) current_subState = run_waitCardRemoved; break; case run_waitCardRemoved: @@ -583,24 +609,24 @@ void WriteCard::react(button_e const &b) { // ####################################################### bool Base::readCard() { - if (not chip_card.readCard(tempCard)) + if (not chip_card.readCard(lastCardRead)) return false; - if (tempCard.cookie != cardCookie) + if (lastCardRead.cookie != cardCookie) return false; - if (tempCard.nfcFolderSettings.folder != 0) { - tonuino.setCard(tempCard); + if (lastCardRead.nfcFolderSettings.folder != 0) { return true; } - if (tempCard.nfcFolderSettings.mode == mode_t::admin_card) { + if (lastCardRead.nfcFolderSettings.mode == mode_t::admin_card) { LOG(state_log, s_debug, F("Base"), str_to(), str_Admin_Entry()); + Admin_Entry::lastCurrentValue = 0; transit(); return false; } - tonuino.specialCard(tempCard); + tonuino.specialCard(lastCardRead); return false; } @@ -677,6 +703,7 @@ void Idle::react(card_e const &c) { switch (c.c) { case cardEvent::inserted: if (readCard()) { + tonuino.setCard(lastCardRead); LOG(state_log, s_debug, str_Idle(), str_to(), str_StartPlay()); transit(); } @@ -754,6 +781,7 @@ void Play::react(card_e const &c) { switch (c.c) { case cardEvent::inserted: if (readCard()) { + tonuino.setCard(lastCardRead); LOG(state_log, s_debug, str_Play(), str_to(), str_StartPlay()); transit(); } @@ -839,10 +867,11 @@ void Pause::react(card_e const &c) { switch (c.c) { case cardEvent::inserted: if (readCard()) { - if (settings.pauseWhenCardRemoved && tonuino.getCard() == tempCard && not tonuino.getActiveModifier().handlePause()) { + if (settings.pauseWhenCardRemoved && tonuino.getCard() == lastCardRead && not tonuino.getActiveModifier().handlePause()) { transit(); return; } + tonuino.setCard(lastCardRead); LOG(state_log, s_debug, str_Pause(), str_to(), str_StartPlay()); transit(); } @@ -894,7 +923,7 @@ void Admin_Allow::react(button_e const &b) { else if (settings.adminMenuLocked == 2) { mp3.playMp3FolderTrack(mp3Tracks::t_991_admin_pin); pin_number = 0; - current_subState = start_pin; + current_subState = wait_for_no_button; } // else if (settings.adminMenuLocked == 3) { // current_subState = start_match; @@ -903,17 +932,17 @@ void Admin_Allow::react(button_e const &b) { current_subState = not_allow; } break; - case start_pin : - if (not mp3.isPlaying()) + case wait_for_no_button: + if (buttons.isNoButton()) current_subState = get_pin; break; case get_pin : - if (b.b == buttonRaw::pause) - pin[pin_number++] = 1; - else if (b.b == buttonRaw::up) - pin[pin_number++] = 2; - else if (b.b == buttonRaw::down) - pin[pin_number++] = 3; + { + const uint8_t c = Buttons::getButtonCode(b.b); + if (c != 0) { + LOG(state_log, s_debug, F("pin: "), c); + pin[pin_number++] = c; + } if (pin_number == 4) { if (pin == settings.adminMenuPin) { current_subState = allow; @@ -923,20 +952,23 @@ void Admin_Allow::react(button_e const &b) { } } break; + } // case start_match : // av = random(10, 20); // bv = random(1, av); // mp3.playMp3FolderTrack(mp3Tracks::t_992_admin_calc); +// timer.start(dfPlayer_timeUntilStarts); // current_subState = play_match_intro; // break; // case play_match_intro : -// if (not mp3.isPlaying()) { +// if (timer.isExpired() && not mp3.isPlaying()) { // mp3.playMp3FolderTrack(av); +// timer.start(dfPlayer_timeUntilStarts); // current_subState = play_match_a; // } // break; // case play_match_a : -// if (not mp3.isPlaying()) { +// if (timer.isExpired() && not mp3.isPlaying()) { // if (random(1, 3) == 2) { // cv = av + bv; // mp3.playMp3FolderTrack(mp3Tracks::t_993_admin_calc); @@ -944,18 +976,20 @@ void Admin_Allow::react(button_e const &b) { // cv = av - bv; // mp3.playMp3FolderTrack(mp3Tracks::t_994_admin_calc); // } +// timer.start(dfPlayer_timeUntilStarts); // LOG(admin_log, s_info, F("Result: "), cv); // current_subState = play_match_operation; // } // break; // case play_match_operation: -// if (not mp3.isPlaying()) { +// if (timer.isExpired() && not mp3.isPlaying()) { // mp3.playMp3FolderTrack(bv); +// timer.start(dfPlayer_timeUntilStarts); // current_subState = play_match_b; // } // break; // case play_match_b : -// if (not mp3.isPlaying()) { +// if (timer.isExpired() && not mp3.isPlaying()) { // numberOfOptions = 255; // startMessage = mp3Tracks::t_0; // messageOffset = mp3Tracks::t_0; @@ -979,6 +1013,7 @@ void Admin_Allow::react(button_e const &b) { // break; case allow: LOG(state_log, s_debug, str_Admin_Allow(), str_to(), str_Admin_Entry()); + Admin_Entry::lastCurrentValue = 0; transit(); return; case not_allow: @@ -995,7 +1030,7 @@ void Admin_Entry::entry() { tonuino.disableStandbyTimer(); numberOfOptions = 13; - startMessage = mp3Tracks::t_900_admin; + startMessage = lastCurrentValue == 0 ? mp3Tracks::t_900_admin : mp3Tracks::t_919_continue_admin; messageOffset = mp3Tracks::t_900_admin; preview = false; previewFromFolder = 0; @@ -1003,6 +1038,8 @@ void Admin_Entry::entry() { tonuino.knownCard = false; VoiceMenu::entry(); + + currentValue = lastCurrentValue; }; void Admin_Entry::react(button_e const &b) { @@ -1020,6 +1057,7 @@ void Admin_Entry::react(button_e const &b) { } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + lastCurrentValue = currentValue; switch (currentValue) { case 0: break; case 1: // create new card @@ -1078,6 +1116,9 @@ void Admin_Entry::react(button_e const &b) { } }; +//using Admin_End = Idle; +using Admin_End = Admin_Entry; + // ####################################################### void Admin_NewCard::entry() { @@ -1124,7 +1165,7 @@ void Admin_NewCard::react(button_e const &b) { break; case end_writeCard: LOG(state_log, s_debug, str_Admin_NewCard(), str_to(), str_Idle()); - transit(); + transit(); return; default: break; @@ -1157,13 +1198,17 @@ void Admin_MaxVolume::react(button_e const &b) { transit(); return; } + if (isWaitForPlayFinish()) { + LOG(state_log, s_debug, str_Admin_MaxVolume(), str_to(), str_Idle()); + transit(); + return; + } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { settings.maxVolume = currentValue + settings.minVolume; settings.writeSettingsToFlash(); - mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); - LOG(state_log, s_debug, str_Admin_MaxVolume(), str_to(), str_Idle()); - transit(); + mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); + startWaitForPlayFinish(); } }; @@ -1193,13 +1238,17 @@ void Admin_MinVolume::react(button_e const &b) { transit(); return; } + if (isWaitForPlayFinish()) { + LOG(state_log, s_debug, str_Admin_MinVolume(), str_to(), str_Idle()); + transit(); + return; + } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { settings.minVolume = currentValue; settings.writeSettingsToFlash(); - mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); - LOG(state_log, s_debug, str_Admin_MinVolume(), str_to(), str_Idle()); - transit(); + mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); + startWaitForPlayFinish(); } }; @@ -1229,13 +1278,17 @@ void Admin_InitVolume::react(button_e const &b) { transit(); return; } + if (isWaitForPlayFinish()) { + LOG(state_log, s_debug, str_Admin_InitVolume(), str_to(), str_Idle()); + transit(); + return; + } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { settings.initVolume = currentValue + settings.minVolume - 1; settings.writeSettingsToFlash(); - mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); - LOG(state_log, s_debug, str_Admin_InitVolume(), str_to(), str_Idle()); - transit(); + mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); + startWaitForPlayFinish(); } }; @@ -1265,14 +1318,18 @@ void Admin_Eq::react(button_e const &b) { transit(); return; } + if (isWaitForPlayFinish()) { + LOG(state_log, s_debug, str_Admin_Eq(), str_to(), str_Idle()); + transit(); + return; + } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { settings.eq = currentValue; mp3.setEq(static_cast(settings.eq - 1)); settings.writeSettingsToFlash(); - mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); - LOG(state_log, s_debug, str_Admin_InitVolume(), str_to(), str_Idle()); - transit(); + mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); + startWaitForPlayFinish(); } }; @@ -1343,7 +1400,7 @@ void Admin_ModCard::react(button_e const &b) { break; case end_writeCard: LOG(state_log, s_debug, str_Admin_ModCard(), str_to(), str_Idle()); - transit(); + transit(); return; default: break; @@ -1396,8 +1453,19 @@ void Admin_ShortCut::react(button_e const &b) { transit(); return; } + if (isWaitForPlayFinish()) { + LOG(state_log, s_debug, str_Admin_ShortCut(), str_to(), str_Idle()); + transit(); + return; + } if (shortcut > 0) { + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + shortcut = currentValue; + current_subState = start_setupCard; + } + } + else { switch (current_subState) { case start_setupCard: SM_setupCard::start(); @@ -1415,19 +1483,14 @@ void Admin_ShortCut::react(button_e const &b) { break; case end_setupCard: settings.shortCuts[shortcut] = SM_setupCard::folder; - LOG(state_log, s_debug, str_Admin_ShortCut(), str_to(), str_Idle()); - mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); - transit(); - return; + mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); + startWaitForPlayFinish(); + break; default: break; } return; } - - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { - shortcut = currentValue; - } }; // ####################################################### @@ -1454,6 +1517,11 @@ void Admin_StandbyTimer::react(button_e const &b) { transit(); return; } + if (isWaitForPlayFinish()) { + LOG(state_log, s_debug, str_Admin_StandbyTimer(), str_to(), str_Idle()); + transit(); + return; + } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { switch (currentValue) { @@ -1464,9 +1532,8 @@ void Admin_StandbyTimer::react(button_e const &b) { case 5: settings.standbyTimer = 0; break; } settings.writeSettingsToFlash(); - mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); - LOG(state_log, s_debug, str_Admin_StandbyTimer(), str_to(), str_Idle()); - transit(); + mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); + startWaitForPlayFinish(); } }; @@ -1542,32 +1609,35 @@ void Admin_CardsForFolder::react(button_e const &b) { if ((b.b == buttonRaw::pause) && (currentValue != 0)) { special2 = currentValue; mp3.playMp3FolderTrack(mp3Tracks::t_936_batch_cards_intro); + timer.start(dfPlayer_timeUntilStarts); current_subState = prepare_writeCard; } break; case prepare_writeCard: - if (not mp3.isPlaying() && chip_card.isCardRemoved()) { + if (timer.isExpired() && not mp3.isPlaying() && chip_card.isCardRemoved()) { if (special > special2) { - mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + //mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); LOG(state_log, s_debug, str_Admin_CardsForFolder(), str_to(), str_Idle()); - transit(); + transit(); return; } folder.special = special; mp3.playMp3FolderTrack(special); + timer.start(dfPlayer_timeUntilStarts); LOG(card_log, s_info, special, F("-te Karte auflegen")); current_subState = start_writeCard; } break; case start_writeCard: - if (not mp3.isPlaying() && chip_card.isCardRemoved()) { + if (timer.isExpired() && not mp3.isPlaying() && chip_card.isCardRemoved()) { SM_writeCard::folder = folder; SM_writeCard::start(); + timer.start(dfPlayer_timeUntilStarts); current_subState = run_writeCard; } break; case run_writeCard: - if (not mp3.isPlaying()) { + if (timer.isExpired() && not mp3.isPlaying()) { SM_writeCard::dispatch(b); if (SM_writeCard::is_in_state()) { ++special; @@ -1609,6 +1679,11 @@ void Admin_InvButtons::react(button_e const &b) { transit(); return; } + if (isWaitForPlayFinish()) { + LOG(state_log, s_debug, str_Admin_InvButtons(), str_to(), str_Idle()); + transit(); + return; + } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { switch (currentValue) { @@ -1616,9 +1691,8 @@ void Admin_InvButtons::react(button_e const &b) { case 2: settings.invertVolumeButtons = true ; break; } settings.writeSettingsToFlash(); - mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); - LOG(state_log, s_debug, str_Admin_InvButtons(), str_to(), str_Idle()); - transit(); + mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); + startWaitForPlayFinish(); } }; @@ -1629,11 +1703,15 @@ void Admin_ResetEeprom::entry() { settings.clearEEPROM(); settings.resetSettings(); mp3.playMp3FolderTrack(mp3Tracks::t_999_reset_ok); + startWaitForPlayFinish(); }; void Admin_ResetEeprom::react(button_e const &/*b*/) { - LOG(state_log, s_debug, str_Admin_ResetEeprom(), str_to(), str_Idle()); - transit(); + if (isWaitForPlayFinish()) { + LOG(state_log, s_debug, str_Admin_ResetEeprom(), str_to(), str_Idle()); + transit(); + return; + } }; // ####################################################### @@ -1648,39 +1726,56 @@ void Admin_LockAdmin::entry() { previewFromFolder = 0; VoiceMenu::entry(); + + current_subState = get_mode; }; void Admin_LockAdmin::react(button_e const &b) { if (b.b != buttonRaw::none) { LOG(state_log, s_debug, str_Admin_LockAdmin(), F("::react() "), static_cast(b.b)); } - VoiceMenu::react(b); if (isAbort(b)) { transit(); return; } + if (isWaitForPlayFinish()) { + LOG(state_log, s_debug, str_Admin_LockAdmin(), str_to(), str_Idle()); + transit(); + return; + } - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { - switch (currentValue) { - case 1: - settings.adminMenuLocked = 0; - break; - case 2: - settings.adminMenuLocked = 1; - break; - case 3: - settings.adminMenuLocked = 2; - settings.adminMenuPin = { 1, 2, 1, 3 }; // TODO implement get key - break; -// case 4: -// settings.adminMenuLocked = 3; -// break; + switch(current_subState) { + case get_mode: + VoiceMenu::react(b); + if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + settings.adminMenuLocked = currentValue-1; + if (settings.adminMenuLocked == 2) { + current_subState = get_pin; + mp3.playMp3FolderTrack(mp3Tracks::t_991_admin_pin); + } + else + current_subState = finished; } + break; + case get_pin : + { + const uint8_t c = Buttons::getButtonCode(b.b); + if (c != 0) { + LOG(state_log, s_debug, F("pin: "), c); + pin[pin_number++] = c; + } + if (pin_number == 4) { + settings.adminMenuPin = pin; + current_subState = finished; + } + break; + } + case finished: settings.writeSettingsToFlash(); - mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); - LOG(state_log, s_debug, str_Admin_LockAdmin(), str_to(), str_Idle()); - transit(); + mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); + startWaitForPlayFinish(); + break; } }; @@ -1688,6 +1783,14 @@ void Admin_LockAdmin::react(button_e const &b) { void Admin_PauseIfCardRemoved::entry() { LOG(state_log, s_info, str_enter(), str_Admin_PauseIfCardRemoved()); + + numberOfOptions = 2; + startMessage = mp3Tracks::t_913_pause_on_card_removed; + messageOffset = mp3Tracks::t_933_switch_volume_intro; + preview = false; + previewFromFolder = 0; + + VoiceMenu::entry(); }; void Admin_PauseIfCardRemoved::react(button_e const &b) { @@ -1700,6 +1803,11 @@ void Admin_PauseIfCardRemoved::react(button_e const &b) { transit(); return; } + if (isWaitForPlayFinish()) { + LOG(state_log, s_debug, str_Admin_PauseIfCardRemoved(), str_to(), str_Idle()); + transit(); + return; + } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { switch (currentValue) { @@ -1707,9 +1815,8 @@ void Admin_PauseIfCardRemoved::react(button_e const &b) { case 2: settings.pauseWhenCardRemoved = true ; break; } settings.writeSettingsToFlash(); - mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); - LOG(state_log, s_debug, str_Admin_PauseIfCardRemoved(), str_to(), str_Idle()); - transit(); + mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); + startWaitForPlayFinish(); } }; @@ -1720,7 +1827,7 @@ FSM_INITIAL_STATE(SM_writeCard, WriteCard) FSM_INITIAL_STATE(SM_tonuino , Idle) template -folderSettings SM::folder = folderSettings{}; +folderSettings SM::folder{}; template Tonuino &SM::tonuino = Tonuino::getTonuino(); template @@ -1731,6 +1838,10 @@ template Settings &SM::settings = Tonuino::getTonuino().getSettings(); template Chip_card &SM::chip_card = Tonuino::getTonuino().getChipCard(); +template +Timer SM::timer{}; +template +bool SM::waitForPlayFinish{}; template int VoiceMenu::numberOfOptions ; @@ -1750,3 +1861,5 @@ Timer VoiceMenu::previewTimer ; template bool VoiceMenu::previewStarted ; +nfcTagObject Base::lastCardRead{}; +uint8_t Admin_Entry::lastCurrentValue = 0; diff --git a/src/state_machine.hpp b/src/state_machine.hpp index 663f3526..e05031d6 100644 --- a/src/state_machine.hpp +++ b/src/state_machine.hpp @@ -5,10 +5,9 @@ #include "buttons.hpp" #include "chip_card.hpp" #include "mp3.hpp" +#include "timer.hpp" -struct ChMode; class Tonuino; -class Mp3; // ---------------------------------------------------------------------------- // Event Declarations @@ -38,15 +37,22 @@ class SM: public tinyfsm::Fsm> virtual void react(card_e const &) { }; virtual void entry(void) { }; - void exit(void) { }; + void exit(void) { waitForPlayFinish = false; }; - static folderSettings folder; // TODO: put this into base of setupCard states + bool isAbort(button_e const &b); + bool isWaitForPlayFinish(); + void startWaitForPlayFinish(); + + static folderSettings folder; protected: static Tonuino &tonuino; static Mp3 &mp3; static Buttons &buttons; static Settings &settings; static Chip_card &chip_card; + + static Timer timer; + static bool waitForPlayFinish; }; typedef SM SM_tonuino; @@ -57,7 +63,7 @@ class Base: public SM_tonuino { protected: bool readCard(); - nfcTagObject tempCard; + static nfcTagObject lastCardRead; }; class Idle: public Base diff --git a/src/tonuino.cpp b/src/tonuino.cpp index 0b71b360..e9fb3f8f 100644 --- a/src/tonuino.cpp +++ b/src/tonuino.cpp @@ -62,7 +62,7 @@ void Tonuino::setup() { void Tonuino::loop() { - unsigned long start = millis(); + unsigned long start_cycle = millis(); checkStandbyAtMillis(); mp3.loop(); @@ -73,10 +73,10 @@ void Tonuino::loop() { SM_tonuino::dispatch(button_e(buttons.getButtonRaw())); SM_tonuino::dispatch(card_e(chip_card.getCardEvent())); - unsigned long stop = millis(); + unsigned long stop_cycle = millis(); - if (stop-start < cycleTime) - delay(cycleTime - (stop - start)); + if (stop_cycle-start_cycle < cycleTime) + delay(cycleTime - (stop_cycle - start_cycle)); } void Tonuino::playFolder() { @@ -179,8 +179,6 @@ void Tonuino::nextTrack() { return; if (not knownCard) - // Wenn eine neue Karte angelernt wird soll das Ende eines Tracks nicht - // verarbeitet werden return; LOG(play_log, s_info, F("= nextTrack()")); @@ -200,9 +198,11 @@ void Tonuino::nextTrack() { if (currentTrack != numTracksInFolder) { ++currentTrack; mp3.playFolderTrack(myFolder->folder, currentTrack); - LOG(play_log, s_info, str_Album(), F(" -> nächster Track: "), currentTrack); + LOG(play_log, s_info, str_Album(), F(" -> next")); + LOG(play_log, s_info, str_Track(), currentTrack); } else { - // mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! + LOG(play_log, s_info, str_Album(), F(" -> stop")); + //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! knownCard = false; } break; @@ -210,17 +210,20 @@ void Tonuino::nextTrack() { case mode_t::party : case mode_t::party_vb: if (currentTrack != numTracksInFolder - firstTrack + 1) { - LOG(play_log, s_info, str_Party(), F(" -> weiter in der Queue ")); ++currentTrack; + LOG(play_log, s_info, str_Party(), F(" -> next in queue")); } else { - LOG(play_log, s_info, str_Party(), F(", Ende der Queue -> beginne von vorne")); currentTrack = 1; + LOG(play_log, s_info, str_Party(), F(" -> end, start again")); //// Wenn am Ende der Queue neu gemischt werden soll bitte die Zeilen wieder aktivieren //LOG(play_log, s_info, F("Ende der Queue -> mische neu")); //shuffleQueue(); } - LOG(play_log, s_info, str_Track(), queue[currentTrack - 1]); - mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); + { + const uint16_t track = queue[currentTrack - 1]; + LOG(play_log, s_info, str_Track(), track); + mp3.playFolderTrack(myFolder->folder, track); + } break; case mode_t::einzel: @@ -229,34 +232,30 @@ void Tonuino::nextTrack() { knownCard = false; break; + case mode_t::hoerbuch_1: + knownCard = false; + LOG(play_log, s_info, str_Hoerbuch(), F(" single -> stop and save")); + __attribute__((fallthrough)); + // no break by intention case mode_t::hoerbuch: - if (currentTrack != numTracksInFolder) { + if (currentTrack < numTracksInFolder) { ++currentTrack; - LOG(play_log, s_info, str_Hoerbuch(), F(" -> nächster Track und Fortschr. speichern")); - LOG(play_log, s_info, str_Track(), currentTrack); - mp3.playFolderTrack(myFolder->folder, currentTrack); - // Fortschritt im EEPROM abspeichern - settings.writeFolderSettingToFlash(myFolder->folder, currentTrack); - } else { - LOG(play_log, s_info, str_Hoerbuch(), F(" -> Ende")); - //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! - // Fortschritt zurück setzen - settings.writeFolderSettingToFlash(myFolder->folder, 1); - knownCard = false; + if (knownCard) { + LOG(play_log, s_info, str_Hoerbuch(), F(" -> next and save")); + mp3.playFolderTrack(myFolder->folder, currentTrack); + } } - break; - case mode_t::hoerbuch_1: - if (currentTrack != numTracksInFolder) - ++currentTrack; - else + else { currentTrack = 1; - - LOG(play_log, s_info, str_Hoerbuch(), F(" single -> Fortschritt speichern und beenden")); + if (knownCard) { + LOG(play_log, s_info, str_Hoerbuch(), F(" -> stop and save")); + knownCard = false; + } + } LOG(play_log, s_info, str_Track(), currentTrack); // Fortschritt im EEPROM abspeichern settings.writeFolderSettingToFlash(myFolder->folder, currentTrack); //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! - knownCard = false; break; default: break; @@ -270,40 +269,46 @@ void Tonuino::previousTrack() { switch (myFolder->mode) { case mode_t::hoerspiel: case mode_t::hoerspiel_vb: - LOG(play_log, s_info, str_Hoerspiel(), F(" -> Track von vorne spielen")); + LOG(play_log, s_info, str_Hoerspiel(), F(" -> restart")); + LOG(play_log, s_info, str_Track(), currentTrack); mp3.playFolderTrack(myFolder->folder, currentTrack); break; case mode_t::album: case mode_t::album_vb: - LOG(play_log, s_info, str_Album(), F(" -> vorheriger Track")); + LOG(play_log, s_info, str_Album(), F(" -> previous")); if (currentTrack != firstTrack) { --currentTrack; } + LOG(play_log, s_info, str_Track(), currentTrack); mp3.playFolderTrack(myFolder->folder, currentTrack); break; case mode_t::party: case mode_t::party_vb: if (currentTrack != 1) { - LOG(play_log, s_info, str_Party(), F(" -> zurück in der Qeueue ")); + LOG(play_log, s_info, str_Party(), F(" -> previous in queue ")); --currentTrack; } else { - LOG(play_log, s_info, str_Party(), F(", Anfang der Queue -> springe ans Ende ")); + LOG(play_log, s_info, str_Party(), F(", beginning of queue -> go to end")); currentTrack = numTracksInFolder - firstTrack + 1; } - LOG(play_log, s_info, str_Track(), queue[currentTrack - 1]); - mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]); + { + const uint16_t track = queue[currentTrack - 1]; + LOG(play_log, s_info, str_Track(), track); + mp3.playFolderTrack(myFolder->folder, track); + } break; case mode_t::einzel: - LOG(play_log, s_info, str_Einzel(), F(" -> Track von vorne spielen")); + LOG(play_log, s_info, str_Einzel(), F(" -> restart")); + LOG(play_log, s_info, str_Track(), currentTrack); mp3.playFolderTrack(myFolder->folder, currentTrack); break; case mode_t::hoerbuch: case mode_t::hoerbuch_1: - LOG(play_log, s_info, str_Hoerbuch(), F(" -> vorheriger Track und Fortschr. speichern")); + LOG(play_log, s_info, str_Hoerbuch(), F(" -> previous and save")); if (currentTrack != 1) { --currentTrack; } @@ -328,7 +333,7 @@ void Tonuino::setStandbyTimer() { } void Tonuino::disableStandbyTimer() { - LOG(standby_log, s_info, F("= disablestandby()")); + LOG(standby_log, s_info, F("= disableStandbyTimer()")); if (settings.standbyTimer != 0) { standbyTimer.stop(); LOG(standby_log, s_info, F("timer stopped")); @@ -412,9 +417,9 @@ void Tonuino::shuffleQueue() { const uint8_t j = random(0, numTracksInFolder - firstTrack + 1); swap(queue[i], queue[j]); } -// LOG(menu_log, s_info, F("Queue :")); -// for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1 ; ++x) -// LOG(menu_log, s_info, queue[x]); + LOG(play_log, s_debug, F("Queue :")); + for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1 ; ++x) + LOG(play_log, s_debug, queue[x]); } From f4b72944f9da32621a68201f2bc9ef9610e7a679 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Sun, 30 Jan 2022 12:50:46 +0100 Subject: [PATCH 16/32] new state machine * improvements and fixes --- src/chip_card.cpp | 43 +++++++++++++++++++++++++------------------ src/constants.hpp | 2 ++ src/log.cpp | 8 ++++---- src/state_machine.cpp | 35 +++++++++++++++++------------------ 4 files changed, 48 insertions(+), 40 deletions(-) diff --git a/src/chip_card.cpp b/src/chip_card.cpp index 6e215786..3ce39fca 100644 --- a/src/chip_card.cpp +++ b/src/chip_card.cpp @@ -15,20 +15,29 @@ constexpr bool verbosePrintPiccType = false; namespace { -const __FlashStringHelper* str_failed () { return F("failed: ") ; } +const __FlashStringHelper* str_failed () { return F(" failed: ") ; } const __FlashStringHelper* str_MIFARE_Read() { return F("MIFARE_Read ") ; } /** Helper routine to dump a byte array as hex values to Serial. */ -String dump_byte_array(byte * buffer, size_t bufferSize) { - String res((char *)0); - res.reserve(3*bufferSize); +const char* dump_byte_array(byte * buffer, size_t bufferSize) { + static char ret[3*10+1]; + ret[0] = '\0'; + if (bufferSize > 10) + return ret; + size_t pos = 0; for (byte i = 0; i < bufferSize; ++i) { - res += String(buffer[i] < 0x10 ? " 0" : " "); - res += String(buffer[i], HEX); + const bool pad = buffer[i] < 0x10; + ret[pos++] = ' '; + if (pad) + ret[pos++] = '0'; + utoa(buffer[i], ret+(pos++), HEX); + if (!pad) + ++pos; } - return res; + ret[pos] = '\0'; + return ret; } auto printStatusCode(MFRC522& mfrc522, MFRC522::StatusCode status) { @@ -49,15 +58,13 @@ auto printPiccType(MFRC522& mfrc522, MFRC522::PICC_Type piccType) { MFRC522::MIFARE_Key key{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; const byte sector = 1; const byte trailerBlock = 7; - -const unsigned int removeDelay = 3; } // namespace Chip_card::Chip_card(Mp3 &mp3, Buttons &buttons) : mfrc522(mfrc522_SSPin, mfrc522_RSTPin) , mp3(mp3) , buttons(buttons) -, cardRemovedSwitch(removeDelay) +, cardRemovedSwitch(cardRemoveDelay) {} bool Chip_card::auth(MFRC522::PICC_Type piccType) { @@ -90,7 +97,7 @@ bool Chip_card::auth(MFRC522::PICC_Type piccType) { bool Chip_card::readCard(nfcTagObject &nfcTag) { // Show some details of the PICC (that is: the tag/card) - LOG(card_log, s_info, F("Card UID: "), dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size).c_str()); + LOG(card_log, s_info, F("Card UID: "), dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size)); const MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); LOG(card_log, s_info, F("PICC type: "), printPiccType(mfrc522, piccType)); @@ -113,7 +120,7 @@ bool Chip_card::readCard(nfcTagObject &nfcTag) { byte size = sizeof(buffer); status = static_cast(mfrc522.MIFARE_Read(4, buffer, &size)); if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, str_MIFARE_Read(), F("4 "), str_failed(), printStatusCode(mfrc522, status)); + LOG(card_log, s_error, str_MIFARE_Read(), F("4"), str_failed(), printStatusCode(mfrc522, status)); return false; } } @@ -124,35 +131,35 @@ bool Chip_card::readCard(nfcTagObject &nfcTag) { status = static_cast(mfrc522.MIFARE_Read(8, buffer2, &size2)); if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, str_MIFARE_Read(), F("8 "), str_failed(), printStatusCode(mfrc522, status)); + LOG(card_log, s_error, str_MIFARE_Read(), F("8"), str_failed(), printStatusCode(mfrc522, status)); return false; } memcpy(buffer, buffer2, 4); status = static_cast(mfrc522.MIFARE_Read(9, buffer2, &size2)); if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, str_MIFARE_Read(), F("9 "), str_failed(), printStatusCode(mfrc522, status)); + LOG(card_log, s_error, str_MIFARE_Read(), F("9"), str_failed(), printStatusCode(mfrc522, status)); return false; } memcpy(buffer + 4, buffer2, 4); status = static_cast(mfrc522.MIFARE_Read(10, buffer2, &size2)); if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, str_MIFARE_Read(), F("10 "), str_failed(), printStatusCode(mfrc522, status)); + LOG(card_log, s_error, str_MIFARE_Read(), F("10"), str_failed(), printStatusCode(mfrc522, status)); return false; } memcpy(buffer + 8, buffer2, 4); status = static_cast(mfrc522.MIFARE_Read(11, buffer2, &size2)); if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, str_MIFARE_Read(), F("11 "), str_failed(), printStatusCode(mfrc522, status)); + LOG(card_log, s_error, str_MIFARE_Read(), F("11"), str_failed(), printStatusCode(mfrc522, status)); return false; } memcpy(buffer + 12, buffer2, 4); } stopCrypto1(); - LOG(card_log, s_info, F("Data on Card: "), dump_byte_array(buffer, 9).c_str()); + LOG(card_log, s_info, F("Data on Card: "), dump_byte_array(buffer, 9)); uint32_t tempCookie; tempCookie = (uint32_t)buffer[0] << 24; @@ -200,7 +207,7 @@ bool Chip_card::writeCard(const nfcTagObject &nfcTag) { MFRC522::StatusCode status = MFRC522::STATUS_ERROR; // Write data to the block - LOG(card_log, s_info, F("Writing data: "), dump_byte_array(buffer, 9).c_str()); + LOG(card_log, s_info, F("Writing data: "), dump_byte_array(buffer, 9)); if ((mifareType == MFRC522::PICC_TYPE_MIFARE_MINI ) || (mifareType == MFRC522::PICC_TYPE_MIFARE_1K ) || diff --git a/src/constants.hpp b/src/constants.hpp index 3c32a3f2..864244c2 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -41,6 +41,8 @@ inline constexpr uint32_t cardCookie = 0x1337b347; inline constexpr uint8_t cardVersion = 0x02; inline constexpr byte mfrc522_RSTPin = 9; // Configurable, see typical pin layout above inline constexpr byte mfrc522_SSPin = 10; // Configurable, see typical pin layout above +inline constexpr unsigned int cardRemoveDelay = 3; + // ####### mp3 ######################################### diff --git a/src/log.cpp b/src/log.cpp index 29053046..cdb3e802 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -2,10 +2,10 @@ const __FlashStringHelper* getSeverityName(severity sev) { switch (sev) { - case s_debug : return F("debug" ); - case s_info : return F("info" ); - case s_warning: return F("warning"); - case s_error : return F("error" ); + case s_debug : return F("D"); + case s_info : return F("I"); + case s_warning: return F("W"); + case s_error : return F("E"); } return F("unknown"); } diff --git a/src/state_machine.cpp b/src/state_machine.cpp index c57dd7c5..dd5d126e 100644 --- a/src/state_machine.cpp +++ b/src/state_machine.cpp @@ -15,21 +15,21 @@ const __FlashStringHelper* str_Idle () { return F("Idle") ; } const __FlashStringHelper* str_StartPlay () { return F("StartPlay") ; } const __FlashStringHelper* str_Play () { return F("Play") ; } const __FlashStringHelper* str_Pause () { return F("Pause") ; } -const __FlashStringHelper* str_Admin_Allow () { return F("Admin_Allow") ; } -const __FlashStringHelper* str_Admin_Entry () { return F("Admin_Entry") ; } -const __FlashStringHelper* str_Admin_NewCard () { return F("Admin_NewCard") ; } -const __FlashStringHelper* str_Admin_MaxVolume () { return F("Admin_MaxVolume") ; } -const __FlashStringHelper* str_Admin_MinVolume () { return F("Admin_MinVolume") ; } -const __FlashStringHelper* str_Admin_InitVolume () { return F("Admin_InitVolume") ; } -const __FlashStringHelper* str_Admin_Eq () { return F("Admin_Eq") ; } -const __FlashStringHelper* str_Admin_ModCard () { return F("Admin_ModCard") ; } -const __FlashStringHelper* str_Admin_ShortCut () { return F("Admin_ShortCut") ; } -const __FlashStringHelper* str_Admin_StandbyTimer () { return F("Admin_StandbyTimer") ; } -const __FlashStringHelper* str_Admin_CardsForFolder () { return F("Admin_CardsForFolder") ; } -const __FlashStringHelper* str_Admin_InvButtons () { return F("Admin_InvButtons") ; } -const __FlashStringHelper* str_Admin_ResetEeprom () { return F("Admin_ResetEeprom") ; } -const __FlashStringHelper* str_Admin_LockAdmin () { return F("Admin_LockAdmin") ; } -const __FlashStringHelper* str_Admin_PauseIfCardRemoved() { return F("Admin_PauseIfCardRemoved") ; } +const __FlashStringHelper* str_Admin_Allow () { return F("AdmAllow") ; } +const __FlashStringHelper* str_Admin_Entry () { return F("AdmEntry") ; } +const __FlashStringHelper* str_Admin_NewCard () { return F("AdmNewCard") ; } +const __FlashStringHelper* str_Admin_MaxVolume () { return F("AdmMaxVolume") ; } +const __FlashStringHelper* str_Admin_MinVolume () { return F("AdmMinVolume") ; } +const __FlashStringHelper* str_Admin_InitVolume () { return F("AdmInitVolume") ; } +const __FlashStringHelper* str_Admin_Eq () { return F("AdmEq") ; } +const __FlashStringHelper* str_Admin_ModCard () { return F("AdmModCard") ; } +const __FlashStringHelper* str_Admin_ShortCut () { return F("AdmShortCut") ; } +const __FlashStringHelper* str_Admin_StandbyTimer () { return F("AdmStandbyTimer") ; } +const __FlashStringHelper* str_Admin_CardsForFolder () { return F("AdmCardsForFolder") ; } +const __FlashStringHelper* str_Admin_InvButtons () { return F("AdmInvButtons") ; } +const __FlashStringHelper* str_Admin_ResetEeprom () { return F("AdmResetEeprom") ; } +const __FlashStringHelper* str_Admin_LockAdmin () { return F("AdmLockAdmin") ; } +const __FlashStringHelper* str_Admin_PauseIfCardRemoved() { return F("AdmPauseIfCardRem") ; } const __FlashStringHelper* str_VoiceMenu () { return F("VoiceMenu") ; } const __FlashStringHelper* str_to () { return F(" -> ") ; } const __FlashStringHelper* str_enter () { return F("enter ") ; } @@ -301,7 +301,6 @@ bool SM::isAbort(button_e const &b) { template bool SM::isWaitForPlayFinish() { if (waitForPlayFinish && timer.isExpired() && not mp3.isPlaying()) { - waitForPlayFinish = false; return true; } return false; @@ -370,7 +369,7 @@ void VoiceMenu::react(button_e const &b) { break; } if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_VoiceMenu(), F("::react() currentValue: "), currentValue); + LOG(state_log, s_info, str_VoiceMenu(), F(" currentVal: "), currentValue); } }; @@ -1841,7 +1840,7 @@ Chip_card &SM::chip_card = Tonuino::getTonuino().getChipCard(); template Timer SM::timer{}; template -bool SM::waitForPlayFinish{}; +bool SM::waitForPlayFinish = false; template int VoiceMenu::numberOfOptions ; From 1910368ba666e9539fbad5f2fd4fbc52628dcb22 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Sun, 30 Jan 2022 14:46:50 +0100 Subject: [PATCH 17/32] new state machine * remove remaining delays --- src/chip_card.hpp | 8 ++++---- src/modifier.cpp | 3 +-- src/mp3.cpp | 1 - src/tonuino.cpp | 5 ----- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/chip_card.hpp b/src/chip_card.hpp index 41bd0792..1b4c8231 100644 --- a/src/chip_card.hpp +++ b/src/chip_card.hpp @@ -70,14 +70,14 @@ class Buttons; class delayedSwitchOn { public: delayedSwitchOn(unsigned int delay) - : delay(delay) + : delaySteps(delay) {} - delayedSwitchOn& operator++() { if (counter < delay) ++counter; return *this; } + delayedSwitchOn& operator++() { if (counter < delaySteps) ++counter; return *this; } void reset() { counter = 0; } - bool on() { return counter == delay; } + bool on() { return counter == delaySteps; } private: - const unsigned int delay; + const unsigned int delaySteps; unsigned int counter = 0; }; diff --git a/src/modifier.cpp b/src/modifier.cpp index a683a977..32d6ca21 100644 --- a/src/modifier.cpp +++ b/src/modifier.cpp @@ -68,8 +68,7 @@ bool KindergardenMode::handleRFID(const nfcTagObject &newCard) { } bool RepeatSingleModifier::handleNext() { - LOG(modifier_log, s_debug, str_RepeatSingleModifier(), F(" -> REPEAT")); - delay(50); // TODO remove delay() + LOG(modifier_log, s_info, str_RepeatSingleModifier(), F(" -> REPEAT")); if (!mp3.isPlaying()) { mp3.loop(); // this will call Mp3Notify::OnPlayFinished() but will be blocked by lastTrackFinished Mp3Notify::ResetLastTrackFinished(); diff --git a/src/mp3.cpp b/src/mp3.cpp index 53284d10..e00cfb8d 100644 --- a/src/mp3.cpp +++ b/src/mp3.cpp @@ -79,7 +79,6 @@ void Mp3::playMp3FolderTrack(mp3Tracks track) { void Mp3::playAdvertisement(uint16_t track, bool olnyIfIsPlaying) { if (isPlaying()) { DFMiniMp3::playAdvertisement(track); - delay(500); // TODO remove delay() } else if (not olnyIfIsPlaying) { start(); DFMiniMp3::playAdvertisement(track); diff --git a/src/tonuino.cpp b/src/tonuino.cpp index e9fb3f8f..ced4df09 100644 --- a/src/tonuino.cpp +++ b/src/tonuino.cpp @@ -159,8 +159,6 @@ void Tonuino::playFolder() { return; } playCurrentTrack(); - if (knownCard && settings.pauseWhenCardRemoved) - mp3.waitForTrackToStart(); // TODO remove waitForTrackToStart } void Tonuino::playTrackNumber () { @@ -260,7 +258,6 @@ void Tonuino::nextTrack() { default: break; } - delay(500); // TODO remove delay } void Tonuino::previousTrack() { @@ -320,7 +317,6 @@ void Tonuino::previousTrack() { default: break; } - delay(500); // TODO remove delay } // Funktionen für den Standby Timer (z.B. über Pololu-Switch oder Mosfet) @@ -399,7 +395,6 @@ bool Tonuino::specialCard(const nfcTagObject &nfcTag) { } if (oldModifier != activeModifier) activeModifier->init(); - delay(2000); // TODO remove delay return true; } From 0591b1510d7056905e929ebcbd4f8454515a9971 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Sun, 30 Jan 2022 15:09:35 +0100 Subject: [PATCH 18/32] new state machine * Change version and Readme --- README.md | 9 +++++++++ Tonuino.ino | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a9a56be3..1eec07f1 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,15 @@ Die DIY Musikbox (nicht nur) für Kinder # Change Log +## Version 3.0 (xx.xx.xxxx) noch WIP +- vollständiges Refactoring mit State Machine +- Die Main-Loop läuft jetzt stabil mit 50 ms +- Neues Feature: neuer Mode: Hörbuch einzeln (nur ein Titel wird gespielt und Fortschritt gespeichert +- Neues Feature: Pause, wenn Karte entfernt wird (lässt sich per Einstellungen steuern) +- Das Admin Menü wir nach einer Einstellung nicht verlassen (kann in der Software geändert leicht werden) +- Das Admin Menü kan an jeder Stelle abgrbrochen werden +- Viele weitere Verbesserungen und Bug Fixes + ## Version 2.1 (xx.xx.xxxx) noch WIP - Partymodus hat nun eine Queue -> jedes Lied kommt nur genau 1x vorkommt - Neue Wiedergabe-Modi "Spezialmodus Von-Bis" - Hörspiel, Album und Party -> erlaubt z.B. verschiedene Alben in einem Ordner zu haben und je mit einer Karte zu verknüpfen diff --git a/Tonuino.ino b/Tonuino.ino index 13f46aed..16d98485 100644 --- a/Tonuino.ino +++ b/Tonuino.ino @@ -12,7 +12,7 @@ |_ _|___ ___| | | | | | | | | | . | | | |- -| | | | | | |_| |___|_|_|_____|_____|_|___|_____| - TonUINO Version 2.1 + TonUINO Version 3.0 created by Thorsten Voß and licensed under GNU/GPL. refactored by Boerge1 @@ -37,9 +37,9 @@ void setup() { LOG(init_log, s_debug, F("|_ _|___ ___| | | | | | |")); LOG(init_log, s_debug, F(" | | | . | | | |- -| | | | | |")); LOG(init_log, s_debug, F(" |_| |___|_|_|_____|_____|_|___|_____|\n")); - LOG(init_log, s_debug, F("TonUINO Version 3.0")); - LOG(init_log, s_debug, F("created by Thorsten Voß and licensed under GNU/GPL.")); - LOG(init_log, s_debug, F("refactored by Boerge1.")); + LOG(init_log, s_info , F("TonUINO Version 3.0")); + LOG(init_log, s_info , F("created by Thorsten Voß and licensed under GNU/GPL.")); + LOG(init_log, s_info , F("refactored by Boerge1.")); LOG(init_log, s_debug, F("Information and contribution at https://tonuino.de.\n")); Tonuino::getTonuino().setup(); From 806831f14674f1d487b32e690830aca8f7a50f48 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Mon, 21 Feb 2022 15:20:01 +0100 Subject: [PATCH 19/32] queue all track and improvements --- src/array.hpp | 13 +- src/buttons.hpp | 4 +- src/chip_card.cpp | 2 +- src/chip_card.hpp | 8 +- src/constants.hpp | 9 +- src/log.hpp | 2 +- src/modifier.cpp | 24 +- src/modifier.hpp | 5 +- src/mp3.cpp | 145 +++++++++-- src/mp3.hpp | 55 ++++- src/queue.hpp | 41 +++ src/state_machine.cpp | 563 ++++++++++++++++-------------------------- src/state_machine.hpp | 4 +- src/tonuino.cpp | 305 +++++------------------ src/tonuino.hpp | 35 +-- 15 files changed, 545 insertions(+), 670 deletions(-) create mode 100644 src/queue.hpp diff --git a/src/array.hpp b/src/array.hpp index e32d8c8d..b374537c 100644 --- a/src/array.hpp +++ b/src/array.hpp @@ -1,17 +1,18 @@ #ifndef SRC_ARRAY_HPP_ #define SRC_ARRAY_HPP_ -template +template struct array { + static_assert(N<=0xff); // Storage T data[N]; - static constexpr size_t length() { return N; } + static constexpr uint8_t length() { return N; } using type = T; // Item access - T &operator[](size_t index) { return data[index]; } - const T &operator[](size_t index) const { return data[index]; } + T &operator[](uint8_t index) { return data[index]; } + const T &operator[](uint8_t index) const { return data[index]; } // Iterators T *begin() { return &data[0]; } @@ -23,7 +24,7 @@ struct array { bool operator==(const array &rhs) const { if (this == &rhs) return true; - for (size_t i = 0; i < N; i++) + for (uint8_t i = 0; i < N; i++) if ((*this)[i] != rhs[i]) return false; return true; @@ -33,7 +34,7 @@ struct array { } array& operator=(const array &rhs) { if (this != &rhs) - for (size_t i = 0; i < N; i++) + for (uint8_t i = 0; i < N; i++) (*this)[i] = rhs[i]; return *this; } diff --git a/src/buttons.hpp b/src/buttons.hpp index 18b0b4f0..04eec2a3 100644 --- a/src/buttons.hpp +++ b/src/buttons.hpp @@ -7,7 +7,7 @@ #include "settings.hpp" #include "constants.hpp" -enum class buttonRaw { +enum class buttonRaw: uint8_t { none, pause, pauseLong, @@ -23,7 +23,7 @@ enum class buttonRaw { start, }; -enum class buttonCmd { +enum class buttonCmd: uint8_t { none, admin, pause, diff --git a/src/chip_card.cpp b/src/chip_card.cpp index 3ce39fca..8654fa54 100644 --- a/src/chip_card.cpp +++ b/src/chip_card.cpp @@ -10,7 +10,7 @@ // select whether StatusCode and PiccType are printed as names // that uses about 690 bytes or 2.2% of flash -constexpr bool verbosePrintStatusCode = false; +constexpr bool verbosePrintStatusCode = true; constexpr bool verbosePrintPiccType = false; namespace { diff --git a/src/chip_card.hpp b/src/chip_card.hpp index 1b4c8231..5d5dc50b 100644 --- a/src/chip_card.hpp +++ b/src/chip_card.hpp @@ -58,7 +58,7 @@ struct nfcTagObject { } }; -enum class cardEvent { +enum class cardEvent: uint8_t { none, removed, inserted, @@ -69,7 +69,7 @@ class Buttons; class delayedSwitchOn { public: - delayedSwitchOn(unsigned int delay) + delayedSwitchOn(uint8_t delay) : delaySteps(delay) {} delayedSwitchOn& operator++() { if (counter < delaySteps) ++counter; return *this; } @@ -77,8 +77,8 @@ class delayedSwitchOn { bool on() { return counter == delaySteps; } private: - const unsigned int delaySteps; - unsigned int counter = 0; + const uint8_t delaySteps; + uint8_t counter = 0; }; class Chip_card { diff --git a/src/constants.hpp b/src/constants.hpp index 864244c2..d9f87a97 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -5,11 +5,11 @@ // ####### helper for level ############################ -enum class level { +enum class level: uint8_t { inactive, active , }; -enum class levelType { +enum class levelType: uint8_t { activeHigh, activeLow, }; @@ -41,16 +41,17 @@ inline constexpr uint32_t cardCookie = 0x1337b347; inline constexpr uint8_t cardVersion = 0x02; inline constexpr byte mfrc522_RSTPin = 9; // Configurable, see typical pin layout above inline constexpr byte mfrc522_SSPin = 10; // Configurable, see typical pin layout above -inline constexpr unsigned int cardRemoveDelay = 3; +inline constexpr uint8_t cardRemoveDelay = 3; // ####### mp3 ######################################### +inline constexpr uint8_t maxTracksInFolder = 255; inline constexpr uint8_t dfPlayer_receivePin = 2; inline constexpr uint8_t dfPlayer_transmitPin = 3; inline constexpr uint8_t dfPlayer_busyPin = 4; inline constexpr levelType dfPlayer_busyPinType = levelType::activeHigh; -inline constexpr unsigned long dfPlayer_timeUntilStarts = 300; +inline constexpr unsigned long dfPlayer_timeUntilStarts = 500; // ####### tonuino ##################################### diff --git a/src/log.hpp b/src/log.hpp index 85b91746..925a9638 100644 --- a/src/log.hpp +++ b/src/log.hpp @@ -17,7 +17,7 @@ if constexpr ( Logger_::will_log(Severity_) ) \ Statement -enum severity { +enum severity: uint8_t { s_debug , s_info , s_warning, diff --git a/src/modifier.cpp b/src/modifier.cpp index 32d6ca21..f45c7d01 100644 --- a/src/modifier.cpp +++ b/src/modifier.cpp @@ -58,24 +58,28 @@ bool KindergardenMode::handleNext() { return false; } bool KindergardenMode::handleRFID(const nfcTagObject &newCard) { - LOG(modifier_log, s_info, str_KindergardenMode(), F(" -> queued!")); - nextCard = newCard; - cardQueued = true; - if (!mp3.isPlaying()) { - handleNext(); + if (!mp3.isPlaying()) + return false; + + if (!cardQueued) { + LOG(modifier_log, s_info, str_KindergardenMode(), F(" -> queued!")); + nextCard = newCard; + cardQueued = true; } return true; } bool RepeatSingleModifier::handleNext() { LOG(modifier_log, s_info, str_RepeatSingleModifier(), F(" -> REPEAT")); - if (!mp3.isPlaying()) { - mp3.loop(); // this will call Mp3Notify::OnPlayFinished() but will be blocked by lastTrackFinished - Mp3Notify::ResetLastTrackFinished(); - tonuino.playCurrentTrack(); - } + mp3.loop(); // WA: this will call again Mp3Notify::OnPlayFinished() (error in DFMiniMp3 lib) + // but will be blocked by lastTrackFinished + Mp3Notify::ResetLastTrackFinished(); // unblock this track so that it can be repeated + mp3.playCurrent(); return true; } +bool RepeatSingleModifier::handlePrevious() { + return handleNext(); +} //bool FeedbackModifier::handleVolumeDown() { // if (volume > settings.minVolume) { diff --git a/src/modifier.hpp b/src/modifier.hpp index 92a511dc..e74d5650 100644 --- a/src/modifier.hpp +++ b/src/modifier.hpp @@ -107,8 +107,9 @@ class KindergardenMode: public Modifier { class RepeatSingleModifier: public Modifier { public: RepeatSingleModifier(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} - bool handleNext() final; - mode_t getActive () final { return mode_t::repeat_single; } + bool handleNext () final; + bool handlePrevious() final; + mode_t getActive () final { return mode_t::repeat_single; } }; // An modifier can also do somethings in addition to the modified action diff --git a/src/mp3.cpp b/src/mp3.cpp index e00cfb8d..51ec3ca3 100644 --- a/src/mp3.cpp +++ b/src/mp3.cpp @@ -30,11 +30,12 @@ void Mp3Notify::OnPlayFinished(DfMp3_PlaySources /*source*/, uint16_t track) { return; else lastTrackFinished = track; - Tonuino::getTonuino().nextTrack(); + delay(1); + Tonuino::getTonuino().nextTrack(true/*fromOnPlayFinished*/); } Mp3::Mp3(const Settings& settings) -: DFMiniMp3{softwareSerial} +: Base{softwareSerial} , softwareSerial{dfPlayer_receivePin, dfPlayer_transmitPin} , settings{settings} { @@ -68,20 +69,13 @@ void Mp3::waitForTrackToStart() { } while (!isPlaying() && millis() < currentTime + maxStartTime); } -void Mp3::playMp3FolderTrack(uint16_t track) { - DFMiniMp3::playMp3FolderTrack(track); -} - -void Mp3::playMp3FolderTrack(mp3Tracks track) { - DFMiniMp3::playMp3FolderTrack(static_cast(track)); -} - void Mp3::playAdvertisement(uint16_t track, bool olnyIfIsPlaying) { + LOG(mp3_log, s_info, F("play adv: "), track); if (isPlaying()) { - DFMiniMp3::playAdvertisement(track); + Base::playAdvertisement(track); } else if (not olnyIfIsPlaying) { start(); - DFMiniMp3::playAdvertisement(track); + Base::playAdvertisement(track); waitForTrackToFinish(); // TODO remove waitForTrackToFinish pause(); } @@ -91,23 +85,144 @@ void Mp3::playAdvertisement(advertTracks track, bool olnyIfIsPlaying) { playAdvertisement(static_cast(track), olnyIfIsPlaying); } +void Mp3::clearFolderQueue() { + LOG(mp3_log, s_info, F("clear folder")); + if (playing == play_folder) playing = play_none; + current_track = 0; + current_folder = 0; + q.clear(); +} +void Mp3::clearMp3Queue() { + LOG(mp3_log, s_info, F("clear mp3")); + if (playing == play_mp3) playing = play_none; + mp3_track = 0; + mp3_track_next = 0; +} +void Mp3::enqueueTrack(uint8_t folder, uint8_t firstTrack, uint8_t lastTrack, uint8_t currentTrack) { + clearAllQueue(); + current_folder = folder; + for (uint8_t i = firstTrack; i<=lastTrack; ++i) { + LOG(mp3_log, s_info, F("enqueue "), folder, F("-"), i); + q.push(i); + } + current_track = currentTrack; +} +void Mp3::enqueueTrack(uint8_t folder, uint8_t track) { + enqueueTrack(folder, track, track); +} +void Mp3::shuffleQueue() { + q.shuffle(); + for (uint8_t i = 0; i(track), playAfter); +} + +void Mp3::playCurrent() { + LOG(mp3_log, s_info, F("play current")); + if (current_folder == 0) { // maybe play mp3 track + if (mp3_track != 0) { + LOG(mp3_log, s_info, F("play mp3 "), mp3_track); + Mp3Notify::ResetLastTrackFinished(); // maybe the same mp3 track is played twice + Base::playMp3FolderTrack(mp3_track); +#ifdef CHECK_MISSING_ONPLAYFINISHED + isPause = false; + startTrackTimer.start(dfPlayer_timeUntilStarts); +#endif + playing = play_mp3; + mp3_track = 0; + swap(mp3_track, mp3_track_next); + } + } + else { // play folder track + uint8_t t = q.get(current_track); + if (t != 0) { + LOG(mp3_log, s_info, F("play "), current_folder, F("-"), t); + Base::playFolderTrack(current_folder, t); +#ifdef CHECK_MISSING_ONPLAYFINISHED + isPause = false; + startTrackTimer.start(dfPlayer_timeUntilStarts); +#endif + playing = play_folder; + } + } +} +void Mp3::playNext() { + if (playing == play_folder && current_track+1 < q.size()) { + ++current_track; + LOG(mp3_log, s_debug, F("playNext: "), current_track); + playCurrent(); + } + else if (playing == play_mp3 && mp3_track != 0) { + LOG(mp3_log, s_debug, F("playNext mp3: "), mp3_track); + playCurrent(); + } + else { + LOG(mp3_log, s_debug, F("playNext: stop")); + clearAllQueue(); + playing = play_none; + } +} +void Mp3::playPrevious() { + if (playing == play_folder && current_track > 0) { + --current_track; + LOG(mp3_log, s_debug, F("playPrevious: "), current_track); + playCurrent(); + } + else { + LOG(mp3_log, s_debug, F("playPrevious: stop")); + clearAllQueue(); + playing = play_none; + } +} void Mp3::increaseVolume() { if (volume < settings.maxVolume) { - DFMiniMp3::setVolume(++volume); + Base::setVolume(++volume); } LOG(mp3_log, s_info, str_Volume(), volume); } void Mp3::decreaseVolume() { if (volume > settings.minVolume) { - DFMiniMp3::setVolume(--volume); + Base::setVolume(--volume); } LOG(mp3_log, s_info, str_Volume(), volume); } void Mp3::setVolume() { volume = settings.initVolume; - DFMiniMp3::setVolume(volume); + Base::setVolume(volume); LOG(mp3_log, s_info, str_Volume(), volume); } + +void Mp3::loop() { +#ifdef CHECK_MISSING_ONPLAYFINISHED + if (not isPause && playing != play_none && startTrackTimer.isExpired() && not isPlaying()) { + missingOnPlayFinishedTimer.start(dfPlayer_timeUntilStarts); + } + else { + missingOnPlayFinishedTimer.stop(); + } + if (missingOnPlayFinishedTimer.isActive() && missingOnPlayFinishedTimer.isExpired()) { + LOG(mp3_log, s_info, F("missing OnPlayFinished")); + playNext(); + } + else +#endif + if (playing == play_none && (current_folder != 0 || mp3_track != 0)) { + playCurrent(); + } + Base::loop(); +} diff --git a/src/mp3.hpp b/src/mp3.hpp index 4a21ea1c..f1b8b9bb 100644 --- a/src/mp3.hpp +++ b/src/mp3.hpp @@ -6,6 +6,10 @@ #include #include "settings.hpp" +#include "queue.hpp" +#include "timer.hpp" + +#define CHECK_MISSING_ONPLAYFINISHED enum class mp3Tracks: uint16_t { t_0 = 0, @@ -126,25 +130,72 @@ class Mp3Notify { class Mp3: public DFMiniMp3 { public: + using Base = DFMiniMp3; Mp3(const Settings& settings); bool isPlaying() const; void waitForTrackToFinish(); void waitForTrackToStart(); - void playMp3FolderTrack(uint16_t track); - void playMp3FolderTrack(mp3Tracks track); void playAdvertisement(uint16_t track, bool olnyIfIsPlaying = true); void playAdvertisement(advertTracks track, bool olnyIfIsPlaying = true); + void clearFolderQueue(); + void clearMp3Queue(); + void clearAllQueue() { clearFolderQueue(); clearMp3Queue(); } + bool isPlayingFolder() { return playing == play_folder; } + bool isPlayingMp3 () { return playing == play_mp3; } + // firstTrack and lastTrack -> index in folder starting with 1 + // currentTrack -> index in queue starting with 0 + void enqueueTrack(uint8_t folder, uint8_t firstTrack, uint8_t lastTrack, uint8_t currentTrack = 0); + void enqueueTrack(uint8_t folder, uint8_t track); + void shuffleQueue(); + void enqueueMp3FolderTrack(uint16_t track, bool playAfter=false); + void enqueueMp3FolderTrack(mp3Tracks track, bool playAfter=false); + void playCurrent(); + void playNext(); + void playPrevious(); + uint8_t getCurrentTrack() { return playing ? q.get(current_track) : 0; } + +#ifdef CHECK_MISSING_ONPLAYFINISHED + void start() { isPause = false; Base::start(); } + void stop () { isPause = false; Base::stop (); } + void pause() { isPause = true ; Base::pause(); } +#endif + void increaseVolume(); void decreaseVolume(); void setVolume (); + void loop (); private: + + typedef queue track_queue; + SoftwareSerial softwareSerial; const Settings& settings; uint8_t volume{}; + + // folder queue + track_queue q{}; + uint8_t current_folder{}; + size_t current_track{}; + + // mp3 queue + uint16_t mp3_track{}; + uint16_t mp3_track_next{}; + + enum play_type: uint8_t { + play_none, + play_folder, + play_mp3, + }; + play_type playing{play_none}; +#ifdef CHECK_MISSING_ONPLAYFINISHED + Timer startTrackTimer{}; + Timer missingOnPlayFinishedTimer{}; + bool isPause{}; +#endif }; #endif /* SRC_MP3_HPP_ */ diff --git a/src/queue.hpp b/src/queue.hpp new file mode 100644 index 00000000..3ceaf3aa --- /dev/null +++ b/src/queue.hpp @@ -0,0 +1,41 @@ +#ifndef SRC_QUEUE_HPP_ +#define SRC_QUEUE_HPP_ + +#include "array.hpp" + +template +void swap(T &lhs, T &rhs) { + const T t = lhs; + lhs = rhs; + rhs = t; +} + +template +class queue { +public: + void push(T t) { + if (s < N) + c[s++] = t; + } + T get (uint8_t pos) { + if (pos >= s) + return T{}; + return c[pos]; + } + void clear() { s = 0; } + uint8_t size() { return s; } + void shuffle() { + // Queue mischen + for (uint8_t i = 0; i < s; ++i) { + const uint8_t j = random(0, s); + swap(c[i], c[j]); + } + } +private: + array c{}; + uint8_t s{}; +}; + + + +#endif /* SRC_QUEUE_HPP_ */ diff --git a/src/state_machine.cpp b/src/state_machine.cpp index dd5d126e..44ef44e1 100644 --- a/src/state_machine.cpp +++ b/src/state_machine.cpp @@ -3,6 +3,11 @@ #include "tonuino.hpp" #include "logger.hpp" +class Admin_Entry; +class Idle; +//using Admin_End = Idle; +using Admin_End = Admin_Entry; + namespace { const __FlashStringHelper* str_ChMode () { return F("ChMode") ; } @@ -18,10 +23,7 @@ const __FlashStringHelper* str_Pause () { return F("Pause") ; const __FlashStringHelper* str_Admin_Allow () { return F("AdmAllow") ; } const __FlashStringHelper* str_Admin_Entry () { return F("AdmEntry") ; } const __FlashStringHelper* str_Admin_NewCard () { return F("AdmNewCard") ; } -const __FlashStringHelper* str_Admin_MaxVolume () { return F("AdmMaxVolume") ; } -const __FlashStringHelper* str_Admin_MinVolume () { return F("AdmMinVolume") ; } -const __FlashStringHelper* str_Admin_InitVolume () { return F("AdmInitVolume") ; } -const __FlashStringHelper* str_Admin_Eq () { return F("AdmEq") ; } +const __FlashStringHelper* str_Admin_SimpleSetting () { return F("Admin_SimpleSetting") ; } const __FlashStringHelper* str_Admin_ModCard () { return F("AdmModCard") ; } const __FlashStringHelper* str_Admin_ShortCut () { return F("AdmShortCut") ; } const __FlashStringHelper* str_Admin_StandbyTimer () { return F("AdmStandbyTimer") ; } @@ -43,17 +45,16 @@ const __FlashStringHelper* str_abort () { return F(" abort") ; template class VoiceMenu : public SM { -public: - void entry() override; - void react(button_e const &) override; protected: + void entry(bool entryPlayAfter = true); + void react(button_e const &) override; void playCurrentValue(); - static int numberOfOptions ; + static uint8_t numberOfOptions ; static mp3Tracks startMessage ; static mp3Tracks messageOffset ; static bool preview ; - static int previewFromFolder; + static uint8_t previewFromFolder; static uint8_t currentValue ; static Timer previewTimer ; @@ -63,45 +64,45 @@ class VoiceMenu : public SM class ChMode : public VoiceMenu { public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; }; class ChFolder : public VoiceMenu { public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; }; class ChTrack : public VoiceMenu { public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; }; class ChFirstTrack : public VoiceMenu { public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; }; class ChLastTrack : public VoiceMenu { public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; }; class WriteCard : public SM_writeCard { public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; private: - enum subState { + enum subState: uint8_t { start_waitCardInserted, run_writeCard, end_writeCard, @@ -113,10 +114,10 @@ class WriteCard : public SM_writeCard class Admin_Allow: public VoiceMenu { public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; private: - enum subState { + enum subState: uint8_t { select_method, wait_for_no_button, get_pin, @@ -138,18 +139,18 @@ class Admin_Allow: public VoiceMenu class Admin_Entry: public VoiceMenu { public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; static uint8_t lastCurrentValue ; }; class Admin_NewCard: public SM_tonuino { public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; private: - enum subState { + enum subState: uint8_t { start_setupCard, run_setupCard, end_setupCard, @@ -160,42 +161,28 @@ class Admin_NewCard: public SM_tonuino subState current_subState; }; -class Admin_MaxVolume: public VoiceMenu -{ -public: - void entry() override; - void react(button_e const &) override; -}; - -class Admin_MinVolume: public VoiceMenu -{ -public: - void entry() override; - void react(button_e const &) override; -}; - -class Admin_InitVolume: public VoiceMenu +class Admin_SimpleSetting: public VoiceMenu { public: - void entry() override; - void react(button_e const &) override; -}; - -class Admin_Eq: public VoiceMenu -{ -public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; + enum Type: uint8_t { + maxVolume, + minVolume, + initVolume, + eq, + }; + static Type type; }; class Admin_ModCard: public VoiceMenu { public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; private: mode_t mode; - enum subState { + enum subState: uint8_t { start_writeCard, run_writeCard, end_writeCard, @@ -207,32 +194,32 @@ class Admin_ModCard: public VoiceMenu class Admin_ShortCut: public VoiceMenu { public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; private: - enum subState { + enum subState: uint8_t { start_setupCard, run_setupCard, end_setupCard, }; subState current_subState; - size_t shortcut; + uint8_t shortcut; }; class Admin_StandbyTimer: public VoiceMenu { public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; }; class Admin_CardsForFolder: public VoiceMenu { public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; private: - enum subState { + enum subState: uint8_t { start_getFolder, run_getFolder, start_getSpecial, @@ -251,38 +238,38 @@ class Admin_CardsForFolder: public VoiceMenu class Admin_InvButtons: public VoiceMenu { public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; }; class Admin_ResetEeprom: public SM_tonuino { public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; }; class Admin_LockAdmin: public VoiceMenu { public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; private: - enum subState { + enum subState: uint8_t { get_mode, get_pin, finished, }; subState current_subState; Settings::pin_t pin; - size_t pin_number; + uint8_t pin_number; }; class Admin_PauseIfCardRemoved: public VoiceMenu { public: - void entry() override; - void react(button_e const &) override; + void entry() final; + void react(button_e const &) final; }; @@ -291,41 +278,27 @@ class Admin_PauseIfCardRemoved: public VoiceMenu template bool SM::isAbort(button_e const &b) { if (b.b == buttonRaw::pauseLong) { - SM::mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); + SM::mp3.enqueueMp3FolderTrack(mp3Tracks::t_802_reset_aborted); LOG(state_log, s_info, F("SM"), str_abort()); return true; } return false; } -template -bool SM::isWaitForPlayFinish() { - if (waitForPlayFinish && timer.isExpired() && not mp3.isPlaying()) { - return true; - } - return false; -} - -template -void SM::startWaitForPlayFinish() { - timer.start(dfPlayer_timeUntilStarts); - waitForPlayFinish = true; -} - // ####################################################### template -void VoiceMenu::entry() { +void VoiceMenu::entry(bool entryPlayAfter) { LOG(state_log, s_debug, str_VoiceMenu(), F("::entry() "), static_cast(startMessage)); if (startMessage != mp3Tracks::t_0) - SM::mp3.playMp3FolderTrack(startMessage); + SM::mp3.enqueueMp3FolderTrack(startMessage, entryPlayAfter); currentValue = 0; }; template void VoiceMenu::playCurrentValue() { - SM::mp3.playMp3FolderTrack(messageOffset + currentValue); + SM::mp3.enqueueMp3FolderTrack(messageOffset + currentValue); previewTimer.start(1000); previewStarted = false; } @@ -339,9 +312,9 @@ void VoiceMenu::react(button_e const &b) { && not SM::mp3.isPlaying()) { LOG(state_log, s_debug, str_VoiceMenu(), F("::react() start preview "), currentValue); if (previewFromFolder == 0) - SM::mp3.playFolderTrack(currentValue, 1); + SM::mp3.enqueueTrack(currentValue, 1); else - SM::mp3.playFolderTrack(previewFromFolder, currentValue); + SM::mp3.enqueueTrack(previewFromFolder, currentValue); previewStarted = true; } @@ -530,9 +503,10 @@ void ChLastTrack::entry() { messageOffset = mp3Tracks::t_0; preview = true; previewFromFolder = folder.folder; - currentValue = folder.special; VoiceMenu::entry(); + + currentValue = folder.special; }; void ChLastTrack::react(button_e const &b) { @@ -567,7 +541,7 @@ void WriteCard::react(button_e const &b) { } if (b.b == buttonRaw::pauseLong) { - mp3.playMp3FolderTrack(mp3Tracks::t_802_reset_aborted); + mp3.enqueueMp3FolderTrack(mp3Tracks::t_802_reset_aborted); LOG(state_log, s_info, str_WriteCard(), str_to(), F("finished_abort")); transit(); return; @@ -575,7 +549,7 @@ void WriteCard::react(button_e const &b) { switch (current_subState) { case start_waitCardInserted: - mp3.playMp3FolderTrack(mp3Tracks::t_800_waiting_for_card); + mp3.enqueueMp3FolderTrack(mp3Tracks::t_800_waiting_for_card, true/*playAfter*/); current_subState = run_writeCard; break; case run_writeCard: @@ -583,9 +557,9 @@ void WriteCard::react(button_e const &b) { nfcTagObject newCard; newCard.nfcFolderSettings = folder; if (chip_card.writeCard(newCard)) - mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); + mp3.enqueueMp3FolderTrack(mp3Tracks::t_400_ok); else - mp3.playMp3FolderTrack(mp3Tracks::t_401_error); + mp3.enqueueMp3FolderTrack(mp3Tracks::t_401_error); timer.start(dfPlayer_timeUntilStarts); current_subState = end_writeCard; } @@ -614,18 +588,24 @@ bool Base::readCard() { if (lastCardRead.cookie != cardCookie) return false; - if (lastCardRead.nfcFolderSettings.folder != 0) { - return true; - } + if (lastCardRead.nfcFolderSettings.folder == 0) { + if (lastCardRead.nfcFolderSettings.mode == mode_t::admin_card) { + LOG(state_log, s_debug, F("Base"), str_to(), str_Admin_Entry()); + Admin_Entry::lastCurrentValue = 0; + transit(); + return false; + } - if (lastCardRead.nfcFolderSettings.mode == mode_t::admin_card) { - LOG(state_log, s_debug, F("Base"), str_to(), str_Admin_Entry()); - Admin_Entry::lastCurrentValue = 0; - transit(); - return false; + if (tonuino.specialCard(lastCardRead)) + return false; } - tonuino.specialCard(lastCardRead); + if (tonuino.getActiveModifier().handleRFID(lastCardRead)) + return false; + + if (lastCardRead.nfcFolderSettings.folder != 0) { + return true; + } return false; } @@ -635,7 +615,6 @@ bool Base::readCard() { void Idle::entry() { LOG(state_log, s_info, str_enter(), str_Idle()); tonuino.setStandbyTimer(); - mp3.stop(); }; void Idle::react(button_e const &b) { @@ -643,13 +622,12 @@ void Idle::react(button_e const &b) { LOG(state_log, s_debug, str_Idle(), F("::react(b) "), static_cast(b.b)); } - buttonCmd cmd = buttons.getButtonCmd(b.b); - uint8_t shortCut = 99; + const buttonCmd cmd = buttons.getButtonCmd(b.b); + uint8_t shortCut = 0xff; switch (cmd) { case buttonCmd::admin: LOG(state_log, s_debug, str_Idle(), str_to(), str_Admin_Allow()); - // adminMenuAllowed() transit(); return; case buttonCmd::pause: @@ -691,7 +669,7 @@ void Idle::react(button_e const &b) { transit(); } else if (shortCut == 3) { - mp3.playMp3FolderTrack(mp3Tracks::t_262_pling); + mp3.enqueueMp3FolderTrack(mp3Tracks::t_262_pling); } }; @@ -732,10 +710,10 @@ void Play::react(button_e const &b) { switch (cmd) { case buttonCmd::admin: LOG(state_log, s_debug, str_Play(), str_to(), str_Admin_Allow()); - // adminMenuAllowed() transit(); return; case buttonCmd::pause: + LOG(state_log, s_debug, F("Pause Taste")); if (tonuino.getActiveModifier().handlePause()) break; LOG(state_log, s_debug, str_Play(), str_to(), str_Pause()); @@ -769,8 +747,11 @@ void Play::react(button_e const &b) { default: break; } - if (not tonuino.knownCard) + if (not mp3.isPlayingFolder()) { + if (mp3.isPlaying()) + mp3.stop(); transit(); + } }; void Play::react(card_e const &c) { @@ -809,8 +790,8 @@ void Pause::react(button_e const &b) { LOG(state_log, s_debug, str_Pause(), F("::react(b) "), static_cast(b.b)); } - buttonCmd cmd = buttons.getButtonCmd(b.b); - uint8_t shortCut = 99; + const buttonCmd cmd = buttons.getButtonCmd(b.b); + uint8_t shortCut = 99; switch (cmd) { case buttonCmd::admin: @@ -886,12 +867,15 @@ void Pause::react(card_e const &c) { void StartPlay::entry() { LOG(state_log, s_info, str_enter(), str_StartPlay()); - tonuino.playFolder(); + mp3.enqueueMp3FolderTrack(mp3Tracks::t_262_pling); }; void StartPlay::react(button_e const &/*b*/) { LOG(state_log, s_debug, str_StartPlay(), str_to(), str_Play()); - transit(); + if (not mp3.isPlayingMp3()) { + tonuino.playFolder(); + transit(); + } }; // ####################################################### @@ -899,6 +883,7 @@ void StartPlay::react(button_e const &/*b*/) { void Admin_Allow::entry() { LOG(state_log, s_info, str_enter(), str_Admin_Allow()); current_subState = select_method; + tonuino.resetActiveModifier(); }; void Admin_Allow::react(button_e const &b) { @@ -920,7 +905,7 @@ void Admin_Allow::react(button_e const &b) { current_subState = not_allow; } else if (settings.adminMenuLocked == 2) { - mp3.playMp3FolderTrack(mp3Tracks::t_991_admin_pin); + mp3.enqueueMp3FolderTrack(mp3Tracks::t_991_admin_pin); pin_number = 0; current_subState = wait_for_no_button; } @@ -955,13 +940,13 @@ void Admin_Allow::react(button_e const &b) { // case start_match : // av = random(10, 20); // bv = random(1, av); -// mp3.playMp3FolderTrack(mp3Tracks::t_992_admin_calc); +// mp3.enqueueMp3FolderTrack(mp3Tracks::t_992_admin_calc); // timer.start(dfPlayer_timeUntilStarts); // current_subState = play_match_intro; // break; // case play_match_intro : // if (timer.isExpired() && not mp3.isPlaying()) { -// mp3.playMp3FolderTrack(av); +// mp3.enqueueMp3FolderTrack(av); // timer.start(dfPlayer_timeUntilStarts); // current_subState = play_match_a; // } @@ -970,10 +955,10 @@ void Admin_Allow::react(button_e const &b) { // if (timer.isExpired() && not mp3.isPlaying()) { // if (random(1, 3) == 2) { // cv = av + bv; -// mp3.playMp3FolderTrack(mp3Tracks::t_993_admin_calc); +// mp3.enqueueMp3FolderTrack(mp3Tracks::t_993_admin_calc); // } else { // cv = av - bv; -// mp3.playMp3FolderTrack(mp3Tracks::t_994_admin_calc); +// mp3.enqueueMp3FolderTrack(mp3Tracks::t_994_admin_calc); // } // timer.start(dfPlayer_timeUntilStarts); // LOG(admin_log, s_info, F("Result: "), cv); @@ -982,7 +967,7 @@ void Admin_Allow::react(button_e const &b) { // break; // case play_match_operation: // if (timer.isExpired() && not mp3.isPlaying()) { -// mp3.playMp3FolderTrack(bv); +// mp3.enqueueMp3FolderTrack(bv); // timer.start(dfPlayer_timeUntilStarts); // current_subState = play_match_b; // } @@ -995,7 +980,7 @@ void Admin_Allow::react(button_e const &b) { // preview = false; // previewFromFolder = 0; // -// tonuino.knownCard = false; +// mp3.clearFolderQueue(); // // VoiceMenu::entry(); // current_subState = get_match_c; @@ -1027,6 +1012,7 @@ void Admin_Allow::react(button_e const &b) { void Admin_Entry::entry() { LOG(state_log, s_info, str_enter(), str_Admin_Entry()); tonuino.disableStandbyTimer(); + tonuino.resetActiveModifier(); numberOfOptions = 13; startMessage = lastCurrentValue == 0 ? mp3Tracks::t_900_admin : mp3Tracks::t_919_continue_admin; @@ -1034,7 +1020,10 @@ void Admin_Entry::entry() { preview = false; previewFromFolder = 0; - tonuino.knownCard = false; + if (mp3.isPlayingFolder()) { + mp3.clearFolderQueue(); + mp3.stop(); + } VoiceMenu::entry(); @@ -1064,20 +1053,24 @@ void Admin_Entry::react(button_e const &b) { transit(); return; case 2: // Maximum Volume - LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_MaxVolume()); - transit(); + LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_SimpleSetting()); + Admin_SimpleSetting::type = Admin_SimpleSetting::maxVolume; + transit(); return; case 3: // Minimum Volume - LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_MinVolume()); - transit(); + LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_SimpleSetting()); + Admin_SimpleSetting::type = Admin_SimpleSetting::minVolume; + transit(); return; case 4: // Initial Volume - LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_InitVolume()); - transit(); + LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_SimpleSetting()); + Admin_SimpleSetting::type = Admin_SimpleSetting::initVolume; + transit(); return; case 5: // EQ - LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_Eq()); - transit(); + LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_SimpleSetting()); + Admin_SimpleSetting::type = Admin_SimpleSetting::eq; + transit(); return; case 6: // create modifier card LOG(state_log, s_debug, str_Admin_Entry(), str_to(), str_Admin_ModCard()); @@ -1115,9 +1108,6 @@ void Admin_Entry::react(button_e const &b) { } }; -//using Admin_End = Idle; -using Admin_End = Admin_Entry; - // ####################################################### void Admin_NewCard::entry() { @@ -1173,63 +1163,35 @@ void Admin_NewCard::react(button_e const &b) { // ####################################################### -void Admin_MaxVolume::entry() { - LOG(state_log, s_info, str_enter(), str_Admin_MaxVolume()); - - numberOfOptions = 30 - settings.minVolume; - startMessage = mp3Tracks::t_930_max_volume_intro; - messageOffset = static_cast(settings.minVolume); +void Admin_SimpleSetting::entry() { + LOG(state_log, s_info, str_enter(), str_Admin_SimpleSetting(), type); + + numberOfOptions = type == maxVolume ? 30 - settings.minVolume : + type == minVolume ? settings.maxVolume - 1 : + type == initVolume ? settings.maxVolume - settings.minVolume + 1 : + type == eq ? 6 : 0; + startMessage = type == maxVolume ? mp3Tracks::t_930_max_volume_intro : + type == minVolume ? mp3Tracks::t_931_min_volume_into : + type == initVolume ? mp3Tracks::t_932_init_volume_into : + type == eq ? mp3Tracks::t_920_eq_intro : mp3Tracks::t_0; + messageOffset = type == maxVolume ? static_cast(settings.minVolume) : + type == minVolume ? mp3Tracks::t_0 : + type == initVolume ? static_cast(settings.minVolume - 1) : + type == eq ? mp3Tracks::t_920_eq_intro : mp3Tracks::t_0; preview = false; previewFromFolder = 0; - VoiceMenu::entry(); - - currentValue = settings.maxVolume - settings.minVolume; -}; - -void Admin_MaxVolume::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Admin_MaxVolume(), F("::react() "), static_cast(b.b)); - } - VoiceMenu::react(b); - - if (isAbort(b)) { - transit(); - return; - } - if (isWaitForPlayFinish()) { - LOG(state_log, s_debug, str_Admin_MaxVolume(), str_to(), str_Idle()); - transit(); - return; - } + VoiceMenu::entry(false); - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { - settings.maxVolume = currentValue + settings.minVolume; - settings.writeSettingsToFlash(); - mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); - startWaitForPlayFinish(); - } + currentValue = type == maxVolume ? settings.maxVolume - settings.minVolume : + type == minVolume ? settings.minVolume : + type == initVolume ? settings.initVolume - settings.minVolume + 1 : + type == eq ? settings.eq : 0; }; -// ####################################################### - -void Admin_MinVolume::entry() { - LOG(state_log, s_info, str_enter(), str_Admin_MinVolume()); - - numberOfOptions = settings.maxVolume - 1; - startMessage = mp3Tracks::t_931_min_volume_into; - messageOffset = mp3Tracks::t_0; - preview = false; - previewFromFolder = settings.maxVolume - settings.minVolume; - - VoiceMenu::entry(); - - currentValue = settings.minVolume; -}; - -void Admin_MinVolume::react(button_e const &b) { +void Admin_SimpleSetting::react(button_e const &b) { if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Admin_MinVolume(), F("::react() "), static_cast(b.b)); + LOG(state_log, s_debug, str_Admin_SimpleSetting(), F("::react() "), static_cast(b.b)); } VoiceMenu::react(b); @@ -1237,99 +1199,22 @@ void Admin_MinVolume::react(button_e const &b) { transit(); return; } - if (isWaitForPlayFinish()) { - LOG(state_log, s_debug, str_Admin_MinVolume(), str_to(), str_Idle()); - transit(); - return; - } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { - settings.minVolume = currentValue; - settings.writeSettingsToFlash(); - mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); - startWaitForPlayFinish(); - } -}; + switch (type) { + case maxVolume : settings.maxVolume = currentValue + settings.minVolume ; break; + case minVolume : settings.minVolume = currentValue ; break; + case initVolume: settings.initVolume = currentValue + settings.minVolume - 1; break; + case eq : settings.eq = currentValue; + mp3.setEq(static_cast(settings.eq - 1)) ; break; -// ####################################################### - -void Admin_InitVolume::entry() { - LOG(state_log, s_info, str_enter(), str_Admin_InitVolume()); - - numberOfOptions = settings.maxVolume - settings.minVolume + 1; - startMessage = mp3Tracks::t_932_init_volume_into; - messageOffset = static_cast(settings.minVolume - 1); - preview = false; - previewFromFolder = 0; - - VoiceMenu::entry(); - - currentValue = settings.initVolume - settings.minVolume + 1; -}; - -void Admin_InitVolume::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Admin_InitVolume(), F("::react() "), static_cast(b.b)); - } - VoiceMenu::react(b); - - if (isAbort(b)) { - transit(); - return; - } - if (isWaitForPlayFinish()) { - LOG(state_log, s_debug, str_Admin_InitVolume(), str_to(), str_Idle()); - transit(); - return; - } - - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { - settings.initVolume = currentValue + settings.minVolume - 1; + } settings.writeSettingsToFlash(); - mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); - startWaitForPlayFinish(); - } -}; - -// ####################################################### - -void Admin_Eq::entry() { - LOG(state_log, s_info, str_enter(), str_Admin_Eq()); - - numberOfOptions = 6; - startMessage = mp3Tracks::t_920_eq_intro; - messageOffset = mp3Tracks::t_920_eq_intro; - preview = false; - previewFromFolder = 0; - - VoiceMenu::entry(); - - currentValue = settings.eq; -}; - -void Admin_Eq::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Admin_Eq(), F("::react() "), static_cast(b.b)); - } - VoiceMenu::react(b); - - if (isAbort(b)) { - transit(); - return; - } - if (isWaitForPlayFinish()) { - LOG(state_log, s_debug, str_Admin_Eq(), str_to(), str_Idle()); + mp3.enqueueMp3FolderTrack(mp3Tracks::t_402_ok_settings); + LOG(state_log, s_debug, str_Admin_SimpleSetting(), str_to(), str_Idle()); transit(); return; } - - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { - settings.eq = currentValue; - mp3.setEq(static_cast(settings.eq - 1)); - settings.writeSettingsToFlash(); - mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); - startWaitForPlayFinish(); - } }; // ####################################################### @@ -1343,7 +1228,7 @@ void Admin_ModCard::entry() { preview = false; previewFromFolder = 0; - VoiceMenu::entry(); + VoiceMenu::entry(false); mode = mode_t::none; current_subState = start_writeCard; @@ -1411,16 +1296,18 @@ void Admin_ModCard::react(button_e const &b) { if (mode == mode_t::none) { mode = static_cast(currentValue); if (mode != mode_t::sleep_timer) { + mp3.clearMp3Queue(); readyToWrite = true; } else { numberOfOptions = 4; startMessage = mp3Tracks::t_960_timer_intro; messageOffset = mp3Tracks::t_960_timer_intro; - VoiceMenu::entry(); + VoiceMenu::entry(false); } } else { + mp3.clearMp3Queue(); readyToWrite = true; } } @@ -1437,7 +1324,7 @@ void Admin_ShortCut::entry() { preview = false; previewFromFolder = 0; - VoiceMenu::entry(); + VoiceMenu::entry(false); shortcut = 0; }; @@ -1452,13 +1339,8 @@ void Admin_ShortCut::react(button_e const &b) { transit(); return; } - if (isWaitForPlayFinish()) { - LOG(state_log, s_debug, str_Admin_ShortCut(), str_to(), str_Idle()); - transit(); - return; - } - if (shortcut > 0) { + if (shortcut == 0) { if ((b.b == buttonRaw::pause) && (currentValue != 0)) { shortcut = currentValue; current_subState = start_setupCard; @@ -1482,9 +1364,10 @@ void Admin_ShortCut::react(button_e const &b) { break; case end_setupCard: settings.shortCuts[shortcut] = SM_setupCard::folder; - mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); - startWaitForPlayFinish(); - break; + mp3.enqueueMp3FolderTrack(mp3Tracks::t_402_ok_settings); + LOG(state_log, s_debug, str_Admin_ShortCut(), str_to(), str_Idle()); + transit(); + return; default: break; } @@ -1503,7 +1386,7 @@ void Admin_StandbyTimer::entry() { preview = false; previewFromFolder = 0; - VoiceMenu::entry(); + VoiceMenu::entry(false); }; void Admin_StandbyTimer::react(button_e const &b) { @@ -1516,11 +1399,6 @@ void Admin_StandbyTimer::react(button_e const &b) { transit(); return; } - if (isWaitForPlayFinish()) { - LOG(state_log, s_debug, str_Admin_StandbyTimer(), str_to(), str_Idle()); - transit(); - return; - } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { switch (currentValue) { @@ -1531,8 +1409,10 @@ void Admin_StandbyTimer::react(button_e const &b) { case 5: settings.standbyTimer = 0; break; } settings.writeSettingsToFlash(); - mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); - startWaitForPlayFinish(); + mp3.enqueueMp3FolderTrack(mp3Tracks::t_402_ok_settings); + LOG(state_log, s_debug, str_Admin_StandbyTimer(), str_to(), str_Idle()); + transit(); + return; } }; @@ -1607,21 +1487,19 @@ void Admin_CardsForFolder::react(button_e const &b) { VoiceMenu::react(b); if ((b.b == buttonRaw::pause) && (currentValue != 0)) { special2 = currentValue; - mp3.playMp3FolderTrack(mp3Tracks::t_936_batch_cards_intro); - timer.start(dfPlayer_timeUntilStarts); + mp3.enqueueMp3FolderTrack(mp3Tracks::t_936_batch_cards_intro); current_subState = prepare_writeCard; } break; case prepare_writeCard: - if (timer.isExpired() && not mp3.isPlaying() && chip_card.isCardRemoved()) { + if (chip_card.isCardRemoved()) { if (special > special2) { - //mp3.playMp3FolderTrack(mp3Tracks::t_400_ok); LOG(state_log, s_debug, str_Admin_CardsForFolder(), str_to(), str_Idle()); transit(); return; } folder.special = special; - mp3.playMp3FolderTrack(special); + mp3.enqueueMp3FolderTrack(special, true/*playAfter*/); timer.start(dfPlayer_timeUntilStarts); LOG(card_log, s_info, special, F("-te Karte auflegen")); current_subState = start_writeCard; @@ -1631,22 +1509,19 @@ void Admin_CardsForFolder::react(button_e const &b) { if (timer.isExpired() && not mp3.isPlaying() && chip_card.isCardRemoved()) { SM_writeCard::folder = folder; SM_writeCard::start(); - timer.start(dfPlayer_timeUntilStarts); current_subState = run_writeCard; } break; case run_writeCard: - if (timer.isExpired() && not mp3.isPlaying()) { - SM_writeCard::dispatch(b); - if (SM_writeCard::is_in_state()) { - ++special; - current_subState = prepare_writeCard; - } - if (SM_writeCard::is_in_state()) { - LOG(state_log, s_info, str_Admin_CardsForFolder(), str_abort()); - transit(); - return; - } + SM_writeCard::dispatch(b); + if (SM_writeCard::is_in_state()) { + ++special; + current_subState = prepare_writeCard; + } + else if (SM_writeCard::is_in_state()) { + LOG(state_log, s_info, str_Admin_CardsForFolder(), str_abort()); + transit(); + return; } break; default: @@ -1665,7 +1540,7 @@ void Admin_InvButtons::entry() { preview = false; previewFromFolder = 0; - VoiceMenu::entry(); + VoiceMenu::entry(false); }; void Admin_InvButtons::react(button_e const &b) { @@ -1678,11 +1553,6 @@ void Admin_InvButtons::react(button_e const &b) { transit(); return; } - if (isWaitForPlayFinish()) { - LOG(state_log, s_debug, str_Admin_InvButtons(), str_to(), str_Idle()); - transit(); - return; - } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { switch (currentValue) { @@ -1690,8 +1560,10 @@ void Admin_InvButtons::react(button_e const &b) { case 2: settings.invertVolumeButtons = true ; break; } settings.writeSettingsToFlash(); - mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); - startWaitForPlayFinish(); + mp3.enqueueMp3FolderTrack(mp3Tracks::t_402_ok_settings); + LOG(state_log, s_debug, str_Admin_InvButtons(), str_to(), str_Idle()); + transit(); + return; } }; @@ -1701,16 +1573,13 @@ void Admin_ResetEeprom::entry() { LOG(state_log, s_info, str_enter(), str_Admin_ResetEeprom()); settings.clearEEPROM(); settings.resetSettings(); - mp3.playMp3FolderTrack(mp3Tracks::t_999_reset_ok); - startWaitForPlayFinish(); + mp3.enqueueMp3FolderTrack(mp3Tracks::t_999_reset_ok); }; void Admin_ResetEeprom::react(button_e const &/*b*/) { - if (isWaitForPlayFinish()) { - LOG(state_log, s_debug, str_Admin_ResetEeprom(), str_to(), str_Idle()); - transit(); - return; - } + LOG(state_log, s_debug, str_Admin_ResetEeprom(), str_to(), str_Idle()); + transit(); + return; }; // ####################################################### @@ -1724,7 +1593,7 @@ void Admin_LockAdmin::entry() { preview = false; previewFromFolder = 0; - VoiceMenu::entry(); + VoiceMenu::entry(false); current_subState = get_mode; }; @@ -1738,11 +1607,6 @@ void Admin_LockAdmin::react(button_e const &b) { transit(); return; } - if (isWaitForPlayFinish()) { - LOG(state_log, s_debug, str_Admin_LockAdmin(), str_to(), str_Idle()); - transit(); - return; - } switch(current_subState) { case get_mode: @@ -1751,7 +1615,7 @@ void Admin_LockAdmin::react(button_e const &b) { settings.adminMenuLocked = currentValue-1; if (settings.adminMenuLocked == 2) { current_subState = get_pin; - mp3.playMp3FolderTrack(mp3Tracks::t_991_admin_pin); + mp3.enqueueMp3FolderTrack(mp3Tracks::t_991_admin_pin); } else current_subState = finished; @@ -1772,9 +1636,10 @@ void Admin_LockAdmin::react(button_e const &b) { } case finished: settings.writeSettingsToFlash(); - mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); - startWaitForPlayFinish(); - break; + mp3.enqueueMp3FolderTrack(mp3Tracks::t_402_ok_settings); + LOG(state_log, s_debug, str_Admin_LockAdmin(), str_to(), str_Idle()); + transit(); + return; } }; @@ -1789,7 +1654,7 @@ void Admin_PauseIfCardRemoved::entry() { preview = false; previewFromFolder = 0; - VoiceMenu::entry(); + VoiceMenu::entry(false); }; void Admin_PauseIfCardRemoved::react(button_e const &b) { @@ -1802,11 +1667,6 @@ void Admin_PauseIfCardRemoved::react(button_e const &b) { transit(); return; } - if (isWaitForPlayFinish()) { - LOG(state_log, s_debug, str_Admin_PauseIfCardRemoved(), str_to(), str_Idle()); - transit(); - return; - } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { switch (currentValue) { @@ -1814,8 +1674,10 @@ void Admin_PauseIfCardRemoved::react(button_e const &b) { case 2: settings.pauseWhenCardRemoved = true ; break; } settings.writeSettingsToFlash(); - mp3.playMp3FolderTrack(mp3Tracks::t_402_ok_settings); - startWaitForPlayFinish(); + mp3.enqueueMp3FolderTrack(mp3Tracks::t_402_ok_settings); + LOG(state_log, s_debug, str_Admin_PauseIfCardRemoved(), str_to(), str_Idle()); + transit(); + return; } }; @@ -1840,10 +1702,10 @@ Chip_card &SM::chip_card = Tonuino::getTonuino().getChipCard(); template Timer SM::timer{}; template -bool SM::waitForPlayFinish = false; +bool SM::waitForPlayFinish{}; template -int VoiceMenu::numberOfOptions ; +uint8_t VoiceMenu::numberOfOptions ; template mp3Tracks VoiceMenu::startMessage ; template @@ -1851,7 +1713,7 @@ mp3Tracks VoiceMenu::messageOffset ; template bool VoiceMenu::preview ; template -int VoiceMenu::previewFromFolder; +uint8_t VoiceMenu::previewFromFolder; template uint8_t VoiceMenu::currentValue ; @@ -1861,4 +1723,5 @@ template bool VoiceMenu::previewStarted ; nfcTagObject Base::lastCardRead{}; -uint8_t Admin_Entry::lastCurrentValue = 0; +uint8_t Admin_Entry::lastCurrentValue{}; +Admin_SimpleSetting::Type Admin_SimpleSetting::type{}; diff --git a/src/state_machine.hpp b/src/state_machine.hpp index e05031d6..81c82841 100644 --- a/src/state_machine.hpp +++ b/src/state_machine.hpp @@ -24,7 +24,7 @@ struct card_e : tinyfsm::Event { // ---------------------------------------------------------------------------- // State Machine Base Class Declaration // -enum class SM_type { +enum class SM_type: uint8_t { tonuino, setupCard, writeCard, @@ -40,8 +40,6 @@ class SM: public tinyfsm::Fsm> void exit(void) { waitForPlayFinish = false; }; bool isAbort(button_e const &b); - bool isWaitForPlayFinish(); - void startWaitForPlayFinish(); static folderSettings folder; protected: diff --git a/src/tonuino.cpp b/src/tonuino.cpp index ced4df09..1cc00fb0 100644 --- a/src/tonuino.cpp +++ b/src/tonuino.cpp @@ -12,20 +12,6 @@ namespace { -template -void swap(T &lhs, T &rhs) { - const T t = lhs; - lhs = rhs; - rhs = t; -} - -const __FlashStringHelper* str_Track () { return F("Track: ") ; } -const __FlashStringHelper* str_Hoerspiel() { return F("Hörspiel"); } -const __FlashStringHelper* str_Album () { return F("Album"); } -const __FlashStringHelper* str_Party () { return F("Party"); } -const __FlashStringHelper* str_Einzel () { return F("Einzel"); } -const __FlashStringHelper* str_Hoerbuch () { return F("Hörbuch"); } -const __FlashStringHelper* str_Von_Bis () { return F("Von-Bis "); } const __FlashStringHelper* str_bis () { return F(" bis "); } } // anonymous namespace @@ -53,10 +39,8 @@ void Tonuino::setup() { settings.loadSettingsFromFlash(); } - // Start Shortcut "at Startup" - e.g. Welcome Sound - //playShortCut(3); - SM_tonuino::start(); + // Start Shortcut "at Startup" - e.g. Welcome Sound SM_tonuino::dispatch(button_e(buttonRaw::start)); } @@ -81,242 +65,101 @@ void Tonuino::loop() { void Tonuino::playFolder() { LOG(play_log, s_debug, F("= playFolder()")); - knownCard = true; numTracksInFolder = mp3.getFolderTrackCount(myFolder->folder); - firstTrack = 1; LOG(play_log, s_info, numTracksInFolder, F(" files in folder "), myFolder->folder); + numTracksInFolder = min(numTracksInFolder, 0xff); + mp3.clearAllQueue(); switch (myFolder->mode) { case mode_t::hoerspiel: // Hörspielmodus: eine zufällige Datei aus dem Ordner - LOG(play_log, s_info, str_Hoerspiel()); - currentTrack = random(1, numTracksInFolder + 1); - LOG(play_log, s_info, str_Track(), currentTrack); + LOG(play_log, s_info, F("Hörspiel")); + myFolder->special = 1; + myFolder->special2 = numTracksInFolder; + __attribute__ ((fallthrough)); + case mode_t::hoerspiel_vb: + // Spezialmodus Von-Bin: Hörspiel: eine zufällige Datei aus dem Ordner + LOG(play_log, s_info, myFolder->special, F(" bis "), myFolder->special2); + mp3.enqueueTrack(myFolder->folder, random(myFolder->special, myFolder->special2 + 1)); break; case mode_t::album: // Album Modus: kompletten Ordner spielen - LOG(play_log, s_info, str_Album()); - currentTrack = 1; + LOG(play_log, s_info, F("Album")); + myFolder->special = 1; + myFolder->special2 = numTracksInFolder; + __attribute__ ((fallthrough)); + case mode_t::album_vb: + // Spezialmodus Von-Bis: Album: alle Dateien zwischen Start und Ende spielen + LOG(play_log, s_info, myFolder->special, str_bis() , myFolder->special2); + mp3.enqueueTrack(myFolder->folder, myFolder->special, myFolder->special2); break; case mode_t::party: // Party Modus: Ordner in zufälliger Reihenfolge - LOG(play_log, s_info, str_Party()); - shuffleQueue(); - currentTrack = 1; + LOG(play_log, s_info, F("Party")); + myFolder->special = 1; + myFolder->special2 = numTracksInFolder; + __attribute__ ((fallthrough)); + case mode_t::party_vb: + // Spezialmodus Von-Bis: Party Ordner in zufälliger Reihenfolge + LOG(play_log, s_info, myFolder->special, str_bis(), myFolder->special2); + mp3.enqueueTrack(myFolder->folder, myFolder->special, myFolder->special2); + mp3.shuffleQueue(); break; case mode_t::einzel: // Einzel Modus: eine Datei aus dem Ordner abspielen - LOG(play_log, s_info, str_Einzel()); - currentTrack = myFolder->special; - LOG(play_log, s_info, str_Track(), currentTrack); + LOG(play_log, s_info, F("Einzel")); + mp3.enqueueTrack(myFolder->folder, myFolder->special); break; case mode_t::hoerbuch: case mode_t::hoerbuch_1: + { // Hörbuch Modus: kompletten Ordner spielen und Fortschritt merken (oder nur eine Datei) - LOG(play_log, s_info, str_Hoerbuch()); - currentTrack = settings.readFolderSettingFromFlash(myFolder->folder); - if (currentTrack == 0 || currentTrack > numTracksInFolder) { - currentTrack = 1; - } - LOG(play_log, s_info, str_Track(), currentTrack); - break; - - case mode_t::hoerspiel_vb: - // Spezialmodus Von-Bin: Hörspiel: eine zufällige Datei aus dem Ordner - LOG(play_log, s_info, str_Von_Bis(), str_Hoerspiel()); - LOG(play_log, s_info, myFolder->special, F(" bis "), myFolder->special2); - firstTrack = myFolder->special; - numTracksInFolder = myFolder->special2; - currentTrack = random(myFolder->special, numTracksInFolder + 1); - LOG(play_log, s_info, str_Track(), currentTrack); - break; - - case mode_t::album_vb: - // Spezialmodus Von-Bis: Album: alle Dateien zwischen Start und Ende spielen - LOG(play_log, s_info, str_Von_Bis(), str_Album()); - LOG(play_log, s_info, myFolder->special, str_bis() , myFolder->special2); - firstTrack = myFolder->special; - numTracksInFolder = myFolder->special2; - currentTrack = myFolder->special; + LOG(play_log, s_info, F("Hörbuch")); + uint16_t startTrack = settings.readFolderSettingFromFlash(myFolder->folder); + if (startTrack > numTracksInFolder) + startTrack = 1; + mp3.enqueueTrack(myFolder->folder, 1, numTracksInFolder, startTrack-1); + } break; - case mode_t::party_vb: - // Spezialmodus Von-Bis: Party Ordner in zufälliger Reihenfolge - LOG(play_log, s_info, str_Von_Bis(), str_Party()); - LOG(play_log, s_info, myFolder->special, str_bis(), myFolder->special2); - firstTrack = myFolder->special; - numTracksInFolder = myFolder->special2; - shuffleQueue(); - currentTrack = 1; - break; default: - knownCard = false; - return; + break; } - playCurrentTrack(); } void Tonuino::playTrackNumber () { - uint8_t advertTrack = getCurrentTrack(); - // Spezialmodus Von-Bis für Album und Party gibt die Dateinummer relativ zur Startposition wieder - if (myFolder->mode == mode_t::album_vb || myFolder->mode == mode_t::party_vb) { - advertTrack = advertTrack - myFolder->special + 1; - } - mp3.playAdvertisement(advertTrack); + const uint8_t advertTrack = mp3.getCurrentTrack(); + if (advertTrack != 0) + mp3.playAdvertisement(advertTrack); } // Leider kann das Modul selbst keine Queue abspielen, daher müssen wir selbst die Queue verwalten -void Tonuino::nextTrack() { +void Tonuino::nextTrack(bool fromOnPlayFinished) { + LOG(play_log, s_info, F("= nextTrack()")); if (activeModifier->handleNext()) return; - - if (not knownCard) - return; - - LOG(play_log, s_info, F("= nextTrack()")); - - switch (myFolder->mode) { - case mode_t::hoerspiel : - case mode_t::hoerspiel_vb: - if (not mp3.isPlaying()) { - LOG(play_log, s_info, str_Hoerspiel(), F(" -> stop")); - knownCard = false; - //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! - } - break; - - case mode_t::album : - case mode_t::album_vb: - if (currentTrack != numTracksInFolder) { - ++currentTrack; - mp3.playFolderTrack(myFolder->folder, currentTrack); - LOG(play_log, s_info, str_Album(), F(" -> next")); - LOG(play_log, s_info, str_Track(), currentTrack); - } else { - LOG(play_log, s_info, str_Album(), F(" -> stop")); - //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! - knownCard = false; - } - break; - - case mode_t::party : - case mode_t::party_vb: - if (currentTrack != numTracksInFolder - firstTrack + 1) { - ++currentTrack; - LOG(play_log, s_info, str_Party(), F(" -> next in queue")); - } else { - currentTrack = 1; - LOG(play_log, s_info, str_Party(), F(" -> end, start again")); - //// Wenn am Ende der Queue neu gemischt werden soll bitte die Zeilen wieder aktivieren - //LOG(play_log, s_info, F("Ende der Queue -> mische neu")); - //shuffleQueue(); - } - { - const uint16_t track = queue[currentTrack - 1]; - LOG(play_log, s_info, str_Track(), track); - mp3.playFolderTrack(myFolder->folder, track); - } - break; - - case mode_t::einzel: - LOG(play_log, s_info, str_Einzel(), F(" -> stop")); - //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! - knownCard = false; - break; - - case mode_t::hoerbuch_1: - knownCard = false; - LOG(play_log, s_info, str_Hoerbuch(), F(" single -> stop and save")); - __attribute__((fallthrough)); - // no break by intention - case mode_t::hoerbuch: - if (currentTrack < numTracksInFolder) { - ++currentTrack; - if (knownCard) { - LOG(play_log, s_info, str_Hoerbuch(), F(" -> next and save")); - mp3.playFolderTrack(myFolder->folder, currentTrack); - } - } - else { - currentTrack = 1; - if (knownCard) { - LOG(play_log, s_info, str_Hoerbuch(), F(" -> stop and save")); - knownCard = false; - } - } - LOG(play_log, s_info, str_Track(), currentTrack); - // Fortschritt im EEPROM abspeichern - settings.writeFolderSettingToFlash(myFolder->folder, currentTrack); - //mp3.sleep(); // Je nach Modul kommt es nicht mehr zurück aus dem Sleep! - break; - default: - break; + if (mp3.isPlayingFolder() && (myFolder->mode == mode_t::hoerbuch || myFolder->mode == mode_t::hoerbuch_1)) { + const uint8_t trackToSave = (mp3.getCurrentTrack() < numTracksInFolder) ? mp3.getCurrentTrack()+1 : 1; + settings.writeFolderSettingToFlash(myFolder->folder, trackToSave); + if (fromOnPlayFinished && myFolder->mode == mode_t::hoerbuch_1) + mp3.clearFolderQueue(); } + mp3.playNext(); } void Tonuino::previousTrack() { LOG(play_log, s_info, F("= previousTrack()")); - - switch (myFolder->mode) { - case mode_t::hoerspiel: - case mode_t::hoerspiel_vb: - LOG(play_log, s_info, str_Hoerspiel(), F(" -> restart")); - LOG(play_log, s_info, str_Track(), currentTrack); - mp3.playFolderTrack(myFolder->folder, currentTrack); - break; - - case mode_t::album: - case mode_t::album_vb: - LOG(play_log, s_info, str_Album(), F(" -> previous")); - if (currentTrack != firstTrack) { - --currentTrack; - } - LOG(play_log, s_info, str_Track(), currentTrack); - mp3.playFolderTrack(myFolder->folder, currentTrack); - break; - - case mode_t::party: - case mode_t::party_vb: - if (currentTrack != 1) { - LOG(play_log, s_info, str_Party(), F(" -> previous in queue ")); - --currentTrack; - } else { - LOG(play_log, s_info, str_Party(), F(", beginning of queue -> go to end")); - currentTrack = numTracksInFolder - firstTrack + 1; - } - { - const uint16_t track = queue[currentTrack - 1]; - LOG(play_log, s_info, str_Track(), track); - mp3.playFolderTrack(myFolder->folder, track); - } - break; - - case mode_t::einzel: - LOG(play_log, s_info, str_Einzel(), F(" -> restart")); - LOG(play_log, s_info, str_Track(), currentTrack); - mp3.playFolderTrack(myFolder->folder, currentTrack); - break; - - case mode_t::hoerbuch: - case mode_t::hoerbuch_1: - LOG(play_log, s_info, str_Hoerbuch(), F(" -> previous and save")); - if (currentTrack != 1) { - --currentTrack; - } - LOG(play_log, s_info, str_Track(), currentTrack); - mp3.playFolderTrack(myFolder->folder, currentTrack); - // Fortschritt im EEPROM abspeichern - settings.writeFolderSettingToFlash(myFolder->folder, currentTrack); - break; - default: - break; + if (mp3.isPlayingFolder() && (myFolder->mode == mode_t::hoerbuch || myFolder->mode == mode_t::hoerbuch_1)) { + const uint8_t trackToSave = (mp3.getCurrentTrack() > numTracksInFolder) ? mp3.getCurrentTrack()-1 : 1; + settings.writeFolderSettingToFlash(myFolder->folder, trackToSave); } + mp3.playPrevious(); } // Funktionen für den Standby Timer (z.B. über Pololu-Switch oder Mosfet) @@ -354,42 +197,35 @@ void Tonuino::checkStandbyAtMillis() { } } -uint8_t Tonuino::getCurrentTrack() const { - if (myFolder->mode == mode_t::party || myFolder->mode == mode_t::party_vb) - return (queue[currentTrack - 1]); - else - return currentTrack; -} - bool Tonuino::specialCard(const nfcTagObject &nfcTag) { LOG(card_log, s_debug, F("special card, mode = "), static_cast(nfcTag.nfcFolderSettings.mode)); if (activeModifier->getActive() == nfcTag.nfcFolderSettings.mode) { resetActiveModifier(); LOG(card_log, s_info, F("modifier removed")); - mp3.playAdvertisement(advertTracks::t_261_deactivate_mod_card, false); + mp3.playAdvertisement(advertTracks::t_261_deactivate_mod_card, false/*olnyIfIsPlaying*/); return true; } const Modifier *oldModifier = activeModifier; switch (nfcTag.nfcFolderSettings.mode) { case mode_t::sleep_timer: LOG(card_log, s_info, F("act. sleepTimer")); - mp3.playAdvertisement(advertTracks::t_302_sleep, false); + mp3.playAdvertisement(advertTracks::t_302_sleep, false)/*olnyIfIsPlaying*/; activeModifier = &sleepTimer; sleepTimer.start(nfcTag.nfcFolderSettings.special) ;break; case mode_t::freeze_dance: LOG(card_log, s_info, F("act. freezeDance")); - mp3.playAdvertisement(advertTracks::t_300_freeze_into, false); + mp3.playAdvertisement(advertTracks::t_300_freeze_into, false/*olnyIfIsPlaying*/); activeModifier = &freezeDance; ;break; case mode_t::locked: LOG(card_log, s_info, F("act. locked")); - mp3.playAdvertisement(advertTracks::t_303_locked, false); + mp3.playAdvertisement(advertTracks::t_303_locked, false/*olnyIfIsPlaying*/); activeModifier = &locked ;break; case mode_t::toddler: LOG(card_log, s_info, F("act. toddlerMode")); - mp3.playAdvertisement(advertTracks::t_304_buttonslocked, false); + mp3.playAdvertisement(advertTracks::t_304_buttonslocked, false/*olnyIfIsPlaying*/); activeModifier = &toddlerMode ;break; case mode_t::kindergarden: LOG(card_log, s_info, F("act. kindergardenMode")); - mp3.playAdvertisement(advertTracks::t_305_kindergarden, false); + mp3.playAdvertisement(advertTracks::t_305_kindergarden, false/*olnyIfIsPlaying*/); activeModifier = &kindergardenMode ;break; case mode_t::repeat_single:LOG(card_log, s_info, F("act. repeatSingleModifier")); - mp3.playAdvertisement(advertTracks::t_260_activate_mod_card, false); + mp3.playAdvertisement(advertTracks::t_260_activate_mod_card, false/*olnyIfIsPlaying*/); activeModifier = &repeatSingleModifier ;break; default: return false; } @@ -398,24 +234,3 @@ bool Tonuino::specialCard(const nfcTagObject &nfcTag) { return true; } -void Tonuino::shuffleQueue() { - // Queue für die Zufallswiedergabe erstellen - for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1; ++x) - queue[x] = x + firstTrack; - - // Rest mit 0 auffüllen - for (uint8_t x = numTracksInFolder - firstTrack + 1; x < maxTracksInFolder; ++x) - queue[x] = 0; - - // Queue mischen - for (uint8_t i = 0; i < numTracksInFolder - firstTrack + 1; ++i) { - const uint8_t j = random(0, numTracksInFolder - firstTrack + 1); - swap(queue[i], queue[j]); - } - LOG(play_log, s_debug, F("Queue :")); - for (uint8_t x = 0; x < numTracksInFolder - firstTrack + 1 ; ++x) - LOG(play_log, s_debug, queue[x]); -} - - - diff --git a/src/tonuino.hpp b/src/tonuino.hpp index aae82e59..bbb393fe 100644 --- a/src/tonuino.hpp +++ b/src/tonuino.hpp @@ -16,11 +16,10 @@ class Tonuino { void loop (); void playFolder (); - void playCurrentTrack() { if (knownCard) mp3.playFolderTrack(myFolder->folder, getCurrentTrack()); } void playTrackNumber (); - void nextTrack(); - void previousTrack(); + void nextTrack(bool fromOnPlayFinished = false); + void previousTrack(); void resetActiveModifier () { activeModifier = &noneModifier; } Modifier& getActiveModifier() { return *activeModifier; } @@ -28,30 +27,21 @@ class Tonuino { void setStandbyTimer(); void disableStandbyTimer (); - void setCard (const nfcTagObject &newCard ) { myCard = newCard; setFolder(&myCard.nfcFolderSettings); } - const nfcTagObject& getCard() const { return myCard; } - void setFolder(const folderSettings *newFolder) { myFolder = newFolder; } + void setCard (const nfcTagObject &newCard) { myCard = newCard; setFolder(&myCard.nfcFolderSettings); } + const nfcTagObject& getCard() const { return myCard; } + void setFolder(folderSettings *newFolder ) { myFolder = newFolder; } Mp3& getMp3 () { return mp3 ; } Buttons& getButtons () { return buttons ; } Settings& getSettings () { return settings ; } Chip_card& getChipCard() { return chip_card; } - bool knownCard = false; - private: - uint8_t getCurrentTrack() const; - void checkStandbyAtMillis(); bool specialCard(const nfcTagObject &nfcTag); - void shuffleQueue (); - - static const size_t maxTracksInFolder = 255; - typedef array queue_t; - Settings settings {}; Mp3 mp3 {settings}; Buttons buttons {settings}; @@ -68,18 +58,13 @@ class Tonuino { RepeatSingleModifier repeatSingleModifier{*this, mp3, settings}; //FeedbackModifier feedbackModifier {*this, mp3, settings}; - Modifier *activeModifier = &noneModifier; - - uint16_t numTracksInFolder = 0; - uint16_t currentTrack = 0; - uint16_t firstTrack = 0; - queue_t queue{}; - - Timer standbyTimer{}; + Modifier* activeModifier = &noneModifier; - nfcTagObject myCard; - const folderSettings *myFolder = &myCard.nfcFolderSettings; + Timer standbyTimer{}; + nfcTagObject myCard{}; + folderSettings* myFolder = &myCard.nfcFolderSettings; + uint16_t numTracksInFolder{}; }; #endif /* SRC_TONUINO_HPP_ */ From c526f77f689a473024ddd0b793a79ce66bc77369 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Wed, 23 Feb 2022 19:17:15 +0100 Subject: [PATCH 20/32] change .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index a8eeaf96..ec06c13f 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,8 @@ Temporary Items /tools/*.pyc /Release/ /sc-card_priv/ + +# Eclipse Sloeber plugin for Arduino /sloeber.ino.cpp /spec.d /.cproject From 0dacbeda4ba4ea40015b1cfef5d3ce4957dbec30 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Wed, 23 Feb 2022 19:24:37 +0100 Subject: [PATCH 21/32] Support for vs code and platformio --- .gitignore | 7 +++++++ .vscode/extensions.json | 10 ++++++++++ platformio.ini | 24 ++++++++++++++++++++++++ src/main.cpp | 9 +++++++++ 4 files changed, 50 insertions(+) create mode 100644 .vscode/extensions.json create mode 100644 platformio.ini create mode 100644 src/main.cpp diff --git a/.gitignore b/.gitignore index ec06c13f..16160c99 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,10 @@ Temporary Items /.pydevproject /.sproject /.settings + +# VS code +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..080e70d0 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 00000000..25783294 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,24 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:nanoatmega328] +platform = atmelavr +board = nanoatmega328 +framework = arduino +lib_deps = + jchristensen/JC_Button@^2.1.2 + miguelbalboa/MFRC522@^1.4.10 + featherfly/SoftwareSerial@^1.0 + makuna/DFPlayer Mini Mp3 by Makuna@1.0.7 +build_flags = + -std=c++17 + -D__IN_VS_CODE__ +build_unflags = + -std=gnu++11 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 00000000..9ae3a711 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,9 @@ +#ifdef __IN_VS_CODE__ +#include "Arduino.h" + +void setup() ; +void loop() ; + +#include "../Tonuino.ino" + +#endif From 1eb498e297d7d4fd951b5ec4b271f46acb4b9b54 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Thu, 3 Mar 2022 21:21:54 +0100 Subject: [PATCH 22/32] Minor improvements to reduce program memory --- src/array.hpp | 1 - src/buttons.cpp | 4 +- src/chip_card.cpp | 144 ++++++++++++++++++++---------------------- src/chip_card.hpp | 6 +- src/constants.hpp | 2 +- src/log.cpp | 1 + src/log.hpp | 1 + src/modifier.cpp | 16 ++--- src/modifier.hpp | 34 +++++----- src/mp3.cpp | 2 +- src/settings.cpp | 20 +++--- src/state_machine.cpp | 131 ++++++++++++++------------------------ src/state_machine.hpp | 29 +++++++-- src/tonuino.cpp | 38 +++++------ src/tonuino.hpp | 22 +++---- src/type_traits.hpp | 5 +- 16 files changed, 215 insertions(+), 241 deletions(-) diff --git a/src/array.hpp b/src/array.hpp index b374537c..dcb7936c 100644 --- a/src/array.hpp +++ b/src/array.hpp @@ -8,7 +8,6 @@ struct array { T data[N]; static constexpr uint8_t length() { return N; } - using type = T; // Item access T &operator[](uint8_t index) { return data[index]; } diff --git a/src/buttons.cpp b/src/buttons.cpp index f5291b4c..c373a94b 100644 --- a/src/buttons.cpp +++ b/src/buttons.cpp @@ -4,7 +4,7 @@ #include "logger.hpp" namespace { -const bool buttonPinIsActiveLow = (buttonPinType == levelType::activeLow); +constexpr bool buttonPinIsActiveLow = (buttonPinType == levelType::activeLow); } Buttons::Buttons(const Settings& settings) @@ -135,7 +135,7 @@ bool Buttons::isNoButton() { } bool Buttons::isReset() { - const int buttonActiveLevel = getLevel(buttonPinType, level::active); + constexpr int buttonActiveLevel = getLevel(buttonPinType, level::active); return (digitalRead(buttonPausePin) == buttonActiveLevel && digitalRead(buttonUpPin ) == buttonActiveLevel && digitalRead(buttonDownPin ) == buttonActiveLevel ); diff --git a/src/chip_card.cpp b/src/chip_card.cpp index 8654fa54..62825390 100644 --- a/src/chip_card.cpp +++ b/src/chip_card.cpp @@ -15,26 +15,37 @@ constexpr bool verbosePrintPiccType = false; namespace { -const __FlashStringHelper* str_failed () { return F(" failed: ") ; } -const __FlashStringHelper* str_MIFARE_Read() { return F("MIFARE_Read ") ; } +constexpr size_t buffferSizeRead = 18; // buffer size for read and write +constexpr size_t buffferSizeWrite = 16; // buffer size for read and write + +const __FlashStringHelper* str_failed () { return F(" failed: ") ; } +const __FlashStringHelper* str_MIFARE_Read () { return F("MIFARE_Read ") ; } +const __FlashStringHelper* str_MIFARE_Write() { return F("MIFARE_Write ") ; } /** Helper routine to dump a byte array as hex values to Serial. */ +void u8toa_hex(uint8_t number, char *arr) +{ + int pos = 0; + if (number < 16) + arr[pos++] = '0'; + do { + const int r = number % 16; + arr[pos++] = (r > 9) ? (r - 10) + 'a' : r + '0'; + number /= 16; + } while (number != 0); +} const char* dump_byte_array(byte * buffer, size_t bufferSize) { static char ret[3*10+1]; ret[0] = '\0'; if (bufferSize > 10) return ret; size_t pos = 0; - for (byte i = 0; i < bufferSize; ++i) { - const bool pad = buffer[i] < 0x10; + for (uint8_t i = 0; i < bufferSize; ++i) { ret[pos++] = ' '; - if (pad) - ret[pos++] = '0'; - utoa(buffer[i], ret+(pos++), HEX); - if (!pad) - ++pos; + u8toa_hex(buffer[i], &ret[pos]); + pos +=2; } ret[pos] = '\0'; return ret; @@ -101,7 +112,7 @@ bool Chip_card::readCard(nfcTagObject &nfcTag) { const MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); LOG(card_log, s_info, F("PICC type: "), printPiccType(mfrc522, piccType)); - byte buffer[18]; + byte buffer[buffferSizeRead]; MFRC522::StatusCode status = MFRC522::STATUS_ERROR; if (not auth(piccType)) @@ -119,56 +130,40 @@ bool Chip_card::readCard(nfcTagObject &nfcTag) { { byte size = sizeof(buffer); status = static_cast(mfrc522.MIFARE_Read(4, buffer, &size)); - if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, str_MIFARE_Read(), F("4"), str_failed(), printStatusCode(mfrc522, status)); - return false; - } + if (status != MFRC522::STATUS_OK) + LOG(card_log, s_debug, str_MIFARE_Read(), F("4"), str_failed(), printStatusCode(mfrc522, status)); } else if (piccType == MFRC522::PICC_TYPE_MIFARE_UL ) { - byte buffer2[18]; - byte size2 = sizeof(buffer2); - - status = static_cast(mfrc522.MIFARE_Read(8, buffer2, &size2)); - if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, str_MIFARE_Read(), F("8"), str_failed(), printStatusCode(mfrc522, status)); - return false; - } - memcpy(buffer, buffer2, 4); - - status = static_cast(mfrc522.MIFARE_Read(9, buffer2, &size2)); - if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, str_MIFARE_Read(), F("9"), str_failed(), printStatusCode(mfrc522, status)); - return false; - } - memcpy(buffer + 4, buffer2, 4); - - status = static_cast(mfrc522.MIFARE_Read(10, buffer2, &size2)); - if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, str_MIFARE_Read(), F("10"), str_failed(), printStatusCode(mfrc522, status)); - return false; - } - memcpy(buffer + 8, buffer2, 4); - - status = static_cast(mfrc522.MIFARE_Read(11, buffer2, &size2)); - if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, str_MIFARE_Read(), F("11"), str_failed(), printStatusCode(mfrc522, status)); - return false; + byte buffer2[buffferSizeRead]; + + for (byte block = 8, bufpos = 0; block <= 11; ++block, bufpos += 4) { + byte size2 = sizeof(buffer2); + status = static_cast(mfrc522.MIFARE_Read(8, buffer2, &size2)); + if (status != MFRC522::STATUS_OK) { + LOG(card_log, s_debug, str_MIFARE_Read(), block, str_failed(), printStatusCode(mfrc522, status)); + break; + } + memcpy(buffer+bufpos, buffer2, 4); } - memcpy(buffer + 12, buffer2, 4); } stopCrypto1(); + if (status != MFRC522::STATUS_OK) { + LOG(card_log, s_error, str_MIFARE_Read(), str_failed(), printStatusCode(mfrc522, status)); + return false; + } + LOG(card_log, s_info, F("Data on Card: "), dump_byte_array(buffer, 9)); - uint32_t tempCookie; - tempCookie = (uint32_t)buffer[0] << 24; - tempCookie += (uint32_t)buffer[1] << 16; - tempCookie += (uint32_t)buffer[2] << 8; - tempCookie += (uint32_t)buffer[3]; + uint32_t tempCookie = 0; + for (byte i = 0, shift = 24; i < 4; ++i, shift -= 8) + tempCookie += static_cast(buffer[i]) << shift; + + if (tempCookie != cardCookie) + return false; - nfcTag.cookie = tempCookie; - uint32_t version = buffer[4]; + const uint32_t version = buffer[4]; if (version == cardVersion) { nfcTag.nfcFolderSettings.folder = buffer[5]; nfcTag.nfcFolderSettings.mode = static_cast(buffer[6]); @@ -189,15 +184,15 @@ bool Chip_card::writeCard(const nfcTagObject &nfcTag) { constexpr byte coockie_3 = (cardCookie & 0x0000ff00) >> 8; constexpr byte coockie_2 = (cardCookie & 0x00ff0000) >> 16; constexpr byte coockie_1 = (cardCookie & 0xff000000) >> 24; - byte buffer[16] = {coockie_1, coockie_2, coockie_3, coockie_4, // 0x1337 0xb347 magic cookie to - // identify our nfc tags - nfcTag.version, // version 1 - nfcTag.nfcFolderSettings.folder, // the folder picked by the user - static_cast(nfcTag.nfcFolderSettings.mode), // the playback mode picked by the user - nfcTag.nfcFolderSettings.special, // track or function for admin cards - nfcTag.nfcFolderSettings.special2, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; + byte buffer[buffferSizeWrite] = {coockie_1, coockie_2, coockie_3, coockie_4, // 0x1337 0xb347 magic cookie to + // identify our nfc tags + cardVersion, // version 1 + nfcTag.nfcFolderSettings.folder, // the folder picked by the user + static_cast(nfcTag.nfcFolderSettings.mode), // the playback mode picked by the user + nfcTag.nfcFolderSettings.special, // track or function for admin cards + nfcTag.nfcFolderSettings.special2, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; const MFRC522::PICC_Type mifareType = mfrc522.PICC_GetType(mfrc522.uid.sak); @@ -213,33 +208,28 @@ bool Chip_card::writeCard(const nfcTagObject &nfcTag) { (mifareType == MFRC522::PICC_TYPE_MIFARE_1K ) || (mifareType == MFRC522::PICC_TYPE_MIFARE_4K ) ) { - status = (MFRC522::StatusCode)mfrc522.MIFARE_Write(4, buffer, 16); + status = (MFRC522::StatusCode)mfrc522.MIFARE_Write(4, buffer, sizeof(buffer)); + if (status != MFRC522::STATUS_OK) + LOG(card_log, s_debug, str_MIFARE_Write(), F("4"), str_failed(), printStatusCode(mfrc522, status)); } else if (mifareType == MFRC522::PICC_TYPE_MIFARE_UL ) { - byte buffer2[16]; + byte buffer2[buffferSizeWrite]; memset(buffer2, 0, sizeof(buffer2)); - //memset(buffer2, 0, size2); - memcpy(buffer2, buffer, 4); - status = static_cast(mfrc522.MIFARE_Write(8, buffer2, 16)); - - //memset(buffer2, 0, size2); - memcpy(buffer2, buffer + 4, 4); - status = static_cast(mfrc522.MIFARE_Write(9, buffer2, 16)); - - //memset(buffer2, 0, size2); - memcpy(buffer2, buffer + 8, 4); - status = static_cast(mfrc522.MIFARE_Write(10, buffer2, 16)); - - //memset(buffer2, 0, size2); - memcpy(buffer2, buffer + 12, 4); - status = static_cast(mfrc522.MIFARE_Write(11, buffer2, 16)); + for (byte block = 8, bufpos = 0; block <= 11; ++block, bufpos += 4) { + memcpy(buffer2, buffer+bufpos, 4); + status = static_cast(mfrc522.MIFARE_Write(block, buffer2, sizeof(buffer2))); + if (status != MFRC522::STATUS_OK) { + LOG(card_log, s_debug, str_MIFARE_Write(), block, str_failed(), printStatusCode(mfrc522, status)); + break; + } + } } stopCrypto1(); if (status != MFRC522::STATUS_OK) { - LOG(card_log, s_error, F("MIFARE_Write() failed: "), printStatusCode(mfrc522, status)); + LOG(card_log, s_error, str_MIFARE_Write(), str_failed(), printStatusCode(mfrc522, status)); return false; } return true; diff --git a/src/chip_card.hpp b/src/chip_card.hpp index 5d5dc50b..297263f8 100644 --- a/src/chip_card.hpp +++ b/src/chip_card.hpp @@ -48,13 +48,9 @@ struct folderSettings { // this object stores nfc tag data struct nfcTagObject { - uint32_t cookie = cardCookie; - uint8_t version = cardVersion; folderSettings nfcFolderSettings; bool operator==(const nfcTagObject& rhs) const { - return cookie == rhs.cookie && - version == rhs.version && - nfcFolderSettings == rhs.nfcFolderSettings; + return nfcFolderSettings == rhs.nfcFolderSettings; } }; diff --git a/src/constants.hpp b/src/constants.hpp index d9f87a97..97a2c8a7 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -14,7 +14,7 @@ enum class levelType: uint8_t { activeLow, }; -inline int getLevel(levelType t, level l) { return (l == level::inactive) ? (t==levelType::activeHigh ? LOW : HIGH) +inline constexpr int getLevel(levelType t, level l) { return (l == level::inactive) ? (t==levelType::activeHigh ? LOW : HIGH) : (t==levelType::activeHigh ? HIGH : LOW ); } // ####### buttons ##################################### diff --git a/src/log.cpp b/src/log.cpp index cdb3e802..cd5f6b10 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -6,6 +6,7 @@ const __FlashStringHelper* getSeverityName(severity sev) { case s_info : return F("I"); case s_warning: return F("W"); case s_error : return F("E"); + case s_none : return F("?"); } return F("unknown"); } diff --git a/src/log.hpp b/src/log.hpp index 925a9638..b3f35961 100644 --- a/src/log.hpp +++ b/src/log.hpp @@ -22,6 +22,7 @@ enum severity: uint8_t { s_info , s_warning, s_error , + s_none , }; extern const __FlashStringHelper* getSeverityName(severity sev); diff --git a/src/modifier.cpp b/src/modifier.cpp index f45c7d01..ff9145eb 100644 --- a/src/modifier.cpp +++ b/src/modifier.cpp @@ -30,19 +30,17 @@ void SleepTimer::start(uint8_t minutes) { } void FreezeDance::loop() { - if (nextStopAtMillis != 0 && millis() > nextStopAtMillis) { + if (stopTimer.isExpired()) { LOG(modifier_log, s_info, str_FreezeDance(), F(" -> FREEZE!")); - if (mp3.isPlaying()) { - mp3.playAdvertisement(advertTracks::t_301_freeze_freeze); - } + mp3.playAdvertisement(advertTracks::t_301_freeze_freeze); setNextStopAtMillis(); } } void FreezeDance::setNextStopAtMillis() { const uint16_t seconds = random(minSecondsBetweenStops, maxSecondsBetweenStops + 1); - LOG(modifier_log, s_info, str_FreezeDance(), F(" next stop at: "), seconds); - nextStopAtMillis = millis() + seconds * 1000; + LOG(modifier_log, s_info, str_FreezeDance(), F(" next stop in "), seconds); + stopTimer.start(seconds * 1000); } bool KindergardenMode::handleNext() { @@ -87,7 +85,7 @@ bool RepeatSingleModifier::handlePrevious() { // } else { // playAdvertisement(volume, false); // } -// LOG(modifier_log, s_info, F("= FeedbackModifier::handleVolumeDown()!")); +// LOG(modifier_log, s_info, F("FeedbackModifier::handleVolumeDown()!")); // return false; //} //bool FeedbackModifier::handleVolumeUp() { @@ -96,11 +94,11 @@ bool RepeatSingleModifier::handlePrevious() { // } else { // playAdvertisement(volume, false); // } -// LOG(modifier_log, s_info, F("= FeedbackModifier::handleVolumeUp()!")); +// LOG(modifier_log, s_info, F("FeedbackModifier::handleVolumeUp()!")); // return false; //} //bool FeedbackModifier::handleRFID(const nfcTagObject &/*newCard*/) { -// LOG(modifier_log, s_info, F("= FeedbackModifier::handleRFID()")); +// LOG(modifier_log, s_info, F("FeedbackModifier::handleRFID()")); // return false; //} diff --git a/src/modifier.hpp b/src/modifier.hpp index e74d5650..4f24adc1 100644 --- a/src/modifier.hpp +++ b/src/modifier.hpp @@ -55,21 +55,21 @@ class FreezeDance: public Modifier { void setNextStopAtMillis(); private: - unsigned long nextStopAtMillis = 0; - const uint8_t minSecondsBetweenStops = 5; - const uint8_t maxSecondsBetweenStops = 30; + Timer stopTimer{}; + static constexpr uint8_t minSecondsBetweenStops = 5; + static constexpr uint8_t maxSecondsBetweenStops = 30; }; class Locked: public Modifier { public: Locked(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} - bool handlePause () final { LOG(modifier_log, s_debug, F("= Locked::handlePause() -> LOCKED!")) ; return true; } - bool handleNextButton () final { LOG(modifier_log, s_debug, F("= Locked::handleNextButton() -> LOCKED!")) ; return true; } - bool handlePreviousButton() final { LOG(modifier_log, s_debug, F("= Locked::handlePreviousButton() -> LOCKED!")); return true; } - bool handleVolumeUp () final { LOG(modifier_log, s_debug, F("= Locked::handleVolumeUp() -> LOCKED!")) ; return true; } - bool handleVolumeDown () final { LOG(modifier_log, s_debug, F("= Locked::handleVolumeDown() -> LOCKED!")) ; return true; } + bool handlePause () final { LOG(modifier_log, s_debug, F("Locked::Pause -> LOCKED!")) ; return true; } + bool handleNextButton () final { LOG(modifier_log, s_debug, F("Locked::NextButton -> LOCKED!")); return true; } + bool handlePreviousButton() final { LOG(modifier_log, s_debug, F("Locked::PrevButton -> LOCKED!")); return true; } + bool handleVolumeUp () final { LOG(modifier_log, s_debug, F("Locked::VolumeUp -> LOCKED!")) ; return true; } + bool handleVolumeDown () final { LOG(modifier_log, s_debug, F("Locked::VolumeDown -> LOCKED!")); return true; } bool handleRFID(const nfcTagObject&) - final { LOG(modifier_log, s_debug, F("= Locked::handleRFID() -> LOCKED!")) ; return true; } + final { LOG(modifier_log, s_debug, F("Locked::RFID -> LOCKED!")) ; return true; } mode_t getActive() final { return mode_t::locked; } }; @@ -77,11 +77,11 @@ class Locked: public Modifier { class ToddlerMode: public Modifier { public: ToddlerMode(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} - bool handlePause () final { LOG(modifier_log, s_debug, F("= ToddlerMode::handlePause() -> LOCKED!")) ; return true; } - bool handleNextButton () final { LOG(modifier_log, s_debug, F("= ToddlerMode::handleNextButton() -> LOCKED!")) ; return true; } - bool handlePreviousButton() final { LOG(modifier_log, s_debug, F("= ToddlerMode::handlePreviousButton() -> LOCKED!")); return true; } - bool handleVolumeUp () final { LOG(modifier_log, s_debug, F("= ToddlerMode::handleVolumeUp() -> LOCKED!")) ; return true; } - bool handleVolumeDown () final { LOG(modifier_log, s_debug, F("= ToddlerMode::handleVolumeDown() -> LOCKED!")) ; return true; } + bool handlePause () final { LOG(modifier_log, s_debug, F("ToddlerMode::Pause -> LOCKED!")) ; return true; } + bool handleNextButton () final { LOG(modifier_log, s_debug, F("ToddlerMode::NextButton -> LOCKED!")); return true; } + bool handlePreviousButton() final { LOG(modifier_log, s_debug, F("ToddlerMode::PrevButton -> LOCKED!")); return true; } + bool handleVolumeUp () final { LOG(modifier_log, s_debug, F("ToddlerMode::VolumeUp -> LOCKED!")) ; return true; } + bool handleVolumeDown () final { LOG(modifier_log, s_debug, F("ToddlerMode::VolumeDown -> LOCKED!")); return true; } mode_t getActive() final { return mode_t::toddler; } }; @@ -91,9 +91,9 @@ class KindergardenMode: public Modifier { KindergardenMode(Tonuino &tonuino, Mp3 &mp3, const Settings &settings): Modifier(tonuino, mp3, settings) {} bool handleNext() final; -//bool handlePause () final { LOG(modifier_log, s_debug, F("= KindergardenMode::handlePause() -> LOCKED!")) ; return true; } - bool handleNextButton () final { LOG(modifier_log, s_debug, F("= KindergardenMode::handleNextButton() -> LOCKED!")) ; return true; } - bool handlePreviousButton() final { LOG(modifier_log, s_debug, F("= KindergardenMode::handlePreviousButton() -> LOCKED!")); return true; } +//bool handlePause () final { LOG(modifier_log, s_debug, F("KindergardenMode::Pause -> LOCKED!")) ; return true; } + bool handleNextButton () final { LOG(modifier_log, s_debug, F("KindergardenMode::NextButton -> LOCKED!")); return true; } + bool handlePreviousButton() final { LOG(modifier_log, s_debug, F("KindergardenMode::PrevButton -> LOCKED!")); return true; } bool handleRFID(const nfcTagObject &newCard) final; mode_t getActive () final { return mode_t::kindergarden; } diff --git a/src/mp3.cpp b/src/mp3.cpp index 51ec3ca3..6a16dc54 100644 --- a/src/mp3.cpp +++ b/src/mp3.cpp @@ -13,7 +13,7 @@ uint16_t Mp3Notify::lastTrackFinished = 0; void Mp3Notify::OnError(uint16_t errorCode) { // see DfMp3_Error for code meaning - LOG(mp3_log, s_error, F("Com Error: "), errorCode); + LOG(mp3_log, s_error, F("DfPlayer Error: "), errorCode); } void Mp3Notify::OnPlaySourceOnline (DfMp3_PlaySources source) { PrintlnSourceAction(source, F("online" )); } void Mp3Notify::OnPlaySourceInserted(DfMp3_PlaySources source) { PrintlnSourceAction(source, F("bereit" )); } diff --git a/src/settings.cpp b/src/settings.cpp index c878dcf7..1bb6c4ef 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -20,19 +20,19 @@ uint8_t Settings::readByteFromFlash(uint16_t address) { } void Settings::clearEEPROM() { - LOG(settings_log, s_info, F("Reset -> EEPROM wird gelöscht")); + LOG(settings_log, s_info, F("clearEEPROM")); for (uint16_t i = 0; i < EEPROM.length(); i++) { writeByteToFlash(i, 0); } } void Settings::writeSettingsToFlash() { - LOG(settings_log, s_debug, F("=== writeSettingsToFlash()")); + LOG(settings_log, s_debug, F("writeSettingsToFlash")); EEPROM.put(startAddressAdminSettings, *this); } void Settings::resetSettings() { - LOG(settings_log, s_debug, F("=== resetSettings()")); + LOG(settings_log, s_debug, F("resetSettings")); cookie = cardCookie; version = 2; maxVolume = 25; @@ -58,7 +58,7 @@ void Settings::resetSettings() { void Settings::migrateSettings(int oldVersion) { if (oldVersion == 1) { - LOG(settings_log, s_info, F("=== migradeSettings() 1 -> 2")); + LOG(settings_log, s_info, F("migradeSettings 1 -> 2")); version = 2; adminMenuLocked = 0; adminMenuPin[0] = 1; @@ -71,27 +71,27 @@ void Settings::migrateSettings(int oldVersion) { } void Settings::loadSettingsFromFlash() { - LOG(settings_log, s_debug, F("=== loadSettingsFromFlash()")); + LOG(settings_log, s_debug, F("loadSettingsFromFlash")); EEPROM.get(startAddressAdminSettings, *this); if (cookie != cardCookie) resetSettings(); migrateSettings(version); LOG(settings_log, s_info, F("Version: " ), version); - LOG(settings_log, s_info, F("Maximal Volume: " ), maxVolume); - LOG(settings_log, s_info, F("Minimal Volume: " ), minVolume); - LOG(settings_log, s_info, F("Initial Volume: " ), initVolume); + LOG(settings_log, s_info, F("Max Vol: " ), maxVolume); + LOG(settings_log, s_info, F("Min Vol: " ), minVolume); + LOG(settings_log, s_info, F("Init Vol: " ), initVolume); LOG(settings_log, s_info, F("EQ: " ), eq); LOG(settings_log, s_info, F("Locked: " ), locked); LOG(settings_log, s_info, F("Sleep Timer: " ), standbyTimer); - LOG(settings_log, s_info, F("Inverted Volume Buttons: "), invertVolumeButtons); + LOG(settings_log, s_info, F("Inverted Vol Buttons: " ), invertVolumeButtons); LOG(settings_log, s_info, F("Admin Menu locked: " ), adminMenuLocked); LOG(settings_log, s_info, F("Admin Menu Pin: " ), adminMenuPin[0], adminMenuPin[1], adminMenuPin[2], adminMenuPin[3]); LOG(settings_log, s_info, F("Pause when card removed: "), pauseWhenCardRemoved); } void Settings::writeFolderSettingToFlash(uint8_t folder, uint16_t track) { - writeByteToFlash(folder, min(track, 0xff)); + writeByteToFlash(folder, min(track, 0xffu)); } uint16_t Settings::readFolderSettingFromFlash(uint8_t folder) { diff --git a/src/state_machine.cpp b/src/state_machine.cpp index 44ef44e1..cf8f5a10 100644 --- a/src/state_machine.cpp +++ b/src/state_machine.cpp @@ -5,8 +5,8 @@ class Admin_Entry; class Idle; -//using Admin_End = Idle; -using Admin_End = Admin_Entry; +//using Admin_End = Idle; // use this if you want end admin menu after one setting +using Admin_End = Admin_Entry; // use this if you want continue admin menu after one setting namespace { @@ -23,7 +23,7 @@ const __FlashStringHelper* str_Pause () { return F("Pause") ; const __FlashStringHelper* str_Admin_Allow () { return F("AdmAllow") ; } const __FlashStringHelper* str_Admin_Entry () { return F("AdmEntry") ; } const __FlashStringHelper* str_Admin_NewCard () { return F("AdmNewCard") ; } -const __FlashStringHelper* str_Admin_SimpleSetting () { return F("Admin_SimpleSetting") ; } +const __FlashStringHelper* str_Admin_SimpleSetting () { return F("AdmSimpleSetting") ; } const __FlashStringHelper* str_Admin_ModCard () { return F("AdmModCard") ; } const __FlashStringHelper* str_Admin_ShortCut () { return F("AdmShortCut") ; } const __FlashStringHelper* str_Admin_StandbyTimer () { return F("AdmStandbyTimer") ; } @@ -61,35 +61,38 @@ class VoiceMenu : public SM static bool previewStarted ; }; -class ChMode : public VoiceMenu +using VoiceMenu_tonuino = VoiceMenu; +using VoiceMenu_setupCard = VoiceMenu; + +class ChMode : public VoiceMenu_setupCard { public: void entry() final; void react(button_e const &) final; }; -class ChFolder : public VoiceMenu +class ChFolder : public VoiceMenu_setupCard { public: void entry() final; void react(button_e const &) final; }; -class ChTrack : public VoiceMenu +class ChTrack : public VoiceMenu_setupCard { public: void entry() final; void react(button_e const &) final; }; -class ChFirstTrack : public VoiceMenu +class ChFirstTrack : public VoiceMenu_setupCard { public: void entry() final; void react(button_e const &) final; }; -class ChLastTrack : public VoiceMenu +class ChLastTrack : public VoiceMenu_setupCard { public: void entry() final; @@ -111,7 +114,7 @@ class WriteCard : public SM_writeCard subState current_subState; }; -class Admin_Allow: public VoiceMenu +class Admin_Allow: public VoiceMenu_tonuino { public: void entry() final; @@ -136,7 +139,7 @@ class Admin_Allow: public VoiceMenu // uint8_t av, bv, cv; }; -class Admin_Entry: public VoiceMenu +class Admin_Entry: public VoiceMenu_tonuino { public: void entry() final; @@ -161,7 +164,7 @@ class Admin_NewCard: public SM_tonuino subState current_subState; }; -class Admin_SimpleSetting: public VoiceMenu +class Admin_SimpleSetting: public VoiceMenu_tonuino { public: void entry() final; @@ -175,7 +178,7 @@ class Admin_SimpleSetting: public VoiceMenu static Type type; }; -class Admin_ModCard: public VoiceMenu +class Admin_ModCard: public VoiceMenu_tonuino { public: void entry() final; @@ -191,7 +194,7 @@ class Admin_ModCard: public VoiceMenu bool readyToWrite; }; -class Admin_ShortCut: public VoiceMenu +class Admin_ShortCut: public VoiceMenu_tonuino { public: void entry() final; @@ -206,14 +209,14 @@ class Admin_ShortCut: public VoiceMenu uint8_t shortcut; }; -class Admin_StandbyTimer: public VoiceMenu +class Admin_StandbyTimer: public VoiceMenu_tonuino { public: void entry() final; void react(button_e const &) final; }; -class Admin_CardsForFolder: public VoiceMenu +class Admin_CardsForFolder: public VoiceMenu_tonuino { public: void entry() final; @@ -235,7 +238,7 @@ class Admin_CardsForFolder: public VoiceMenu uint8_t special2; }; -class Admin_InvButtons: public VoiceMenu +class Admin_InvButtons: public VoiceMenu_tonuino { public: void entry() final; @@ -249,7 +252,7 @@ class Admin_ResetEeprom: public SM_tonuino void react(button_e const &) final; }; -class Admin_LockAdmin: public VoiceMenu +class Admin_LockAdmin: public VoiceMenu_tonuino { public: void entry() final; @@ -265,7 +268,7 @@ class Admin_LockAdmin: public VoiceMenu uint8_t pin_number; }; -class Admin_PauseIfCardRemoved: public VoiceMenu +class Admin_PauseIfCardRemoved: public VoiceMenu_tonuino { public: void entry() final; @@ -280,6 +283,7 @@ bool SM::isAbort(button_e const &b) { if (b.b == buttonRaw::pauseLong) { SM::mp3.enqueueMp3FolderTrack(mp3Tracks::t_802_reset_aborted); LOG(state_log, s_info, F("SM"), str_abort()); + this->template transit(); return true; } return false; @@ -368,10 +372,8 @@ void ChMode::react(button_e const &b) { } VoiceMenu::react(b); - if (isAbort(b)) { - transit(); + if (isAbort(b)) return; - } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { folder.mode = static_cast(currentValue); @@ -379,7 +381,7 @@ void ChMode::react(button_e const &b) { if (folder.mode == mode_t::admin) { folder.folder = 0; folder.mode = mode_t::admin_card; - transit(); + transit(); return; } transit(); @@ -406,10 +408,8 @@ void ChFolder::react(button_e const &b) { } VoiceMenu::react(b); - if (isAbort(b)) { - transit(); + if (isAbort(b)) return; - } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { folder.folder = currentValue; @@ -424,7 +424,7 @@ void ChFolder::react(button_e const &b) { transit(); return; } - transit(); + transit(); return; } }; @@ -448,15 +448,13 @@ void ChTrack::react(button_e const &b) { } VoiceMenu::react(b); - if (isAbort(b)) { - transit(); + if (isAbort(b)) return; - } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { folder.special = currentValue; LOG(state_log, s_info, str_ChTrack(), F(": "), currentValue); - transit(); + transit(); return; } }; @@ -480,10 +478,8 @@ void ChFirstTrack::react(button_e const &b) { } VoiceMenu::react(b); - if (isAbort(b)) { - transit(); + if (isAbort(b)) return; - } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { folder.special = currentValue; @@ -515,15 +511,13 @@ void ChLastTrack::react(button_e const &b) { } VoiceMenu::react(b); - if (isAbort(b)) { - transit(); + if (isAbort(b)) return; - } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { folder.special2 = currentValue; LOG(state_log, s_info, str_ChLastTrack(), F(": "), currentValue); - transit(); + transit(); return; } }; @@ -540,12 +534,8 @@ void WriteCard::react(button_e const &b) { LOG(state_log, s_debug, str_WriteCard(), F("::react() "), static_cast(b.b)); } - if (b.b == buttonRaw::pauseLong) { - mp3.enqueueMp3FolderTrack(mp3Tracks::t_802_reset_aborted); - LOG(state_log, s_info, str_WriteCard(), str_to(), F("finished_abort")); - transit(); + if (isAbort(b)) return; - } switch (current_subState) { case start_waitCardInserted: @@ -571,7 +561,7 @@ void WriteCard::react(button_e const &b) { case run_waitCardRemoved: if (chip_card.isCardRemoved()) { LOG(state_log, s_info, str_WriteCard(), str_to(), F("finished")); - transit(); + transit(); } break; default: @@ -585,9 +575,6 @@ bool Base::readCard() { if (not chip_card.readCard(lastCardRead)) return false; - if (lastCardRead.cookie != cardCookie) - return false; - if (lastCardRead.nfcFolderSettings.folder == 0) { if (lastCardRead.nfcFolderSettings.mode == mode_t::admin_card) { LOG(state_log, s_debug, F("Base"), str_to(), str_Admin_Entry()); @@ -891,10 +878,8 @@ void Admin_Allow::react(button_e const &b) { LOG(state_log, s_debug, str_Admin_Allow(), F("::react() "), static_cast(b.b)); } - if (isAbort(b)) { - transit(); + if (isAbort(b)) return; - } switch (current_subState) { case select_method : @@ -1002,7 +987,7 @@ void Admin_Allow::react(button_e const &b) { return; case not_allow: LOG(state_log, s_debug, str_Admin_Allow(), str_abort()); - transit(); + transit(); return; } }; @@ -1039,10 +1024,8 @@ void Admin_Entry::react(button_e const &b) { } VoiceMenu::react(b); - if (isAbort(b)) { - transit(); + if (isAbort(b)) return; - } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { lastCurrentValue = currentValue; @@ -1130,7 +1113,7 @@ void Admin_NewCard::react(button_e const &b) { current_subState = end_setupCard; if (SM_setupCard::is_in_state()) { LOG(state_log, s_info, str_Admin_NewCard(), str_abort()); - transit(); + transit(); return; } break; @@ -1148,7 +1131,7 @@ void Admin_NewCard::react(button_e const &b) { current_subState = end_writeCard; if (SM_writeCard::is_in_state()) { LOG(state_log, s_info, str_Admin_NewCard(), str_abort()); - transit(); + transit(); return; } break; @@ -1195,10 +1178,8 @@ void Admin_SimpleSetting::react(button_e const &b) { } VoiceMenu::react(b); - if (isAbort(b)) { - transit(); + if (isAbort(b)) return; - } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { switch (type) { @@ -1241,10 +1222,8 @@ void Admin_ModCard::react(button_e const &b) { } VoiceMenu::react(b); - if (isAbort(b)) { - transit(); + if (isAbort(b)) return; - } if (readyToWrite) { switch (current_subState) { @@ -1278,7 +1257,7 @@ void Admin_ModCard::react(button_e const &b) { current_subState = end_writeCard; if (SM_writeCard::is_in_state()) { LOG(state_log, s_info, str_Admin_ModCard(), str_abort()); - transit(); + transit(); return; } break; @@ -1335,10 +1314,8 @@ void Admin_ShortCut::react(button_e const &b) { } VoiceMenu::react(b); - if (isAbort(b)) { - transit(); + if (isAbort(b)) return; - } if (shortcut == 0) { if ((b.b == buttonRaw::pause) && (currentValue != 0)) { @@ -1358,7 +1335,7 @@ void Admin_ShortCut::react(button_e const &b) { current_subState = end_setupCard; if (SM_setupCard::is_in_state()) { LOG(state_log, s_info, str_Admin_ShortCut(), str_abort()); - transit(); + transit(); return; } break; @@ -1395,10 +1372,8 @@ void Admin_StandbyTimer::react(button_e const &b) { } VoiceMenu::react(b); - if (isAbort(b)) { - transit(); + if (isAbort(b)) return; - } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { switch (currentValue) { @@ -1431,10 +1406,8 @@ void Admin_CardsForFolder::react(button_e const &b) { LOG(state_log, s_debug, str_Admin_CardsForFolder(), F("::react() "), static_cast(b.b)); } - if (isAbort(b)) { - transit(); + if (isAbort(b)) return; - } switch (current_subState) { case start_getFolder: @@ -1520,7 +1493,7 @@ void Admin_CardsForFolder::react(button_e const &b) { } else if (SM_writeCard::is_in_state()) { LOG(state_log, s_info, str_Admin_CardsForFolder(), str_abort()); - transit(); + transit(); return; } break; @@ -1549,10 +1522,8 @@ void Admin_InvButtons::react(button_e const &b) { } VoiceMenu::react(b); - if (isAbort(b)) { - transit(); + if (isAbort(b)) return; - } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { switch (currentValue) { @@ -1603,10 +1574,8 @@ void Admin_LockAdmin::react(button_e const &b) { LOG(state_log, s_debug, str_Admin_LockAdmin(), F("::react() "), static_cast(b.b)); } - if (isAbort(b)) { - transit(); + if (isAbort(b)) return; - } switch(current_subState) { case get_mode: @@ -1663,10 +1632,8 @@ void Admin_PauseIfCardRemoved::react(button_e const &b) { } VoiceMenu::react(b); - if (isAbort(b)) { - transit(); + if (isAbort(b)) return; - } if ((b.b == buttonRaw::pause) && (currentValue != 0)) { switch (currentValue) { diff --git a/src/state_machine.hpp b/src/state_machine.hpp index 81c82841..ca711134 100644 --- a/src/state_machine.hpp +++ b/src/state_machine.hpp @@ -24,6 +24,12 @@ struct card_e : tinyfsm::Event { // ---------------------------------------------------------------------------- // State Machine Base Class Declaration // +class finished_setupCard ; +class finished_abort_setupCard; +class finished_writeCard ; +class finished_abort_writeCard; +class Idle ; + enum class SM_type: uint8_t { tonuino, setupCard, @@ -33,6 +39,19 @@ template class SM: public tinyfsm::Fsm> { public: + typedef typename conditional< + (SMT == SM_type::setupCard), finished_setupCard, + typename conditional< + (SMT == SM_type::writeCard), finished_writeCard, + Idle>::type + >::type finished; + typedef typename conditional< + (SMT == SM_type::setupCard), finished_abort_setupCard, + typename conditional< + (SMT == SM_type::writeCard), finished_abort_writeCard, + Idle>::type + >::type finished_abort; + virtual void react(button_e const &) { }; virtual void react(card_e const &) { }; @@ -50,12 +69,12 @@ class SM: public tinyfsm::Fsm> static Chip_card &chip_card; static Timer timer; - static bool waitForPlayFinish; + static bool waitForPlayFinish; // with this it needs 66 Byte lesser program code ;-) }; -typedef SM SM_tonuino; -typedef SM SM_setupCard; -typedef SM SM_writeCard; +using SM_tonuino = SM; +using SM_setupCard = SM; +using SM_writeCard = SM; class Base: public SM_tonuino { @@ -103,6 +122,4 @@ class finished_abort_setupCard: public SM_setupCard{}; class finished_writeCard : public SM_writeCard{}; class finished_abort_writeCard: public SM_writeCard{}; - - #endif /* SRC_STATE_MACHINE_HPP_ */ diff --git a/src/tonuino.cpp b/src/tonuino.cpp index 1cc00fb0..586c4271 100644 --- a/src/tonuino.cpp +++ b/src/tonuino.cpp @@ -47,11 +47,10 @@ void Tonuino::setup() { void Tonuino::loop() { unsigned long start_cycle = millis(); - checkStandbyAtMillis(); + checkStandby(); mp3.loop(); - // Modifier : WIP! activeModifier->loop(); SM_tonuino::dispatch(button_e(buttons.getButtonRaw())); @@ -64,46 +63,49 @@ void Tonuino::loop() { } void Tonuino::playFolder() { - LOG(play_log, s_debug, F("= playFolder()")); + LOG(play_log, s_debug, F("playFolder")); numTracksInFolder = mp3.getFolderTrackCount(myFolder->folder); LOG(play_log, s_info, numTracksInFolder, F(" files in folder "), myFolder->folder); - numTracksInFolder = min(numTracksInFolder, 0xff); + numTracksInFolder = min(numTracksInFolder, 0xffu); mp3.clearAllQueue(); switch (myFolder->mode) { case mode_t::hoerspiel: // Hörspielmodus: eine zufällige Datei aus dem Ordner - LOG(play_log, s_info, F("Hörspiel")); myFolder->special = 1; myFolder->special2 = numTracksInFolder; __attribute__ ((fallthrough)); + /* no break */ case mode_t::hoerspiel_vb: // Spezialmodus Von-Bin: Hörspiel: eine zufällige Datei aus dem Ordner - LOG(play_log, s_info, myFolder->special, F(" bis "), myFolder->special2); + LOG(play_log, s_info, F("Hörspiel")); + LOG(play_log, s_info, myFolder->special, str_bis(), myFolder->special2); mp3.enqueueTrack(myFolder->folder, random(myFolder->special, myFolder->special2 + 1)); break; case mode_t::album: // Album Modus: kompletten Ordner spielen - LOG(play_log, s_info, F("Album")); myFolder->special = 1; myFolder->special2 = numTracksInFolder; __attribute__ ((fallthrough)); + /* no break */ case mode_t::album_vb: // Spezialmodus Von-Bis: Album: alle Dateien zwischen Start und Ende spielen + LOG(play_log, s_info, F("Album")); LOG(play_log, s_info, myFolder->special, str_bis() , myFolder->special2); mp3.enqueueTrack(myFolder->folder, myFolder->special, myFolder->special2); break; case mode_t::party: // Party Modus: Ordner in zufälliger Reihenfolge - LOG(play_log, s_info, F("Party")); myFolder->special = 1; myFolder->special2 = numTracksInFolder; __attribute__ ((fallthrough)); + /* no break */ case mode_t::party_vb: // Spezialmodus Von-Bis: Party Ordner in zufälliger Reihenfolge + LOG(play_log, s_info, F("Party")); LOG(play_log, s_info, myFolder->special, str_bis(), myFolder->special2); mp3.enqueueTrack(myFolder->folder, myFolder->special, myFolder->special2); mp3.shuffleQueue(); @@ -141,7 +143,7 @@ void Tonuino::playTrackNumber () { // Leider kann das Modul selbst keine Queue abspielen, daher müssen wir selbst die Queue verwalten void Tonuino::nextTrack(bool fromOnPlayFinished) { - LOG(play_log, s_info, F("= nextTrack()")); + LOG(play_log, s_info, F("nextTrack")); if (activeModifier->handleNext()) return; if (mp3.isPlayingFolder() && (myFolder->mode == mode_t::hoerbuch || myFolder->mode == mode_t::hoerbuch_1)) { @@ -154,7 +156,7 @@ void Tonuino::nextTrack(bool fromOnPlayFinished) { } void Tonuino::previousTrack() { - LOG(play_log, s_info, F("= previousTrack()")); + LOG(play_log, s_info, F("previousTrack")); if (mp3.isPlayingFolder() && (myFolder->mode == mode_t::hoerbuch || myFolder->mode == mode_t::hoerbuch_1)) { const uint8_t trackToSave = (mp3.getCurrentTrack() > numTracksInFolder) ? mp3.getCurrentTrack()-1 : 1; settings.writeFolderSettingToFlash(myFolder->folder, trackToSave); @@ -164,7 +166,7 @@ void Tonuino::previousTrack() { // Funktionen für den Standby Timer (z.B. über Pololu-Switch oder Mosfet) void Tonuino::setStandbyTimer() { - LOG(standby_log, s_info, F("= setStandbyTimer()")); + LOG(standby_log, s_info, F("setStandbyTimer")); if (settings.standbyTimer != 0 && not standbyTimer.isActive()) { standbyTimer.start(settings.standbyTimer * 60 * 1000); LOG(standby_log, s_info, F("timer started")); @@ -172,14 +174,14 @@ void Tonuino::setStandbyTimer() { } void Tonuino::disableStandbyTimer() { - LOG(standby_log, s_info, F("= disableStandbyTimer()")); + LOG(standby_log, s_info, F("disableStandbyTimer")); if (settings.standbyTimer != 0) { standbyTimer.stop(); LOG(standby_log, s_info, F("timer stopped")); } } -void Tonuino::checkStandbyAtMillis() { +void Tonuino::checkStandby() { if (standbyTimer.isActive() && standbyTimer.isExpired()) { LOG(standby_log, s_info, F("power off!")); // enter sleep state @@ -209,20 +211,20 @@ bool Tonuino::specialCard(const nfcTagObject &nfcTag) { switch (nfcTag.nfcFolderSettings.mode) { case mode_t::sleep_timer: LOG(card_log, s_info, F("act. sleepTimer")); - mp3.playAdvertisement(advertTracks::t_302_sleep, false)/*olnyIfIsPlaying*/; + mp3.playAdvertisement(advertTracks::t_302_sleep , false/*olnyIfIsPlaying*/); activeModifier = &sleepTimer; sleepTimer.start(nfcTag.nfcFolderSettings.special) ;break; case mode_t::freeze_dance: LOG(card_log, s_info, F("act. freezeDance")); - mp3.playAdvertisement(advertTracks::t_300_freeze_into, false/*olnyIfIsPlaying*/); + mp3.playAdvertisement(advertTracks::t_300_freeze_into , false/*olnyIfIsPlaying*/); activeModifier = &freezeDance; ;break; case mode_t::locked: LOG(card_log, s_info, F("act. locked")); - mp3.playAdvertisement(advertTracks::t_303_locked, false/*olnyIfIsPlaying*/); + mp3.playAdvertisement(advertTracks::t_303_locked , false/*olnyIfIsPlaying*/); activeModifier = &locked ;break; case mode_t::toddler: LOG(card_log, s_info, F("act. toddlerMode")); - mp3.playAdvertisement(advertTracks::t_304_buttonslocked, false/*olnyIfIsPlaying*/); + mp3.playAdvertisement(advertTracks::t_304_buttonslocked , false/*olnyIfIsPlaying*/); activeModifier = &toddlerMode ;break; case mode_t::kindergarden: LOG(card_log, s_info, F("act. kindergardenMode")); - mp3.playAdvertisement(advertTracks::t_305_kindergarden, false/*olnyIfIsPlaying*/); + mp3.playAdvertisement(advertTracks::t_305_kindergarden , false/*olnyIfIsPlaying*/); activeModifier = &kindergardenMode ;break; case mode_t::repeat_single:LOG(card_log, s_info, F("act. repeatSingleModifier")); mp3.playAdvertisement(advertTracks::t_260_activate_mod_card, false/*olnyIfIsPlaying*/); diff --git a/src/tonuino.hpp b/src/tonuino.hpp index bbb393fe..2c448727 100644 --- a/src/tonuino.hpp +++ b/src/tonuino.hpp @@ -12,11 +12,11 @@ class Tonuino { Tonuino() {} static Tonuino& getTonuino() { static Tonuino tonuino; return tonuino; } - void setup (); - void loop (); + void setup (); + void loop (); - void playFolder (); - void playTrackNumber (); + void playFolder (); + void playTrackNumber(); void nextTrack(bool fromOnPlayFinished = false); void previousTrack(); @@ -25,7 +25,7 @@ class Tonuino { Modifier& getActiveModifier() { return *activeModifier; } void setStandbyTimer(); - void disableStandbyTimer (); + void disableStandbyTimer(); void setCard (const nfcTagObject &newCard) { myCard = newCard; setFolder(&myCard.nfcFolderSettings); } const nfcTagObject& getCard() const { return myCard; } @@ -38,7 +38,7 @@ class Tonuino { private: - void checkStandbyAtMillis(); + void checkStandby(); bool specialCard(const nfcTagObject &nfcTag); @@ -58,13 +58,13 @@ class Tonuino { RepeatSingleModifier repeatSingleModifier{*this, mp3, settings}; //FeedbackModifier feedbackModifier {*this, mp3, settings}; - Modifier* activeModifier = &noneModifier; + Modifier* activeModifier {&noneModifier}; - Timer standbyTimer{}; + Timer standbyTimer {}; - nfcTagObject myCard{}; - folderSettings* myFolder = &myCard.nfcFolderSettings; - uint16_t numTracksInFolder{}; + nfcTagObject myCard {}; + folderSettings* myFolder {&myCard.nfcFolderSettings}; + uint16_t numTracksInFolder {}; }; #endif /* SRC_TONUINO_HPP_ */ diff --git a/src/type_traits.hpp b/src/type_traits.hpp index 466856c4..7b733364 100644 --- a/src/type_traits.hpp +++ b/src/type_traits.hpp @@ -11,7 +11,10 @@ template struct bool_ { enum { value = b }; typedef bool_ result template struct is_same_type : bool_ {}; template struct is_same_type : bool_ {}; - +template +struct conditional { typedef T type; }; +template +struct conditional { typedef F type; }; #endif /* SRC_TYPE_TRAITS_HPP_ */ From ce09b1cea9699abcf09b2f65c4a839017550f7fb Mon Sep 17 00:00:00 2001 From: boerge1 Date: Tue, 8 Mar 2022 18:13:07 +0100 Subject: [PATCH 23/32] Normalize mp3 --- sd-card/mp3/0001.mp3 | Bin 3650 -> 3856 bytes sd-card/mp3/0002.mp3 | Bin 3493 -> 3699 bytes sd-card/mp3/0003.mp3 | Bin 3179 -> 3385 bytes sd-card/mp3/0004.mp3 | Bin 3336 -> 3542 bytes sd-card/mp3/0005.mp3 | Bin 3963 -> 4169 bytes sd-card/mp3/0006.mp3 | Bin 3806 -> 4012 bytes sd-card/mp3/0007.mp3 | Bin 3963 -> 4169 bytes sd-card/mp3/0008.mp3 | Bin 3023 -> 3197 bytes sd-card/mp3/0009.mp3 | Bin 3963 -> 4169 bytes sd-card/mp3/0010.mp3 | Bin 3336 -> 3542 bytes sd-card/mp3/0011.mp3 | Bin 3179 -> 3385 bytes sd-card/mp3/0012.mp3 | Bin 3963 -> 4169 bytes sd-card/mp3/0013.mp3 | Bin 4747 -> 4953 bytes sd-card/mp3/0014.mp3 | Bin 4277 -> 4483 bytes sd-card/mp3/0015.mp3 | Bin 5060 -> 5266 bytes sd-card/mp3/0016.mp3 | Bin 4590 -> 4796 bytes sd-card/mp3/0017.mp3 | Bin 4904 -> 5110 bytes sd-card/mp3/0018.mp3 | Bin 4277 -> 4483 bytes sd-card/mp3/0019.mp3 | Bin 4747 -> 4953 bytes sd-card/mp3/0020.mp3 | Bin 4433 -> 4639 bytes sd-card/mp3/0021.mp3 | Bin 6157 -> 6363 bytes sd-card/mp3/0022.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0023.mp3 | Bin 6314 -> 6520 bytes sd-card/mp3/0024.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0025.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0026.mp3 | Bin 6941 -> 7147 bytes sd-card/mp3/0027.mp3 | Bin 6941 -> 7147 bytes sd-card/mp3/0028.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0029.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0030.mp3 | Bin 3963 -> 4169 bytes sd-card/mp3/0031.mp3 | Bin 5844 -> 6050 bytes sd-card/mp3/0032.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0033.mp3 | Bin 6314 -> 6520 bytes sd-card/mp3/0034.mp3 | Bin 6471 -> 6677 bytes sd-card/mp3/0035.mp3 | Bin 6471 -> 6677 bytes sd-card/mp3/0036.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0037.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0038.mp3 | Bin 6471 -> 6677 bytes sd-card/mp3/0039.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0040.mp3 | Bin 4120 -> 4326 bytes sd-card/mp3/0041.mp3 | Bin 6001 -> 6207 bytes sd-card/mp3/0042.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0043.mp3 | Bin 6314 -> 6520 bytes sd-card/mp3/0044.mp3 | Bin 6471 -> 6677 bytes sd-card/mp3/0045.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0046.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0047.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0048.mp3 | Bin 6001 -> 6207 bytes sd-card/mp3/0049.mp3 | Bin 6471 -> 6677 bytes sd-card/mp3/0050.mp3 | Bin 4590 -> 4764 bytes sd-card/mp3/0051.mp3 | Bin 6471 -> 6677 bytes sd-card/mp3/0052.mp3 | Bin 6941 -> 7147 bytes sd-card/mp3/0053.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0054.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0055.mp3 | Bin 6941 -> 7147 bytes sd-card/mp3/0056.mp3 | Bin 7098 -> 7304 bytes sd-card/mp3/0057.mp3 | Bin 6941 -> 7147 bytes sd-card/mp3/0058.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0059.mp3 | Bin 6941 -> 7147 bytes sd-card/mp3/0060.mp3 | Bin 4277 -> 4451 bytes sd-card/mp3/0061.mp3 | Bin 6001 -> 6207 bytes sd-card/mp3/0062.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0063.mp3 | Bin 6314 -> 6520 bytes sd-card/mp3/0064.mp3 | Bin 6314 -> 6520 bytes sd-card/mp3/0065.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0066.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0067.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0068.mp3 | Bin 6314 -> 6520 bytes sd-card/mp3/0069.mp3 | Bin 6471 -> 6677 bytes sd-card/mp3/0070.mp3 | Bin 4433 -> 4639 bytes sd-card/mp3/0071.mp3 | Bin 5844 -> 6050 bytes sd-card/mp3/0072.mp3 | Bin 6471 -> 6677 bytes sd-card/mp3/0073.mp3 | Bin 6157 -> 6363 bytes sd-card/mp3/0074.mp3 | Bin 6314 -> 6520 bytes sd-card/mp3/0075.mp3 | Bin 6471 -> 6677 bytes sd-card/mp3/0076.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0077.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0078.mp3 | Bin 6471 -> 6677 bytes sd-card/mp3/0079.mp3 | Bin 6314 -> 6520 bytes sd-card/mp3/0080.mp3 | Bin 4120 -> 4294 bytes sd-card/mp3/0081.mp3 | Bin 6001 -> 6207 bytes sd-card/mp3/0082.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0083.mp3 | Bin 6157 -> 6363 bytes sd-card/mp3/0084.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0085.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0086.mp3 | Bin 6941 -> 7147 bytes sd-card/mp3/0087.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0088.mp3 | Bin 6157 -> 6331 bytes sd-card/mp3/0089.mp3 | Bin 6314 -> 6520 bytes sd-card/mp3/0090.mp3 | Bin 4277 -> 4483 bytes sd-card/mp3/0091.mp3 | Bin 6471 -> 6677 bytes sd-card/mp3/0092.mp3 | Bin 7255 -> 7461 bytes sd-card/mp3/0093.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0094.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0095.mp3 | Bin 7255 -> 7461 bytes sd-card/mp3/0096.mp3 | Bin 7255 -> 7461 bytes sd-card/mp3/0097.mp3 | Bin 7098 -> 7304 bytes sd-card/mp3/0098.mp3 | Bin 6941 -> 7147 bytes sd-card/mp3/0099.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0100.mp3 | Bin 4904 -> 5110 bytes sd-card/mp3/0101.mp3 | Bin 6314 -> 6520 bytes sd-card/mp3/0102.mp3 | Bin 6001 -> 6207 bytes sd-card/mp3/0103.mp3 | Bin 5374 -> 5580 bytes sd-card/mp3/0104.mp3 | Bin 6001 -> 6207 bytes sd-card/mp3/0105.mp3 | Bin 6471 -> 6677 bytes sd-card/mp3/0106.mp3 | Bin 5844 -> 6050 bytes sd-card/mp3/0107.mp3 | Bin 6314 -> 6520 bytes sd-card/mp3/0108.mp3 | Bin 5844 -> 6050 bytes sd-card/mp3/0109.mp3 | Bin 6314 -> 6520 bytes sd-card/mp3/0110.mp3 | Bin 6157 -> 6363 bytes sd-card/mp3/0111.mp3 | Bin 6157 -> 6363 bytes sd-card/mp3/0112.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0113.mp3 | Bin 7411 -> 7617 bytes sd-card/mp3/0114.mp3 | Bin 7255 -> 7461 bytes sd-card/mp3/0115.mp3 | Bin 7725 -> 7931 bytes sd-card/mp3/0116.mp3 | Bin 7568 -> 7774 bytes sd-card/mp3/0117.mp3 | Bin 7411 -> 7617 bytes sd-card/mp3/0118.mp3 | Bin 7255 -> 7461 bytes sd-card/mp3/0119.mp3 | Bin 7568 -> 7774 bytes sd-card/mp3/0120.mp3 | Bin 7411 -> 7617 bytes sd-card/mp3/0121.mp3 | Bin 8822 -> 9028 bytes sd-card/mp3/0122.mp3 | Bin 9449 -> 9655 bytes sd-card/mp3/0123.mp3 | Bin 9135 -> 9341 bytes sd-card/mp3/0124.mp3 | Bin 9135 -> 9341 bytes sd-card/mp3/0125.mp3 | Bin 9449 -> 9655 bytes sd-card/mp3/0126.mp3 | Bin 9449 -> 9655 bytes sd-card/mp3/0127.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0128.mp3 | Bin 9606 -> 9812 bytes sd-card/mp3/0129.mp3 | Bin 9449 -> 9655 bytes sd-card/mp3/0130.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0131.mp3 | Bin 8665 -> 8871 bytes sd-card/mp3/0132.mp3 | Bin 9292 -> 9498 bytes sd-card/mp3/0133.mp3 | Bin 8979 -> 9185 bytes sd-card/mp3/0134.mp3 | Bin 9135 -> 9341 bytes sd-card/mp3/0135.mp3 | Bin 9292 -> 9498 bytes sd-card/mp3/0136.mp3 | Bin 9292 -> 9498 bytes sd-card/mp3/0137.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0138.mp3 | Bin 9292 -> 9498 bytes sd-card/mp3/0139.mp3 | Bin 9292 -> 9498 bytes sd-card/mp3/0140.mp3 | Bin 6941 -> 7147 bytes sd-card/mp3/0141.mp3 | Bin 9292 -> 9498 bytes sd-card/mp3/0142.mp3 | Bin 9292 -> 9498 bytes sd-card/mp3/0143.mp3 | Bin 9135 -> 9341 bytes sd-card/mp3/0144.mp3 | Bin 9292 -> 9498 bytes sd-card/mp3/0145.mp3 | Bin 8979 -> 9185 bytes sd-card/mp3/0146.mp3 | Bin 9449 -> 9655 bytes sd-card/mp3/0147.mp3 | Bin 9292 -> 9498 bytes sd-card/mp3/0148.mp3 | Bin 9135 -> 9341 bytes sd-card/mp3/0149.mp3 | Bin 9606 -> 9812 bytes sd-card/mp3/0150.mp3 | Bin 7255 -> 7461 bytes sd-card/mp3/0151.mp3 | Bin 9135 -> 9341 bytes sd-card/mp3/0152.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0153.mp3 | Bin 9449 -> 9655 bytes sd-card/mp3/0154.mp3 | Bin 9449 -> 9655 bytes sd-card/mp3/0155.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0156.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0157.mp3 | Bin 10076 -> 10282 bytes sd-card/mp3/0158.mp3 | Bin 9606 -> 9812 bytes sd-card/mp3/0159.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0160.mp3 | Bin 7098 -> 7304 bytes sd-card/mp3/0161.mp3 | Bin 8979 -> 9185 bytes sd-card/mp3/0162.mp3 | Bin 9135 -> 9341 bytes sd-card/mp3/0163.mp3 | Bin 9135 -> 9341 bytes sd-card/mp3/0164.mp3 | Bin 9135 -> 9341 bytes sd-card/mp3/0165.mp3 | Bin 9449 -> 9655 bytes sd-card/mp3/0166.mp3 | Bin 9449 -> 9655 bytes sd-card/mp3/0167.mp3 | Bin 9606 -> 9812 bytes sd-card/mp3/0168.mp3 | Bin 9135 -> 9341 bytes sd-card/mp3/0169.mp3 | Bin 9449 -> 9655 bytes sd-card/mp3/0170.mp3 | Bin 7098 -> 7304 bytes sd-card/mp3/0171.mp3 | Bin 8822 -> 9028 bytes sd-card/mp3/0172.mp3 | Bin 9135 -> 9341 bytes sd-card/mp3/0173.mp3 | Bin 8979 -> 9185 bytes sd-card/mp3/0174.mp3 | Bin 9135 -> 9341 bytes sd-card/mp3/0175.mp3 | Bin 9292 -> 9498 bytes sd-card/mp3/0176.mp3 | Bin 9292 -> 9498 bytes sd-card/mp3/0177.mp3 | Bin 9449 -> 9655 bytes sd-card/mp3/0178.mp3 | Bin 9292 -> 9498 bytes sd-card/mp3/0179.mp3 | Bin 9292 -> 9498 bytes sd-card/mp3/0180.mp3 | Bin 6941 -> 7147 bytes sd-card/mp3/0181.mp3 | Bin 9135 -> 9341 bytes sd-card/mp3/0182.mp3 | Bin 9292 -> 9498 bytes sd-card/mp3/0183.mp3 | Bin 9135 -> 9341 bytes sd-card/mp3/0184.mp3 | Bin 9292 -> 9498 bytes sd-card/mp3/0185.mp3 | Bin 9135 -> 9341 bytes sd-card/mp3/0186.mp3 | Bin 9135 -> 9341 bytes sd-card/mp3/0187.mp3 | Bin 9606 -> 9812 bytes sd-card/mp3/0188.mp3 | Bin 9135 -> 9341 bytes sd-card/mp3/0189.mp3 | Bin 9449 -> 9655 bytes sd-card/mp3/0190.mp3 | Bin 7255 -> 7461 bytes sd-card/mp3/0191.mp3 | Bin 9606 -> 9812 bytes sd-card/mp3/0192.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0193.mp3 | Bin 9919 -> 10125 bytes sd-card/mp3/0194.mp3 | Bin 9606 -> 9812 bytes sd-card/mp3/0195.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0196.mp3 | Bin 9919 -> 10125 bytes sd-card/mp3/0197.mp3 | Bin 10233 -> 10439 bytes sd-card/mp3/0198.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0199.mp3 | Bin 9919 -> 10125 bytes sd-card/mp3/0200.mp3 | Bin 5374 -> 5580 bytes sd-card/mp3/0201.mp3 | Bin 6941 -> 7147 bytes sd-card/mp3/0202.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0203.mp3 | Bin 6001 -> 6207 bytes sd-card/mp3/0204.mp3 | Bin 6628 -> 6834 bytes sd-card/mp3/0205.mp3 | Bin 6941 -> 7147 bytes sd-card/mp3/0206.mp3 | Bin 6471 -> 6677 bytes sd-card/mp3/0207.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0208.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0209.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0210.mp3 | Bin 7098 -> 7304 bytes sd-card/mp3/0211.mp3 | Bin 6784 -> 6990 bytes sd-card/mp3/0212.mp3 | Bin 7411 -> 7617 bytes sd-card/mp3/0213.mp3 | Bin 8352 -> 8558 bytes sd-card/mp3/0214.mp3 | Bin 7881 -> 8087 bytes sd-card/mp3/0215.mp3 | Bin 8195 -> 8401 bytes sd-card/mp3/0216.mp3 | Bin 8352 -> 8558 bytes sd-card/mp3/0217.mp3 | Bin 8038 -> 8244 bytes sd-card/mp3/0218.mp3 | Bin 8195 -> 8401 bytes sd-card/mp3/0219.mp3 | Bin 8195 -> 8401 bytes sd-card/mp3/0220.mp3 | Bin 7725 -> 7931 bytes sd-card/mp3/0221.mp3 | Bin 9606 -> 9812 bytes sd-card/mp3/0222.mp3 | Bin 10076 -> 10282 bytes sd-card/mp3/0223.mp3 | Bin 9919 -> 10125 bytes sd-card/mp3/0224.mp3 | Bin 9606 -> 9812 bytes sd-card/mp3/0225.mp3 | Bin 10076 -> 10282 bytes sd-card/mp3/0226.mp3 | Bin 10233 -> 10439 bytes sd-card/mp3/0227.mp3 | Bin 10233 -> 10439 bytes sd-card/mp3/0228.mp3 | Bin 10076 -> 10282 bytes sd-card/mp3/0229.mp3 | Bin 10233 -> 10439 bytes sd-card/mp3/0230.mp3 | Bin 7411 -> 7617 bytes sd-card/mp3/0231.mp3 | Bin 9606 -> 9812 bytes sd-card/mp3/0232.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0233.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0234.mp3 | Bin 9606 -> 9812 bytes sd-card/mp3/0235.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0236.mp3 | Bin 10076 -> 10282 bytes sd-card/mp3/0237.mp3 | Bin 10546 -> 10752 bytes sd-card/mp3/0238.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0239.mp3 | Bin 10076 -> 10282 bytes sd-card/mp3/0240.mp3 | Bin 7568 -> 7774 bytes sd-card/mp3/0241.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0242.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0243.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0244.mp3 | Bin 9449 -> 9655 bytes sd-card/mp3/0245.mp3 | Bin 9606 -> 9812 bytes sd-card/mp3/0246.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0247.mp3 | Bin 9919 -> 10125 bytes sd-card/mp3/0248.mp3 | Bin 9919 -> 10125 bytes sd-card/mp3/0249.mp3 | Bin 10076 -> 10282 bytes sd-card/mp3/0250.mp3 | Bin 7881 -> 8087 bytes sd-card/mp3/0251.mp3 | Bin 9919 -> 10125 bytes sd-card/mp3/0252.mp3 | Bin 10233 -> 10439 bytes sd-card/mp3/0253.mp3 | Bin 10233 -> 10439 bytes sd-card/mp3/0254.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0255.mp3 | Bin 10546 -> 10752 bytes sd-card/mp3/0262.mp3 | Bin 6407 -> 6613 bytes sd-card/mp3/0300_new_tag.mp3 | Bin 9449 -> 9655 bytes sd-card/mp3/0301_select_folder.mp3 | Bin 40482 -> 40688 bytes sd-card/mp3/0310.mp3 | Bin 25279 -> 25485 bytes sd-card/mp3/0311_mode_random_episode.mp3 | Bin 23241 -> 23447 bytes sd-card/mp3/0312_mode_album.mp3 | Bin 17129 -> 17335 bytes sd-card/mp3/0313_mode_party.mp3 | Bin 16032 -> 16238 bytes sd-card/mp3/0314_mode_single_track.mp3 | Bin 21047 -> 21253 bytes sd-card/mp3/0315_mode_audio_book.mp3 | Bin 24339 -> 24545 bytes sd-card/mp3/0316_admin.mp3 | Bin 7098 -> 7304 bytes sd-card/mp3/0317_special_random.mp3 | Bin 36250 -> 36456 bytes sd-card/mp3/0318_special_album.mp3 | Bin 32959 -> 33165 bytes sd-card/mp3/0319_special_party.mp3 | Bin 36250 -> 36456 bytes sd-card/mp3/0320_mode_audio_book_single.mp3 | Bin 47496 -> 47702 bytes sd-card/mp3/0321_mode_repeat_last_card.mp3 | Bin 0 -> 32068 bytes sd-card/mp3/0327_select_file.mp3 | Bin 22458 -> 22664 bytes sd-card/mp3/0328_select_first_file.mp3 | Bin 24809 -> 25015 bytes sd-card/mp3/0329_select_last_file.mp3 | Bin 19637 -> 19843 bytes sd-card/mp3/0330.mp3 | Bin 45498 -> 45704 bytes sd-card/mp3/0331.mp3 | Bin 11800 -> 12006 bytes sd-card/mp3/0332.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0400_ok.mp3 | Bin 16345 -> 16551 bytes sd-card/mp3/0401_error.mp3 | Bin 14778 -> 14984 bytes sd-card/mp3/0402_ok_settings.mp3 | Bin 18069 -> 18275 bytes sd-card/mp3/0800_waiting_for_card.mp3 | Bin 10233 -> 10439 bytes sd-card/mp3/0802_reset_aborted.mp3 | Bin 15405 -> 15611 bytes sd-card/mp3/0900_admin.mp3 | Bin 63836 -> 64042 bytes sd-card/mp3/0901_card_reset.mp3 | Bin 11016 -> 11222 bytes sd-card/mp3/0902_max_volume.mp3 | Bin 12427 -> 12633 bytes sd-card/mp3/0903_min_volume.mp3 | Bin 11330 -> 11536 bytes sd-card/mp3/0904_init_volume.mp3 | Bin 12427 -> 12633 bytes sd-card/mp3/0905_eq.mp3 | Bin 8195 -> 8401 bytes sd-card/mp3/0906_modifiers.mp3 | Bin 48946 -> 49152 bytes sd-card/mp3/0907_shortcut.mp3 | Bin 53648 -> 53854 bytes sd-card/mp3/0908_standbytimer.mp3 | Bin 9292 -> 9498 bytes sd-card/mp3/0909_batch_cards.mp3 | Bin 13837 -> 14043 bytes sd-card/mp3/0910_switch_volume.mp3 | Bin 15405 -> 15611 bytes sd-card/mp3/0911_reset.mp3 | Bin 8665 -> 8871 bytes sd-card/mp3/0912_admin_lock.mp3 | Bin 9762 -> 9968 bytes sd-card/mp3/0913_pause_on_card_removed.mp3 | Bin 12584 -> 12790 bytes sd-card/mp3/0919_continue_admin.mp3 | Bin 10076 -> 10282 bytes sd-card/mp3/0920_eq_intro.mp3 | Bin 33586 -> 33792 bytes sd-card/mp3/0921_normal.mp3 | Bin 3806 -> 4012 bytes sd-card/mp3/0922_pop.mp3 | Bin 2396 -> 2602 bytes sd-card/mp3/0923_rock.mp3 | Bin 2709 -> 2915 bytes sd-card/mp3/0924_jazz.mp3 | Bin 3493 -> 3699 bytes sd-card/mp3/0925_classic.mp3 | Bin 3650 -> 3824 bytes sd-card/mp3/0926_bass.mp3 | Bin 2709 -> 2883 bytes sd-card/mp3/0930_max_volume_intro.mp3 | Bin 22301 -> 22507 bytes sd-card/mp3/0931_min_volume_into.mp3 | Bin 21047 -> 21253 bytes sd-card/mp3/0932_init_volume_into.mp3 | Bin 21674 -> 21880 bytes sd-card/mp3/0933_switch_volume_intro.mp3 | Bin 41736 -> 41942 bytes sd-card/mp3/0934_no.mp3 | Bin 3023 -> 3229 bytes sd-card/mp3/0935_yes.mp3 | Bin 2396 -> 2602 bytes sd-card/mp3/0936_batch_cards_intro.mp3 | Bin 68068 -> 68274 bytes sd-card/mp3/0940_shortcut_into.mp3 | Bin 24182 -> 24388 bytes sd-card/mp3/0941_pause.mp3 | Bin 5530 -> 5736 bytes sd-card/mp3/0942_up.mp3 | Bin 11643 -> 11849 bytes sd-card/mp3/0943_down.mp3 | Bin 12584 -> 12790 bytes sd-card/mp3/0944_startup.mp3 | Bin 11643 -> 11849 bytes sd-card/mp3/0960_timer_intro.mp3 | Bin 20420 -> 20626 bytes sd-card/mp3/0961_timer_5.mp3 | Bin 6157 -> 6363 bytes sd-card/mp3/0962_timer_15.mp3 | Bin 7725 -> 7931 bytes sd-card/mp3/0963_timer_30.mp3 | Bin 6941 -> 7147 bytes sd-card/mp3/0964_timer_60.mp3 | Bin 7411 -> 7617 bytes sd-card/mp3/0965_timer_disabled.mp3 | Bin 10076 -> 10282 bytes sd-card/mp3/0970_modifier_Intro.mp3 | Bin 23868 -> 24074 bytes sd-card/mp3/0971_modifier_SleepTimer.mp3 | Bin 5844 -> 6050 bytes sd-card/mp3/0972_modifier_FreezeDance.mp3 | Bin 32959 -> 33165 bytes sd-card/mp3/0973_modifier_Locked.mp3 | Bin 7881 -> 8087 bytes sd-card/mp3/0974_modifier_Toddler.mp3 | Bin 40482 -> 40688 bytes sd-card/mp3/0975_modifier_KinderGarden.mp3 | Bin 59604 -> 59810 bytes sd-card/mp3/0976_modifier_repeat1.mp3 | Bin 19480 -> 19686 bytes sd-card/mp3/0980_admin_lock_intro.mp3 | Bin 19637 -> 19843 bytes sd-card/mp3/0981_admin_lock_disabled.mp3 | Bin 32646 -> 32852 bytes sd-card/mp3/0982_admin_lock_card.mp3 | Bin 48946 -> 49152 bytes sd-card/mp3/0983_admin_lock_pin.mp3 | Bin 49886 -> 50092 bytes sd-card/mp3/0984_admin_lock_calc.mp3 | Bin 30608 -> 30814 bytes sd-card/mp3/0991_admin_pin.mp3 | Bin 7725 -> 7931 bytes sd-card/mp3/0992_admin_calc.mp3 | Bin 4590 -> 4796 bytes sd-card/mp3/0993_admin_calc.mp3 | Bin 3179 -> 3385 bytes sd-card/mp3/0994_admin_calc.mp3 | Bin 3493 -> 3699 bytes sd-card/mp3/0999_reset_ok.mp3 | Bin 9762 -> 9968 bytes 338 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 sd-card/mp3/0321_mode_repeat_last_card.mp3 diff --git a/sd-card/mp3/0001.mp3 b/sd-card/mp3/0001.mp3 index eb9a47615cb4c180e0b8b1018afe842396ce0432..babc3708deb862743dcecefd6418f145a4a859fa 100644 GIT binary patch delta 350 zcmX>kGeK^GDI?EBv)PQtCSIDuxM^}e;~d6?lX;ltFfN_El4&;MzR9l4a~QWyegq^} zO|EB|!#H=c4C`#hbCY+l&SC7D9LhF_aogliKr(Z3Kl^OPQlhdqSb!J^7Jx}00Tc!U-vDEGM^C?aUr#?@#|Q>PBV!#y3lnZM*-$?he+F#> z10x+U;l~4&4{{Cgaf}2T5EA6*>>UqcGUyuU`I{PmfI^BB2QH-nu8!Ue271OuCMM=) LIGo!6)yx0@PH00!_qFXzww2{!TO3&<2%Q+)*4Y?9(5xbtZ5GmY&6K{6XbFp2GKCSMfI z2XM+S{G!G=j9RG2ZHrr+3I`4$B=n82VPY&U>=uM0v)mo0MPbFzv3PRt2y^yJ!ik^U s5ocwKl+B8e@tXVKpeo#HT#GlC?*BWUu;$CNm~iY1J=Ox@%Wbgu0nbo4!~g&Q diff --git a/sd-card/mp3/0002.mp3 b/sd-card/mp3/0002.mp3 index 77180b23dd2682e92cb9dc5e5281126925894c04..ac99c73a80509f021ec6b00700bafde5a1a5bace 100644 GIT binary patch delta 344 zcmZ1~{aI#$DdUxiX0sV%CtjMvcy4k&;~d7*lX;ltFz%YXl4&;M)XA>Qa~Ri8egq`n zO|EB|!?<&@4C`#hW0QBV&S5+`Ig|~g@)M9eHMyUCHskKe+8lEjS4}LHe=4@d0cZCeI{FT&tVLle1m&7qrv17o;i#MCJXXTsdo%;4RLgLjkv(hz_5;i zfq?~xfnWic1QI}DFz^j9c6apji}&^P^L311Ff=sMF|;t}Mw1QobMa@;HZU;J0TX^a zQ28L&03XLlumK@Kj?UikASQ#Zfu6qs7$~GTao|!K;OgkjV4!DYW@K()hQqlHP|XYg DD<)c; delta 136 zcmWm4I}QO+00q$8$N0>QLTz@SrqO6DL8nnDMWd8h0+Xwdyc^vs>_9YHpGFkolPJUr z=*82V>UlXd=Bs@5PaA$0UJ(tG9v6T@ve0skeXN4V;z~anRAY9qb{pZ_z o<0Nl!V6`Bux%E2sio%>Gv>0&_3LQ2gq038T(c~}|jx3jEKPH$qlmGw# diff --git a/sd-card/mp3/0003.mp3 b/sd-card/mp3/0003.mp3 index c748eb047ef0451751d89b9d066f1c395c7a7e8f..305f95423bb0fcf9f91babfe5e786a803ad7d3a9 100644 GIT binary patch delta 340 zcmaDYu~TY-DdUZaX0sVXCtjMv_-JxI;~d6qlX;ltFm9Z@l4&;M+R3iWa~O9`egq^B zPOfK}!+2=24C`#hqmy^A&S5+?Ih1V<pF!Kez)%NF`0+sH zgIoiA93#O7gakP{d&h&AK!tk#M#iQF1_lZ#P8_(D2DmzUGZ^TZ7#JEGn&5D515`5u E04?oWPyhe` delta 132 zcmV-~0DJ$r8tWL4H38g_Hk$!ikx+Xorl<~quv)PPI6EDqSY?_?UIEQi3WFDqDjO!+^WSY&mX|gNx9LC*~9|6ft zlj~XLFm9VH!#bOB?c^P-a~Kaz4rK$0d;*f2Cik<0#I-r*FfN>Y0!YrCoXRl<~<#v)PQ(CSIDuIBjx1;~d6qlX;ltFdmw`l4&;MvB|E?a~RK0egq_s zO|EB|!+2`44C`#hgOhi#&SAVZIg||~@(D;Do7~S164&OK!?<#wnAT oxMnk!OrFOzhp}+7HTN7wkI6TwskDWN*INY&)1d+XPG} zf8^_$#Lrsq7~mS>=*?p~7{Op@ zXrNxyiLN2IacwA^*-Qzxd}@yzr3F`(O@#w@reMR4 zB@ll8PBRJ%*0O>*6YZY8oFJlME36pVf|w0Qu;jy0`g7ol?xnowRjeJd?I|lUTH~V6 Jw>Oy;%pc?vKZgJS diff --git a/sd-card/mp3/0006.mp3 b/sd-card/mp3/0006.mp3 index 5554a389ddb3582fabe3744e6c86f8ab315877e4..60ba27dbc887bccb5579927e930b6e03946fa0f5 100644 GIT binary patch delta 362 zcmca7yGDM3DdU}qX0sVfCtjMvxNdSj;~d6olX;ltFrJ^hl4&;Mp~ zO|EAF$;zvsSEtPF1lLPsBnLlhdqSb!J^7Jx}00Tc!U-vDEGM^C?aUr#?@#|Q>P zLrWb)3qx)+*-$?he+F#>0}~xE;l~4&4{{Cgaf}2T5EA6*>>Uqc0u}1{8<-mafkKKC b2QH-nu8!Ue26{#&rsfs~IGo!6)yx0@2F+tw delta 162 zcmWm2Aq)Xw7zW_I#~tp@ZMvDR5GaU}tf@9lxggj~5fpcOH@ofsCSg-#Zc`0Wk_Abj z65N$1JeT6P;8pSRYYL%%6NDT<@T9H9ZZ(A~TZ#A|kw=b`f&=rq!ijrb5U^tiR{TT; zoTn7lETsh=&ygGUGJ+8`Q(?lTDOj*>35L8|3Vph^pvzoV(BLkz%(kQSpIezW`F3XX G9ODlZB0qNk diff --git a/sd-card/mp3/0008.mp3 b/sd-card/mp3/0008.mp3 index fc6a83ed86707e5bb14200b639bc974c7aac0dd0..01965c5eff64b1743ba0d0e0e8888e76289a5514 100644 GIT binary patch delta 183 zcmX>v{#RnddG3100M`&lch`su>?-xW;{#kBy%|6% PjZDnVa5$#{s+$1-VHP6f delta 7 Ocmew>abA4Gd2Rp?q63Bi diff --git a/sd-card/mp3/0009.mp3 b/sd-card/mp3/0009.mp3 index 0acd5abaae7d40571fe06018725c97f15b228ac8..63a7f642b29bf02a8fdc8ad3d9e97ea2c3a22b02 100644 GIT binary patch delta 371 zcmew@cT!=336t!XiDt7I114UY!x%F;pK%W3uE{)1a~L}&uVk9dcx|#P^Bl&NlOF-e zxs&Tz<}l8gEWaxgc>y0=y<>oDh@-n}#07Q+hII@K3@ktl1Pj0w&2cIN^s=Y zc;_^&ab_ta*zsbF*>wb4Dpw<7x+Xorl<~ntv)POq6EDqSTsAqMaSr49$vjMR7(Y*5$uyhs(PUTVIgH09KLU~) zC)cygVcaxXhIKaM$;mrd=P+)Y9LfgL^$AGMoZQboo3UfEHpd*s&dDc$lhdqSb!J^7Jx}00Tc!U-vDEGM^C?aUr#?@#|Q>PBLf{n3kz;E*-$?he+F#>10x+U z;l~4&4{{Cgaf}2T5EA6*>>UqcGUyuU`5Rgq0D(e^69+D(0j`eT3l<~zxv)POW6EDqSTsJwNaSr45$vjMR7=KS*$uyhs)nr%ZIgIBfKLV0F zC)cygVca!YhIKaM#mPHZ=P>S@9LfgL^$AEWoZQbon{mQqZH_sN6DOYllItd?a?W9F roXo^En=xndJgzy6A(O4S=P(9LzQH|Ky}ILmb^*BQCHrFsx%>U|<1aAXoqtbg z3=Pb63@r_~(PTsYT>Kfd4Gau*z=R(UR6fWxz{fEXY(PkmqqBEBhzV4v=Wk?eYG7cX ekmAIFOKE_sqc?+ro{52hrI`T^=Qcn!GXMbl>t;3p delta 162 zcmWm4ArAp@90uUM#~tp@ZMvB*Qxrv#g!>3GLC|!C+Z+EsH?s+Z+f6Vhm}G2{B%A3J z!DjBtlb&<=Tbfky@M;Qe?<)AP3&D@JCU|nGDXiE`34GpT7aXPq2NrdOh+AE-r)vm~ ze8=uM&M1VeWCa0Fu@O5t!IYY*u%>S&ziuVoEQJmGwqVXeUeIF@+hEI4dZ-O9E?>@w HGspM?OIATP diff --git a/sd-card/mp3/0013.mp3 b/sd-card/mp3/0013.mp3 index aa3537f7ebca623d41a2d887dc01519845bd340b..8c82d326a8443e0e771469d86cb8bc384c0d5b0d 100644 GIT binary patch delta 400 zcmZ{ey-Px26vp3Ue&xNHHyRsis6~^jx2{FgTU|`-UN5F#EjHNP(i#>t^+Cy@={`K02-B0j{69FOl5Fj|ja*)8qU&&8A4+-dEJWTL|uaa%t8SZ8JA_8vl zDnii0C57MxJBoly+*1ioQ5zvR!WaZ97XoVvDg zhcI@GO)XgGu2te$tMVRVti>4f%aUq(l#p#twrga0&Z*it$L1BLXEKS@f~owQmYvlC zi|cwi;lWY+^P=Uh^Gz?HROBo9YR|G+Jy|ePy3}NCIXr09wRoQCNyA*sq|E{E9`ujd EA5a8tk^lez delta 192 zcmW;EuWJHv9L8~;(>s6NPS1SBpv9_|S%rZt4MFUJ&0@D`P=m^&>HC>j7j9ueW-*wC zVZm_Bq^t;w`VZ`@<;6R_54llxQp9OmQh1*31wPvltZ5s9fNzGiNMAM;Vcm0#L`&DiZ}#1%fchznMHObEXDlTdhN#}>@!J_zP~ mzgu+_`aC;=7JErSjhU37z)ecQW5X4*Y(KsDM0&`jYyATpSxN^0 diff --git a/sd-card/mp3/0014.mp3 b/sd-card/mp3/0014.mp3 index bf717d475b5fd9622285c050ec6c02d361a62adc..d588a104e29e71c2e724f5bfc2de2b84161c73d8 100644 GIT binary patch delta 374 zcmdn0*sMIkl#yYg*=)wNiI?UuwoJ}voWr4KBp?Rz%c``dMaDK--X!Tbp}=_V?= zD0!97p7czrTQ8`?2^$K-@F55|fM7$%5Zt&l6b@{s1be=;D~{8Gh!s5PA80h=R39kV8|8yA3v{9k+XkXozZf zP(xTlQ$(~BO$|*gL1T++_dT2f|8RaB&d2|p$C=~V)&hLC7bWbp-wB$y4+OUu@(`Tk zrALB|`Vhetekpdb;U#F{q));Hc6|hQSRN*L!C%EQJn~Ce#aMt~51$o#SQ{buK~GS^ zE1m=i?r}wP{691a*H{k`JYjUysoRP-XoMwn@H^~WX^uJnOh*U~@gX9ij@waE{yoJw zcE$-TOio}!Sb9lh^~#%{F`F^wRiSk1QbCnn<*js96!JQFT%_#}kEAk}FN^t_22-TDmY8Po#NzDZ>Z6d(dBI Ee^l_lOgaS(UbH_}U4jn{x5AxMx8TH_$LxQ26t--71y_cAX1>twSoA9h-+ptY c6EOe8f`T6RL4_78A<_L`ZJOh-pv+W+e?GEJZU6uP diff --git a/sd-card/mp3/0016.mp3 b/sd-card/mp3/0016.mp3 index 17a6e459f7fe22950bdd849774916bc9a0ecbafd..8469046afed913263a7d29bed3dc7cb497ab26ef 100644 GIT binary patch delta 393 zcmaE-yhn9{DdU}qX0sXhO}sRR@zCUa#yO17C-X4PVZ1kaCDUxiO_N=j=P+KH{0K;% zn_SN_hw=Pm8P?g1+a~W|ox@l)Ih1VL1jX&Nqj# zWO55X$Ye!;nk0B1ja~+`BXB`P1EC6PTF^f|+=2rZGjZ z2|j`6p77h*31?sW7VRpYPEFzHc!CqV5PX<41TQWOg$)})!HqAi$6-itW!_ZSacc?& zY=s3De_D@|h{A*AsNc`pH9IlEfks^E=Xu=U>j}Y%_k_ZQy`*5qTuLzIQfsrBRv7c0 a7W6sF_|Ju`pu$5|nN~oD?c6x%y6ztc8%Zeu diff --git a/sd-card/mp3/0017.mp3 b/sd-card/mp3/0017.mp3 index 27bd9b14d1292f5d405896d61b88e73dab073198..92ccc9e1ec7eccf69988149271d0da90ac2a576a 100644 GIT binary patch delta 406 zcmZ{eJxD@f6o$WJntwM_=Z0Ep(B$*yhEjvKx`>#15d~{;vALxsD3s`jmP!r;@tvZY zD#)NI8f*wEnj#vSTN;CcTKl{>1-@_|4(C1R<(v-xjO?oL+87hC)p#Me#04O@LB&Gw zfM*r~pQ!f`wD4W>3Tsw^101jksN%Jaz`#;3!5)4|CUM6uU>gGtf?Ir){Kk1F!5vy$ zLM9x#2%d1<-Ohh=3%I~Vh2R{0eFXQ|l-$RRN5BbwcnH>Ut)HNdLtcVaYI%@ zgW-7G{m(7srt>Voc{FGur*!;+Zp>)2WF) delta 198 zcmWm6A!q_&00m&)xtmjO>X|Jo8q8Z+u?z&QD`H*Vg2gaaL=B=l9+v$NVS~XeMo|Vv z5f=suV{usQ3o|U}Z~5Z)mwn4Tp5rIDFz5wK;hnb-X7n6k#erk+&#REI;MzLn!;vsx z+BLZ2*cB?gJ{Dg1Yb~)GHh5z0MEKy`y5mhm_@omxcIZciFJ8tD|98w_!m1~X=${Hp s4y~^&#tnwt#D#$MgwW<$Qs{7+G$^p_3wdsRxh=2OxaV`K$$ToZ2UH18#sB~S diff --git a/sd-card/mp3/0018.mp3 b/sd-card/mp3/0018.mp3 index 0fadde4bf9acb030c90aea8d0b6f7b82f4ea9c61..3f1aff6177a462a27d2f7435634a8a1a5cd7e23a 100644 GIT binary patch delta 374 zcmdn0*sMIkl<~_%v)PRMCSIDu_-t}M;~d6)lX;jxu+C--nY@E_4r9~gP_{XY_a=V=lAk8`v(IL{JXxD#4v;(nBu`IH<($K~ZZZ?s zY{ui0=W&79*4%R#dnVuDp3OLKatY5IASuW@hp}(+Iv}}evNzvs#)!$!`Q|XXOm5+y z!?lhdqSb!J^7Jx}00Tc!U-vDEGM^C?aUr#?@#|Q>P zLqi=y3kz;E*-$?he+F#>10x+U;l~4&4{{Cgaf}2T5EA6*>>Uqc0u}1{o0u8^fkKKC b2QH-nu8!Ue271Pp=0+CgIGo!6)yx0@nOSF2 delta 166 zcmWm0FAM=;90uUM$N6)24qds4GDViCnxM#vE-*pi_TH55H_@gDHfERYrr9VqO`r*) z3BD`Oo>6({&8ztNHHFB(2;S^N5YaJW*M>sKMnbd??Uuu&V8W8A(BZ)pOxaEep8ROP zoTU}+tYk!c(q7ri3idQCg)^5{{JJd&c(WB&95|va<^&6Fv>}_WLYFUB(Be2RIMOQw Ee=_buvH$=8 diff --git a/sd-card/mp3/0019.mp3 b/sd-card/mp3/0019.mp3 index 6b8d72b35c2ae65e55f95196ef4cee3dd0a0ef05..065f95dae1664b286a0eaacc337bb6c54ac18c94 100644 GIT binary patch delta 400 zcmeBHy{R_Al+kda*=)v?iI?Uu=1k6KoWnR}G7r-n#s!mCGR`pO-|*U z!+3cz6W46UZIkD5&0*X+*_wL}Ky}ILmb^* zBQCHrFsx%>U|<1aAXoqtbg3``Al3@yyL(PTsYT>Kfd z4GfHQz=R(UR6fWxz{fEXY(PkmqqBEBh{>R9pyzL5ZU6)dDNY=?lm@sudNUa48Jidz NTbkf-ZUa;^0{}rQZTA2G delta 192 zcmWN>D`-Mt00m&rb=UXnniVv-vg}}J+={cV*lmIfb`i8}$R;QLhk;E+#UhBHVJjvX z2HCJ#7Hk&l?iaq_>(|_S9(TQ4gIaGVG+2iqMl4~#p=B`SgIAdF=ssujN*FU`8+>tL z3w=KMge}kR7ryxoPP_>SJ8sgd*1ogA6N<>~Bx0oF! z*^CP(&*Pc{B(1sUFs_<>gL^jPuE`}la~Ka#7UTtqtOJwYd>|X2^UYywo7}=bhtX!T zqQD%+ipl!~Cf7R#xP~~oyGC4KXJA;zz`(!)#6YkBOacj@Fc|m-7`r=q`o;Tt`uRFW zFc?^x=ong9aHGkF`nmWsXd4(9>VOG99;kefYk-eqB-ntEAV+8Kco368*FewT(Ad<# gz(66zi369?09QwE1_M1~b3-#rV;s(HfNEv{0PmD)ZU6uP delta 172 zcmW;FuMYun7zXe?$N6!04qaiIOi>g`68s6WBH9#5;r7JuI})36GZRd>O+v9z$Y#4P zP<>DN?Ag2L**h;^OVcV|UQMCx-2`9uAo$QR1b40sg(aIY!Jbd;hQqkvz=El;=FW_A zCn37?)}A>@Dr{Lv2`)ToLw3{A%CHm`oLQo++X9~tTOnZI5geJ%L>K>W%Z)yUu3*ej NR?uKECm1lu@dw|vMPmQ} diff --git a/sd-card/mp3/0021.mp3 b/sd-card/mp3/0021.mp3 index 87e1dbb403a3b50ce7b1ce84d7210cb80141e20b..bb54f000bae133624f39815a477c4e9a260c925a 100644 GIT binary patch delta 453 zcmZ|JJxD@P6ae6ROtaKXD{X10El}dVObc3i)l0-Q5d~{;simc*G0>v1C^>ES5dBcl zgA5F!78`QXV!4NcPppsx|+~ZYFNuW zTNx`d#?11_IH)U;*Y$`c0%0{0&&DG0n3`eAaLBKWh3x;T>G<><^Kq{D>%<+6dQwZw ts`K>)siZm;%^E37`uI>_EXX-fvox>HgdKjCgmKn$IBai3k>k z4kRLxu!%@C!eX(IkO+%KVsc+&lK7JQ%gOn^^W_$z<=A?Z-it9w{lz=!y8Ol>F}=X& zVmiTrOVTdPDyAbmDefa*&2)}#x1>v)x}D5)jpKeQ9%9WS)h1ZWw2mvqUCh@p<>0E9 z^o;F#rZ)_GneOr6l`6mFb27~hPESek8p%ef+Fl!(7%NRoE9mtzt>WA-X%n-}PV<*y z85=Fmt==kW3pcGy+gNC0I)JBLQVDzQOcxmGaQLWrhUHF4$LQ!{k~mh(!45F>;3I&% zVVhG%+|1m1geVCis?}54sZc?!D@xgsxRJ23sf3j>GC~hW`t%{q`^TNO#?vCGY2m&K zEZ?s`Y1$LUOr>CI(ilr-olylt>4BK0)H63)`(M;Hjik^*kwIM_(*OBx;rCel0AGKP ASpWb4 delta 263 zcmWm6F-XE;5Cw4FSEi|%W?Di^ThP?-cWG&HYH4X{Z)s_1YSBTQf|S<~+!2uxBoR~~ zQs5F~OAZbqNJ~pY)F6SY_btES?!DK|mx5}5|5{!U)OzANj)zeEG42#?-a0{pg(<}| zzsx%prxkBp2?@Gvg?wgr#(ziVBTvI(m-UF^obP6X`?HD)T~zSHi>PA6&6r}yK};-v z6!)3boZq=Od)!Wlt$imH*DTK~PPm#>RM}1nF1eTT%VV?8vjyMXNDHp`kyhOBU{UeF ja7NH%J)`JwE9?CPcfWH^mET}70`H|Lhn3O`K^<3t;2512 zf(tyca5zJ)jo=nPO%~b-E^*k(p^7(F63liGoZz?MHttFsis-Wu9O8rE6)treev6$$ z8OwIVKjAPs4Tq7i<}{K$T?B+x!8*oW91ifyMR0^$-2|`bbrU>c&CSI<3m#+Fk6;b= zdPq2xUJj@D*h_GY%Y6jrDD`uZS?MRJ;N$?o2EGZ7;`$)BHth2K2YQEg@NQ5lsJfO> zXSLk3#8{Ov)-FnD-=u(On@Z@RSv8i-Ct}HjnqzV#>{rGV$G>SdIg?^OSq}P5B%RH9 vTGQv$c{3oBR;S~6<0*`RR5U6JjVw;tT3K~XjWanA3d!N|u@>iUHb1jJdp?SE delta 252 zcmW;GElfgj6b0~|r_ZMkL=X{!g{(o`q*y2>vc*oJSV$I&b2AA$2Df`>(Yc|F2^*pW zU~W3cX0d2-eiY3Xjo!cI7w*o^xxdS2*~$vOsyRWq`luLk6N(E)J;gO^o}kUO5ycHZ z{@or`bUE(}E_vmvxjv?7^4qL%KM)*pVO&w?z1d@N!sWeCaLS|57X9ACDd^xn9C!Eq@_i4V$U8Dv3;i5s3y%bgxBvu2 zbXy2+@W{g97}Yw0621wZV%|z{h+Q@==5rgt6;9O?yx^DM8*bY<9ALmfaE-Tu>zHdG z_(Y47!!wqg1Q$5qBG|>MiwmD6x3Tdy5>&7(SjL2h!zF%r2)1yui6DnP%>++aY3A^V zGhTu_{1!aKVvC{eYc&$yw;I~{HY1_Eox>UKw;P^AJ`!%Pf+bw*;INE7KiMLd)y*HA z@b_TVzXF4(cNX)CuBMeSHS=I+tjrj*iWaqJQb4TDO6uV;C9Y+YaV@E2SYId>3`tSf zpX-!1nqmP-3I|PS?%I7))yI`-(;z*mj3lzgC?0!Lk+3XDQuo}jL9l4B*bQ1l5JV9KS+eLjGu_J&1i=Nt z7eovO1viBuSX|LC6`R4bCf5pmoXyRUu4BbWLWU;H(Hco-L4a$-QyX3IR_NV+=CZ#xI zCneUinRcJ-pyG)a<_+gFf+oK+iepxW6dNoCiY{*h!8@0;idXi`h_zvtoy@s~?VQW5 sjktyJyx^XvdDmGksI|SB4epEz_E`wkFMHdSKQ4wvcET;pabc|X4_%mQn*aa+ diff --git a/sd-card/mp3/0025.mp3 b/sd-card/mp3/0025.mp3 index 9e47a3d27566e4d5f47b2ed2d04f25bcf01215f4..d9222d734a77592823486001935cc5b06d3bf0e2 100644 GIT binary patch delta 470 zcmZ{ePbhOorO@g6W($m36b10!6Hrp!3jDo z1h2Snk#K{0ndy8m_i(P9U=3TX64vp;O0a{e3gXN+^9)yPX5MQjp58GRaiWqSkCrM4 z$C$4oc*GtD!9Bh>BzI0b2@cR*O>l`v%mRjMBs-rq1Y5XJOE8P=F0ydhf=p4+%$%QicoerG!_`riEv|rOh|jGQv6Y z!@@1w?h6;P=E-+fIALQ%XmBbQ7P>h@i_7`&$)DR}Yg9PoOu?|j$AZx2%2+U249f>a f;f`}9;fC+-HP^=t9hN49ZFbx`Yh|IvpECaeYlmsJ diff --git a/sd-card/mp3/0026.mp3 b/sd-card/mp3/0026.mp3 index 0cfd8c72ba559085ebe328dcf8f5001196d69a96..78be09a3d3a6318251e72619654a63f34b02ea9a 100644 GIT binary patch delta 484 zcmZ{eO(=w65XYZcyLQ?2{VfO1Ha*+grChvtlh&>`QMkBp;Ns%sf|8T4{ay47iBcR0 zB~hzfxF{U7DJd64E-tQSHz#FkeogcL&&Njeqh&Qh>15Q2Qg{^}Vb;yGi8_y=Yn*wQo-mlp^nx#83M*bi8))<~F>ZwC7|&x`f-B#Mx|z@P zh&Dgd8`6G5mzWMPB~enqbc_SxDPo02rrruOR~Cwx=Fw2hw18Bxk$R+rX%FAReXNyc z{Gl>KYq%|ATE}EL(=NOfMz-x#FkPU%lI6;?@DMXqMp#+RWN;!}gj2)Ri_aS4Ne6u1 z2_$Qm&|d4FwH$lE>b8gOyh|)gV=C zLVPLUzX&Q#cLZYkd^5v5I@BZ#=IbY176XXT;;7w8D1{AOK9Yu>ZNASn_0Y#H- z^N!mg#W^Qk!8>nU#V3~r6*hTdVq#Zcv3|?Wh+>aZaYd1hxY)dsP+YNRUh{CYpPxwzs(eW*PPjg% xxS*F3`*xgCJhG5hYiXIc+!_}%J)t<|rFp=`jAEJJ8F4#)*jrWiJ6^I}B_jFzze`qsS0 zN-5I?TFR96+>|lhW3t@wdn=S~yHnw0c2+t$FPdkVsZw-=%4((!9BIzL4l;G(GpIZr zLJv|ki%19EbEa(#nY~ux)+3>J%-fyH97Si~R_UXJZvO~pq-q>)OR z#!z7)5(^0yM3)Z2AQG$lzr%01Irrp#j_&5SNAMfw1qIXvU=??)94^pjBiP3WLBgdrJvFs+ zsNq37!7EPM_2#>sLjhME1W)MfAUMOa;627Vx$xhe1bevEMX-%S-2`R4?bbIgIQ5M` zf;BAm5S*aj#l^Hom+qH)^`_O$;R=u41Qnd}knkUZC0y_0u!$Zofrb??H(45}^%wJs zrlysMntir1R%VQu#SuC*C?Kv4#kF8Wi6(OKXda~6Twi4NE3}z z{M8^$I*5hAN37Dt>b>QcliZVg8kMiv{R)0-bB2dnPq^a-gePX45b@L*zPUIc^!VY8 zxIHNBax!LU@FphoS&a)#_Pr7J5{5RXhlC4uyysj?hO0{%x;#n=pPWyJ=4;w;%*~AO z&it@&%S-PIm$K&bPgXeN&WLcxshrSaD;E}5@?r7M+hu)JxaLg3{I};q$m?UFnJ5|_ fc~TVaxloeNZ{8-i#tjE7m4#JamCeN+mnZ7~92jXn diff --git a/sd-card/mp3/0029.mp3 b/sd-card/mp3/0029.mp3 index 53a6f4e59741a923413c7105a64e265005615c9b..ce70797331cda67fc1962d92639ae903528991ef 100644 GIT binary patch delta 472 zcmZ{eJxD@P6vyu|%~CV1G=qk0?SrQ;MQHJrk1$goQD7}DwX{SK1QFO^O+m^ri0&Z_ zA!tx2=yR~GT8fAUmzIdyTUxtMngaiDe_ZbQ-E+=|d=|(Bpq3AEh~!@gE^rwLa%eLU zT;qv>LmAa3f+D^PRxx8FILBTShdaD75fIKc6FlL!;6Cn}Io#kt3qcki1TS#0m4s=q zaJa`q3qb`(tpo>HvvT;vRT~N4*`}Kn!6S^>Ic(vVonQmA?F6Ujbr9@f)xqHs=bgI$ zBUr{lhd$BQ$>A6uJ4tYW##FZ5D`9gz!DP;XuolQiN zY(SFygLNcqfAwiqn^NZL4Q8g5@mN~FvLR_WDNB0#7RFi{shX<9m^3W=<=}|F!MdA& GpV==t&yGI; delta 264 zcmWm7El5Lg7{y`E)$81cb8~K9!7{5!WEP7>5Vv5l*ewiaKG`f<)h;cIHYfRF05pj3TPgj|E-fC*30X2TP-IM<^% zS?{0K2dL`=klZ&pm2(c`)X7X- zvw`G1t~rd8CR=mQVO%x&2KQ{n<&#Tz<}l8mEXX^Dv2F4?An7pKn{PJjf!8M-7$$$@ z>zc&RR__?#8sg~g8gYT0fngm30|N^X1Hl3?2_%5RVBi~I?C$947w_xo=j#~3U}$Kh zV`y%{jV2rF=i<+xZD3%c119`t6o+#gpqd!~d5&f7 delta 161 zcmW;DuMYuX90y>Z;|_P6>1Mi2Q4~p0{0Xw6>k8+yDerfJO;DJEnJJ1N+sz1Uim4J+ z@lu}l>@uHpCx^S2SD1Mx!J2glHjEj914o9!nWdm$&xh7$GxRfMDonXB1uIr9!Hw^~ z?Xbdy*@)=1)%vVO1w$IP()Mk^fCWde}U) zRt~tBj_~f1bc}g7(>;8ROb;k3-w^Xidcv27=>*G7WxKqrZn*JE%3;dKbbue_Eeg#{ z=g?aut>d|cX#umXOcoq%k^NGUN$EK6{25PgaB*PMwd;tqQM9 z`3!9~eZnoz)`WY`oOKrch7azp3oqr5YMARgLCI6E89Yc2QfpHUooM;tGIAMf5LFghlJ2) HCRtekCA(Xg diff --git a/sd-card/mp3/0032.mp3 b/sd-card/mp3/0032.mp3 index 5d279d00aeb1ab928a231bd5a8c7cbabaec653cb..dba8c5ce24a5d2d3ef3d5138283713fc32eb7443 100644 GIT binary patch delta 470 zcmZ{eF-SsD6o&6HO;ah%ogb3KoP*LA10qwdf#0Q^|3Wdyyy_ za&QSM8YH5kh9n%SrHGcMmX_A;eKZ9gxF47Q|L=cKG17|eMc}<0r4TE(8MJW&7_`uC zV9>;41BEK8T?`KJgYy&%Mh0&0hHbCz(|OyL^GEDY}Pj7-m)atZy1 z^A;=p4C)wkQ#is0x7cuffI$t-9tsUS^oX{YSM2=CS;wtG%3bdegKa$FTt>~uU=6=~ zSWq-Iqs*zfM>8P}LWq$cKKnX3xU5e?ljoHAWIi#UOei@bO-laYglzlgW|OliGA2nO zzmBB+&rhq`g0iR=WYWq^JTIlXhWlV68z BkJ|tM delta 262 zcmWlTF-U>|5QTYvnWbi$X$dXOO%1xZxCBCrOG~>;OASpeH8e)`^V;qRBnS$0PzWc5 zhLA`hhlrqphH7bPaqVvThVLHl-mrLBI4j^MED9>&Nb$-YD4v-J6!*Li1a+=YC~o*^ z?DAkz@x|qcSf&$EymNEP&7bj@r%}NpSEm(E>>KN>%_x*XOc3%Srs%O0_vV+lpwGR8 zH>79%Z_{|kjilg#-$}&>kLEnNl2Q!VO^KPUwEMAf$n$wci)$G{gM*AO++I*zF`5zFecKMsiGsDb)Dl5UG_)Ae&{lFHf_sX7 zmJAXq`W+k^B8(_%X^Enxy`{!(52wHb_v3KSx#ymP@K0n_hWB!e!*uzH;2LLuU?1%U zf+MULI2@wdLU4s2x+NpQF?N|aRPn+@!h9=11;2#LxMJq8fdLD_BHjuKC))^4(O~7m zEh|9{`)vf5_+sNCpS2Uz(cMn)j8$PBlMW6y_~{^cz$GWa8}_;g?y%35S-yuC&4+Iy2 delta 247 zcmW;GF)Tv?7zE(l)2Foc+Nats3}#a$ES4@Nvw_`Wv2-%9m@L|y?Y}e<4DBEqAtg~^ z5RouE1BqBHHCq_{v3$AYhP&U{PPH+Iujag=-b{o?Za_F?E)Xu+4h-j991`yM`R{g0 zxZ-4JxL_}oyF4s(`Rz8imo^-5W<=QKgL}laQQ?|F#@tp$xaUGvxaC{cJlAtV!gx%0 z=ZTweDKb2B5DBl`$qNHc7laq~3+B74v2e^Ex5NE$q0MU1{M%De=yQESxM8|v-X4~O X7Hef;iyvi$E74|3j4M-TR>|WJ5-Vd6 diff --git a/sd-card/mp3/0034.mp3 b/sd-card/mp3/0034.mp3 index 892167772ec04d01540510afffa7d91520773a24..b0e7d7d7e15bfa6ca1b837f58b43c7e47e391f2b 100644 GIT binary patch delta 466 zcmZ{eKS+X66vf{)%~C7PG>3+o74ctIwDeUUBIX~$KwE04rKP1IqOc*Pt>Wg=yNDDN zH8=!G6bJ@U5S2K!w6p}#)Y97hq$%jY`*C>p-1FXDpb**}g!*1sLS?T(aDvM~@Prl} z!84xfBwVA~LQugsW)*XKf(G^(BwXR8fnWn?S_w+{#k|EGql80rw-IdPJ+p?3?Zio) zNx}mbO#~M>YS#K+%#!_zMQgToXe=|&G1e*J7~eYyK5(mx;06b*#Jg3iWIt;oo_;e6 zxZh21fPTA#B0k!Q-(Bj_8jKDJXL#fwDC3w@%e*r4xYjEni%u6AKE10i>sWK;ao%Oh zDJeCrMAS^(C`4HZq36RYHOb)XrsAn!M2RNm;?YE0$q2tc?C}qW&3~pdiOHmJ`+Ol! z6A8=jd|FLSDcNR0dRm!?&1sf{-efT7WA!hLxBVAQsY*=vyn*n@P$=-vcQ=2R#SbHc BjmrQ4 delta 258 zcmWm7JxD@v6a{e3tC#gO(@c9qOLGtu<ig;K!`qYX>SRlq5EHcT)5|+do+g=Hh}Ye#I5P+!;}HIGIqpttZ6hm89DC$2{ZVsN#Y%DM5u#DYd_A nW3C~R7SvcyE9zX#xXhb*%*}B@ftgVKzv+$c7H`8{=ELA0gc)k( diff --git a/sd-card/mp3/0035.mp3 b/sd-card/mp3/0035.mp3 index 366e6b38ec2f79ca1634df201a2b22fdeedfedd3..e025dd7ed76b8a52048aa4f3deaf67710c4b4ef7 100644 GIT binary patch delta 466 zcmZ{eJuE|E6o$W3s?}Dh`srY(naFMZRV*%Uq^Y(y4M|yaV6j+OEJ(zPybSW4Ho}5L zBN9I$sdTCV5etb}EOv|4cbiTUZ*rcToO9ljw-ny(+3KZ_?LJA_?H8s?ED%!(9vjm+ zj%|`|VN@}l;!|-CE7eRVXtPT?!nK|043jlXPxw~6!KOpfF*=+~JGfU|!(1&>0XCOZ zuAGbM0)uX*EZ*Fb@>uj(E?=F+yy697Ua9&oFH;fg^-Qm5Z(w>yp+V9erhQhzAH@Ug zG&1E7@=IFBqu-j$H!)qo(QIWtY-T#dPz%!%J`|_0+$t%EmH^vLTjn8)VqgQAfNMoJ zjR}3sNIyG-$O|E=RVnS4si4MX#m(+9J(`$_M-y>9Ewo5aXDAYO|JhC^Mw6mL)4Ds$ zNO&sylwpqRQ{@K}DSae1W3}qAD;bLPYMK_D9d`bAY8rY>XkFofuomh6C+=#+EPeqQ CO^faT delta 258 zcmWm8F)Rad7zOaW zSrrBn8_t-EIdi$26FS@;6uvo@7rJca4bNOIBoF_b4ek#KXPhh=YU~t~W_4J&XR4HZ hJ}C+3oG%Mo{B%~iF=CitIh4QRRbCDmhdZo3U`E;DAvm~mGYtW7%8n2Bus_?t9l{K?d?v0-ClV<8)(BrBI*E9WCo ziiMI~qNZ3Na$SjRY?QLGv335*N_p!%ozDAy-+42U+vq}so))8u))ya{<}gZ3XYe|h z9;Fqk`!KZI!J1ylNg|qDtv}Wi`_b_B1V6HB969 ztihyVnSDmgOy4_&*bzeH>!Y=nqlSLYDPe_UMptq$(UnXXX(3yhf}!T9=Z~98cJ_z{ zDZ{}WlHOmx*R;Bg{@jATUZW#EXkT@(u_qFZNGa=v+Fk!eEz^h#*%+1~8E*QgZsvDb F`~Z?4kGuc? delta 262 zcmWm8AxuJX7zFU$Q$zs~L{K)1*<^rXv6w6ti?doR7L!E+QKxt$znjI)4Z{n#ISOv- zrjCslU{g1S!8u75162R7{L0;S7Zgi{vjV;=MT1}I2p8Ok5HR5hebzj~kn3YYi(k$r zkH&=?E=CMpHX_0gw*p4PbKW|4xS1Eq{LS+p3)yT? diff --git a/sd-card/mp3/0037.mp3 b/sd-card/mp3/0037.mp3 index 7e15c35fbdfd64d0ce6bb4f8b2408b594b7e829f..616757d7bd0ae89cca1fc34e18d23ecbc54b4c72 100644 GIT binary patch delta 470 zcmZ{eKS)AR6vpqdKU!L8Wq+WdW=*cjv!bO}Jw(jsLli0qms(m{ni|rIpWnuR*{TUN#WyVW!bkz9lQTZWnNXJ_kVw?NMjcL;m2f;{4-g^vNXi zaUS+t&|Nh@r5RJ|jP*e}rB1}N=93NbKr%F>aL)VZ#~uHT8k!nsJP;ZQ^5O75ar3oh F_6w~%j+X!c delta 262 zcmWlTKP-cB6h(7CsXiL2o_1Y#AgxH1?G#HUQtuR^n}Y2sWJ?@n0D~3 zxQ3Ndre%1`B+cQjj8&fTa;5{=DkPm?tAc3*?QW(CyeST0rczQh+#a?@?SaG(t~?2h zcb!p4@yG2CclC-| zP1EZ#2s^WWpBd>f1~Lz#eMV<+INgeRt)bVeYf9Cl9r^#QMoc3pv<6>`&#$-s6L&Uy GEPeq3NsGt; delta 258 zcmWm8AuNP(9LDiIFR$ahbMBlwZ%||fLs1mLWJMAvHd7>OgPErFf&8I zF)zXimlOgsL9r`};D6;)o+o^_haLtuhw&4Q81AE*aKl9iPfU42%ze-B$?+zk#&>7T z)n;LxJ-*?D=e|(qREu!Qx-;fR!f?yJR-w#C=K<&1gdKW;;exw?aLmzUV|__Bo=Yj= zjoEgg!b9gNC)0*!ex-#f*E)o6_GW|+US-TROlO5N{yNLt>J&;G$QdG5b80v3c?CsoeNyj;OzUSGk&7RGNwZn98W$xlE-rFV+72#GtS^f9NlMg4 zBTAGUD5R8>lHwrcii?x-{+5&S)br`-`+VQ;`@9DBn-+qW@Ap)*BqMdlG=d3YT7uKU zw1c!o(k6^NrUkqxrjf8RWl(LCw1OiW(=lTCrv9n8iFv!EEz}n#An!!yu(;UVtSp9CVlqzSVl4%?59;RJ9DQ;l8O41ZOUbboC{Ne;3-kO7Z zZz(18s4<{-8}VDa5LqFFRkf=192JzDt8g^Xt@lKR!#$C(9v7OXH8i#a9Dla4NLQbz z*R)_m4iV??zTb%U>Vvs}fquO+G;EH_=K8j_YVKo z_W0&J=UTVW;IMC~^2`_RSnLV=A7`CgQNt<6dW8yao%>wq6OQP`3}4)hg_kpN;fl|3 z!wXjuLWh}t;hM+J7UzB>BZan0mMV6vWN z4&#!^GOV*18z%2yox|ucIh1VBR07x}xi2wiq delta 169 zcmW;8FAM=;90uUMcMf+r(*?33_!DFWRa0d}rr1PoZ!zC*f<(cDo1Lf#l0r0rYJ%*e zJbND5NoHKc%_}Jc-dQkb2Z9UZhQR02P}-^~*zo?h9}+B>wiE{3S%LJ0ysL z8iX3G404MKf(U{LS{iDurM2%poC5!FKaRWq{kiW*U)qnubR)r`+ju5;z#15ITL`Z3 z#KPem)e(X-{1Cjyx|N`dGd3g*sLqMT2NAI>B-JmhcRXqC3Ij? zK#Wag^;k+tYn5zT%PJ)%B@ch64fIeZX;ufwne<*-JuhwDIag$@%z z9ZMz&x2Uxc9N|0j0=Lb?l>v){8s1n49x>BO?7x|JxNntELSGv}5g(WZTxmDvO*RP? zJhc&=NajZa}(U-q=(=d-NYykyRz?j=~lPcLq( zx|UXBTK2^%L|F)7=Eo{F$l!8ANqsb?##8xZJe5?lLWztI2O?4XziuWqGcSCK5*%(I z<@oC_X!@L*YXqbh)agXtuwq2<&nrQNHMl(0)@)SQ)Pzv{qv1$2G}h$Yoxf%A3&u)~ A&;S4c delta 262 zcmWm8F)V{|5C!nOk1B0dt6G{^%qBw{iN#{FbdiWn!eX&>vM`7d&9mJjp$3*FNJPE} zi9v)gbP|chP?N>N#{HLHUM`n+_fp-c>{jsGs2V04ZDE_M5U!btgbq(5!yRY3gnfRv z=UnfWPev1lH(n)#Hs^Ze`P+TuPSSA3@m}GC53ab}7xtr+A?86!XmKhn&#!6oT+axX z%=ZiTY`HOKvxZ}SW`#O82ZTkA<%D_OtmjQWlYrh=suGqd0|dg`^0+DwuY$TFI1$uZrmt*(yoLn5bsj!MEZm zwrZ?~!CI-hJ=HSZVy=$q4$gW>*EpEWj~L^H4-#WWe|XXwRWLBp-k~YAO!v9|i^feOEVMvdXGjlq|5JCRcr1PaB{Yfe delta 252 zcmWm8JuE|U6a{eS@jTku*%!`AuGGnY>K3RtrC8TXt+KeRhWG0tRX43i$H{NRNe# zr`De|%yB(y1x!uqW2u~yte1x;IFE78yXHro|Baf4o?<*4(X=6L;GgfV*Ou8Y)E|oP delta 258 zcmWlSAxJ}E6ooy{J*RJTZq7Gfu$WDXvshLP!(v$x>xu|27{p?cMRgoa_acHT3knXA z%^+w{8BAe`6;b+$D=SNnX)Gl0HT$GZUgM)+o=|Sp! z(h6m%r5%>29k@t|oD@=0%F)Hu`}?(%^3?n3>3QDw+xrnLgtkKPvK{6yy8TG7hVwvB zK&Oe|8FMBMSEy9zlXu|>X3PY;*ka+ZkLMPGBTQ5hlQ delta 264 zcmW;EKS)Ax6vc7QtCyu_nrSa+ac+vRrKP11S_*1u3uQ?|Dpc&$&@Wldt9z_r??3#n6y!(rRl>j{DdN}Om?g&go>~cBaIBKx6~BaKT()sB+gU}hi&w&RoUSI= zN6F6N95?L*XV~vBd~g_@8K;rg*AN_F!8B6KA%h>a1S7auM=*ySE`lXIcX2qviF$%Z zEDKAx)<94|rIAAsZyE{mIMYNTw>5LIbElc$0teiN&%zU&Z{e_kZV$mE9(uTx`$oNW zEvxEUQVnUTTN`5q#+XGs5nHAL@oh#__lMMQEFBHUqH2mMO3y+hg zuSv}LTTf{Es5)*gNG8;wNZLqQm+Xu824oTKQ-f9iwd$H0VY1I3=nV#yf6iU_9cDj# CGmbL= delta 257 zcmW;EFHAyl90c&)6GQ`*>%sKyT z26b){v{)$0q#HEq?^!;(aCfK0exX)Gzdmnh)n9}?u0rTC?g>2}d4_M6hlD4-JFmGF z5{@|?HoWp8EPQflSQzlj`OBS%x$W$T(Bz}@fYnjqgkIEe&;6)y$5JftEfzG_;z3T0 z3FmD6yO=O+a*z-z+#VO!Ig=E8-XslITuuoe{Bd^aPY4~(r45&SP77_WOv>d*#@uHk dBRp^+8`yK+a%0MH$ZSri@+@cGZitn<{|DZTX#fBK diff --git a/sd-card/mp3/0047.mp3 b/sd-card/mp3/0047.mp3 index 393b7356865247e761ac8d2252c1868139ca3d52..5ecc365f740637b7ec62b762fb3cb8d1c18eab3a 100644 GIT binary patch delta 470 zcmZ{eODKe46o$VuE;G!yj4_z9VC~X3#!OQ-e*8&u@u!(W$%c)Mjg^gASV=Lxqtthr zT%yKe6opbOl%!m;kd2KE8yg#2-#=L?r_R&qyyty8necsNJxm|jh@z?N6Vol`iRlO) z2h%eO4n}*=dey zMVH7|GZoS2W;(%(TWKzM?2Wg^zA0)RAXclWfcILa87$W^S!l0k%HX12(JCgrOxO6< zd_b;&X$L`{(ylw7oiNwPvOjqb{VmigE<~bId6&<3fg=q!HnsHbG`{&O9 zmJG{G8N=rIqf>~Y5TabSuB{R^^tx0DD>Q73B&QQ2$%HX3g0iPG7!A7rsOjX;sOXR~ z)LBB(^Q(`UR@|5<1*FD|!PvAtiZ0nb8j@itW!ubv>%UXWG-5(__e6W6(a1mVW`51$ E2dAx#*8l(j delta 262 zcmWm8F(|}w6bJCWf6j4t&Yg3|mBnl_+$|Q1$zoBG)xg4FvM?AG*H=>Sn_O<8Tp<#5 z0|OE21}QPzVzNkCz2EW~-h1C~@438I+A86D!VhdV>|-mP-3E3`!k!z1?+!WBo8!VTY&<}Rng zB-h1+6(uQ+>rG+J~_Xx8b$_N{5WDL8U$qG&Wx$oTW73v(xnWvs|p<$s6k|9}5`zT@}O&KSH^$2qjBErJhR1A;5`I|xqk z$id+p^)7-#{1n{4ij&|Lhg=+*c;h0tz+$)M{s`7_+im5e9&7$2*u>=?f@5@eIW%z3 zYjsZg2x|E1n5-(ke{Rv3&9SH?C1W-Wf7{RN=A1Tf7Zmc^Or~sI*{BrH eDHD>=@Y1xWQ`FS843pvsRaTN}hjlmGkJ%r{nu5gu delta 232 zcmWlTF-U?@0ED^UOjFZeO(z%UKx(L^r9}{&UEEz=K`pJlw|v9had+dbpF(Q~U+rCkW&2I|<^hC9#ysJUSDwK=ivi)5OUnjN zR)iPkf(A48gTjQBRpPtT5+Cw(}3%17EZN diff --git a/sd-card/mp3/0049.mp3 b/sd-card/mp3/0049.mp3 index b757949f7d172840bd4a03675c750f8753d028f4..725a66140af64c37974fc705066d591a6573ad32 100644 GIT binary patch delta 466 zcmZ{eJxD@f6o$W}nWpw*WjQp|tjYDJq14c=E+*y=qF^mH7(@guO)WJbriNE8IsU_?PgLrY6bLt9Hz(04CRfiHXyhx5MY1V+4W(DI`W zt>H(oh?|WBCG<6M(e~7&bxt-DT%p;?;SLX++T7Pdu#fM8OE}xgVFX)TWXit|J^td2 zYZ1p>);T4qrj@XoeKs>zV2o9XC$y_mK&*Ael3igX63@jV@tBfjQZVH4g=E`5HxnOB zFt;Sh9vyLe*-xp-VP!-wNT-y6Xighhr{qlp{E|?6zTZ+QnpBl2le}GWAmk5L`0jl9 GnEe4*3yZ4& delta 258 zcmW;EKP*F07zOa0$5Y;`YE|nE7KzP7uviS0NX(W_3>J|vbh22)N}bknPej5}p+hGO zFF~T>Pb7`OVzD&o$Y7GV*YeAk@0^_KRC98F8e`=dgUxbFIOGO|3X`62%9>};;Cz=* z=jXrM-NGYBe1j`q`a+9KJ;E`+tk>L27}Pl4D^&Sljk($J_dE)OTh4~A`5wAY zDJfhq(=VK}ZY^;!Wl&%{C7bRH2;Uq{3nAmQ>lZU_;l~=YGAO)qB5Um0OV(|!4GDKl l?-xW;{#kBy%`Mj RjLppq%*}B)rva*)0RUMxBGmu@ delta 7 OcmbQE`c8SnJ3#;rSObCp diff --git a/sd-card/mp3/0051.mp3 b/sd-card/mp3/0051.mp3 index 925288e027c2b177a02882efa310e7e8c651fe71..96dc7b3a6269979727d5af3459c69ca4da012416 100644 GIT binary patch delta 466 zcmZ{eJxD@f6o$WJnx$r%X$}oFD{*gX2`$~~B4Yj^3D#mmEiE(Td1}W%bPH>Dr zf)(8A(yq%64rP3HkWd%839ixThZS_W$#BbNU)S))ox>%! zDXS#ZDJ7()UyY2F8Dn~pB8qhih;}`q313JF$1>4yEUKiLEc?82P&WTlQ?ZFS^GK5H zts`b>=t(s(sZ7@wOeK}^NJcA#aUkvs1|&)9n;UCuW=*I{gh>Ow;ILnko4mW;7-oO| C4UGH% delta 258 zcmWm7KPZH87zc3Qm)G&$Id{&Tcd(dEgp|c%GFwa*i-o~L$|8e>>w8K)-)=!sTv6Vn zTpfc;`4c56i^XoS82oE!p{MjA38{QekUwO=slwX8vE<*TVCJ+)H2ZlXPwF(t} zImPuh;fVdA;f$A|aKV{&;ebERLvE)GuN>^~yG`d6=R18JM20%6k)N4J3-^3a8&0{L z5l)!z5*n;IZ#bPbZ1FoQjB}$~c;i4$cw#;0FU;nJ8h@P$D?R@EP{DA{hk{(X&@0?B kRWv+uzvw%YCAsv|S>S&Nr@=g*w_#$+1SZ_la+Gn{<@ue&beoy_N{KFhCZUTlJw}Ube(@=n3!JScQGAe z!zF1ARsz!=o|NktcQc)$*dyr@`yQvJJCSJzpUQ1adnIk5EQ#qJ7s_J{COf)Pq^ev@ zaq1g=OdELcIhj#E(<(AkndT5v&LWT|=>X4ZtmaRqJCs64?~zN91M6)?rHtDJ{j$TWiYLg}dw z_6J`OEt-N>5C!nOpZ=7#s#Ps*EM^NsYhbW+(!pX8gT-R8uvjcc3BNpp9GTEjmbI)AoXk2j4x47E=rFLg;LUF=+LNLjRq+)}A=04Xt6yF?32_ATxQoB3T zsd!{KE%r|}?YbiwMT0Nq6&Jb$7tC}k4tZo&IF?n+^D`^{aA~Ua&LFqU{#@uEPFQea diff --git a/sd-card/mp3/0053.mp3 b/sd-card/mp3/0053.mp3 index f73633dc6df673b30a3bd74e0618bd80ac38574b..afbc7f0a3ac54e9ade78fe160a137ec45ca2ce1f 100644 GIT binary patch delta 464 zcmZ{eKS)AR6vpo{O;a!CG8uX=!P4F`_XjI}Xu3NYEcL zNXRH^FIhD- zs^f5rN)N#kehA)UQLoKo1`dySXV5a)UV;n!5v<~_k;5r^Oq%m7c!sNe1QpboIqYM_ zOmK)Z7R_l{IPBntmEZs!{RD(H!E=n;xaj}3X_@T-f(%aB2@2S-bNIq#2f-Wu3f8bZ zNN|aMCxys?Ty(le2)6KAa0=DYa#2>5j2u;R zuSUjdj4{112)nI>8jQ-ZWIho~CgdFR`@>#;IAr;^%_bL8%p*wwZyQN#XP;Ko uMR}=RkV(t)@w_&&Ny(QAMI@o|l{r(lsH(_uCi#MqNJI*B`R-=tnEe5*_KSi5 delta 256 zcmWm6JxD@v6a{e3E4BRUsc8i*%}x=v*x=IS(h@|pwzRl3wFJ@BQgYtpy+o7*K}s4$ z2?;J%Lk&$42q8gRLrZJ-zx+5H4yRrCS~x1;v$`l)tG+2dSb^e!aZho{3s2DH@`&P< zL$k--QI}^!f@j`^+-5bbX!F->@h~E2FhAy+C$q`Tam6{ksG!El+xDUYLOV2OLM1Mgu&s`NZs|)Xrh)bEF!V67_@^?{K-Qkzn6wc zM1>9-iI8+Kh(#kNi=~T6A|kQ+-NhvFlK08W_x<_4Y`7fR4#USzghOiQnP3r@fZzZ< z62U#@B@Q>JRS}f1B6xurhoyH`bD>;U6P#kQhF}*z1uMAaqVHrHE9L2v_O4c3LX1`@vOjRc$6-9#{gr6vw*INMBc zh2MgAxZ6TdK*h_2%cGaz5*J!6FQ<<~8IOGgw>avz_$pY!)i&z~e>*`Ai-Id?c5q4m z!uJl$sHQfpMzxtICu0T1m_ww9Lz@CZwnH~VQFYQt>yw7A&M>8~zegGDcmL@ojfpr5 z$a1L1hT-|`Cp2?PP1y&g6Y6*@ZI!H74#wqBSeE6kxv|>+MomqPF*z7glp$r{pLesr G%j^e-?v9H9 delta 263 zcmWm8KP6a;Yhqe@%Vs+J}ev&qmd7K=n85sSs%#bPmt1&PT*a|ZD);@?8T;9ts6 zu^>%!=p+)0#l&E-7`*TD$;%CQuUTF#ZI$p>EgLG;maxPH2**rCLW?Jn;gJ*VLW3Xf zEmt~1J`^)gYBAx8vz@{|f81|w#tmm2=@M#ucCT6K7A_bi3^&|M2=^RI%J0{t;ed-N z;gH#$kes_aoJxm=&$N7Zy;s=fa7LJ9J!9D5TvmACuiNBypK!;~ocYR2PN;LfKWxSG jhBqGNg(r>|g70pF%LCzu!k}=o2K_h7}<7Zk$Ng>k`(ux_xiX^S!y-43$EM_`KeSm2WmjOvpjFvDx;#=_P?KrdlPYMSeRxcWJV*tKkiVX zt6wx2Mq?<8g#Xtcu$?|@B)ed6!0L>R=~fMf`^{*RVHkDe9eMwaI<^%PMmW+MF`_O1 Ke0SkDEPep{N0aIR delta 276 zcmWm7JuE|U6b0~{r%&}mwW_6!#cW|vgGEFw7K_Dhu~;m+SWF_}<=F16NJOGb6CV*I zZIdTKL=7wk3{x%Yd#TG}4NS5Okvf=5NjWhkDQ^b}3jJi!gi9f}LS zn+IHrD9$+O3r>0Nt2y7Pxa6-Haw{r0;&7KsZp}k3#uROOak15I+-)Zku6<4j>Rd@G zl$mZtz`7Z7HYM2OXUhFr?@?THD6QCFGcB0rLPqh%Kl6h-y^1@IX5Hm>R&mUwK1GAk uoJ;m|uAR&)uJ~#`u+lGRF+bqGpO{t76%?!dDTseRRhfBVP~7B5G4c;H#BhiJ diff --git a/sd-card/mp3/0056.mp3 b/sd-card/mp3/0056.mp3 index d199d0c87ff3825964d954182317ed23843a46fc..657ca40342fdd40f816b474d91f08fc89d72ec86 100644 GIT binary patch delta 490 zcmZ{eO(;ZB7>3U`jKPeb@o!kL_S3y1#$@B-Hl{J-CJGxHHa1dLHYACK7%!{yB~gmS zEKH6O;w2f+~q)Qw-nO-oE#q^3V#T%@+BpskGn`sLdipvIi2JD-ob7YKUQ5^jC28N)LBjgUEb>k=X%gveA!X)8?yWv3OjnnFf6HWCfTqQ;=m z^(hvNe&>Cr)ng2$1N!@n&d7*;6%ATrufJ7Osu}Ib{_m+} U8WEv2HZ=!=LH(b)v%jCk59sTcNB{r; delta 282 zcmWm8JuE|U6b0~{$Ma~bTGi6Bm`xpg#bS|2EEbF1VzF3QEFx;5IV9>{7KvbZ36e%C z3?w2FA|yzpi=dOm!0P^&-*AR=&hlWTf2|iE#Xdn;d{k_428w&eJVliUp5T`wb&6ZQ zo9A4pR~)j-7d-LQSA21zK~dw6`NGveP+?D_VxKqW2B(_T5-%dS(A?%&Tu@>yuGr*ai{gmg3AcWc5Ug-AsTkm&`ONiJ#T)Y}vA1_A*E!wh zwt}?#u$xxvAI`X>YF=@^UCc~};+iMs8OO7VJ$`2eb1dZ4|16I#f%(n?A9BGz5BYO2 diff --git a/sd-card/mp3/0057.mp3 b/sd-card/mp3/0057.mp3 index ccdfed78bdf6a9eea19b2634620fc05da6c634a8..750810fee1da0d6a54570299085157a7d5ebf9b2 100644 GIT binary patch delta 484 zcmZ{eJuE|E6o$W3t(F$8e_~+vliQZMiN(bYQmq>yK_XpPEEX|zz+f==YhDua9gPqY zh7Kf%!7LJq7{p?+kcfrB>bo~4iIbcsC+9uqJ!d!e-oDXBZ<&~+iOd7jEoO-66ag30 zBaU5?9$@A%9pOcB8w+lxP1JcLZQ;_xl*LFs(+)lr8S7q2yJ+;W;*H`erV5yr;VP80 zhHN3zK05qNIXwBLx-uJJI!CC8X#+Wj@nWg!*J7qUER`@_qakQ-T?ZxgVLZf?#<$`Z zHcRaf!(~#r-Idus)8)3aw?fhh4l0-q(OJoKg=fWcq^l%dpt72kdZw6#Rl_uoj~eOe zn+x9gkf~jPR_k6cEOXfCHAnBgLga)HZj~zb94e@`6A3HYYxE~miT-557!~12q$#ZH z{y*wSvS&~jvA^Qj>-Ap+~s_iP@xw$?C~Hj)H#t5Zuych zcV#&#G@0oZ#QML}DRc9CN;v0wkMPK$beL+S4Q0+{gbn^Ue_81bKOD`PYini0p2fbf xIhr$E^EfA5a5694^VQkpYQN!*`2o4@m9xf~g0RWYg8Ai@bSuW7xW$oT@jgT!xIz19;TWJiufb=fZJvc`{=U}9OJEE85dg!&e34i z{T-|B58DU`pKTm+xN0YOL07B3u_Rc=m_s+e90YT?*+wvreNKXRtT?>c&0pe+5fe zXeVLHZVrcd@78;lItcF2?BQ^OyB>l|9P#SgUj-|;*2!TDy{&RV@;W*(6en#nQ=QU@_@nv53TEu`qLU?)Ezo2@4TJ#Z4no z(Z-)eB%Qi2ln{%-V)cE?GrcFLR{oedDC0YvHFU$a@WTxV_sj&s4Nn8Z9Vj~kE%YDKLf1PdaCk^ME>KCs0m9{A_1F&dUDrwfKFJ{SCUeMER+vS@hZ daZzY-zU1FuolS0!8bX%FgiT&Lm$*0{{R18gXlMWc diff --git a/sd-card/mp3/0059.mp3 b/sd-card/mp3/0059.mp3 index e6e20f24d8e8c2d32f3e28ef552c9c5f90ed158c..565fe2f8b40cfd5883d32559d2eb2671c6fbe4bc 100644 GIT binary patch delta 484 zcmZ{eODIHP6vw|aW(;P$->m?WlocpXfP zeTSq27->vwc;JdGOaX6mZWWL zWHDW!)x(s)i$^L>d95w^*-Yz5D4rvnBWV}!Io6iBT&5JN@|cEkktb;oL-|Zk_*Q(z zY5~(R{DqQEaa(Bli6ZL;cd?bcQ_OUUPzlo+UKLZAE|toclrbfdR9t{r&MNb>T)IMH z21N@>%kf_kVLR$pTy>izBa8fLdXXb1H5>h0l}Wks!4*Ar;elqyHtGX5Ji4LvNhx(0ts NeIW49xwF5Y#SgfrlAr(p delta 276 zcmW;GJuE|U6a{eS=~LROR<*RTNNgs98Z2ET5{t!RV(4OFFj*`{2`{G%?yX2LRHOrm z_=qK_ns)i$N?FtNXY7a;9_6*J!o4F@nLMC|C@diY+ccQDMSU?6c+xjyc|{ zsPp6Bm5Abz{l4IwXTGAz={ChKf6NQ6M+G$wwJWyyU_P_l;o7{I;E+2p#U01uic`MC z#av3bm2{`#fOYehlS#o5Ka*}{wM!9lAf+fXObO;VlUBU)*L-KCTXDtVjNqD$jLY*q x?n5-|n)kAbdzNyF8@`z#mwUx>u1|5v6LXVOd9}{(y!aC>vyCSPg*BE6kw2ILZj%52 diff --git a/sd-card/mp3/0060.mp3 b/sd-card/mp3/0060.mp3 index 4bdb064d55d1fb69265ddaf096a97fdf15486a10..e43dce517a3cb796b745e0516c0fd5ca09b770ba 100644 GIT binary patch delta 183 zcmdn0_*iMfR)KoQ0M`&lch`su>?-xW;{#kBy%`Mj Rj4cez4J~mvrva*)0RTP?BC-Gg delta 7 OcmaE?v{iA#RsjGGp94_< diff --git a/sd-card/mp3/0061.mp3 b/sd-card/mp3/0061.mp3 index 7ddc18f534918ffb3bb6373ef370c58eb72e62eb..0c173eba1ca0ddf132e5879a7e07c31b03c65a74 100644 GIT binary patch delta 440 zcmZ|JJxD@P6bJBoOw-g%Go1=**5vw0($cFQBBnk>!CG8uX=!O_vmqKQIZoY!6iqcq zAM`fd}r7!#)3VxnF@hz7vI!N=(3YrAhFDE1+vP5ZvOS zLBIv7Jp||2k{)B(NN|F~CIS0+Wg@6yp_gD6zooypZPxQX3qcDXrI)zWM{tS;tB_kY zE6M4kjo=O6Z2}s&YA4u4S3f}n>(VBs90H#3(?QaW0g{<9r`|W50_wQnBDlvNX#=Z+ z1P>T=3%J2&H^CV$4-p)p*&|>J4?F}KPI(E|@WWdzE1H^D5^C|)%vhZgO;u8i2f`DfSR~rzyX&20_6I2#gEIgC delta 232 zcmWm5Elh#|7zE(mhlm35BSNx}-JAxUo5f9hvn)_MdiGS{p%|&6v^_*eAaZc#+U`c2*mN%U7A}^G< LQxJ~%QD{y97-n8k diff --git a/sd-card/mp3/0062.mp3 b/sd-card/mp3/0062.mp3 index 4042d45843885fd3907fd4eeea700d61f780520b..40a2837dde1093e09c94f7ac374c7a8df839b74c 100644 GIT binary patch delta 465 zcmZ{eJxD@P6vyv*WtN(0W*^W{vnKE5Q;L>e^^h>14^glPB&emOrK!b+=DvJl1wJ-%`?U72QUz`I5 zpXiVnT;PF3VGs2h2B-MSxsTae2A9}rp>Tue7U4po7mdUU=mL_AJFW` zWi?YzY7sqsZzUv82&v_d*LIm4TrOA43`Vq3BNH1nVp^IgO4zUTEB1fWlra(~K3P`$ zWf+diJfWLo+ITr2nb3x#8PSp+IS^O-Lb5D(O%K&qvzodVC2~Mj6}4Bb^4-PCGWi3U CdX7E- delta 257 zcmWm7JxIb)6a{e3D>FYb&9s7+<|dK9OG`^rOG`^ri%W}3lS@l`*^!oea}fkZf|Mc> z92!)D8j7f;B?7kw5n8)%`SCcMdtN8_$)5+9Rtkck@+xlVSc2l0ai{3B=>#qA%qSlC zZMJzbs~B)SBzWO{$anVV6nC7Mw>%3AZn!zGxZ=pXU@_u5E-GlS7FAo@iYeas857$% zjw>FSTJW7~^Mktyv1FW3T=H~LQRGHa@x*@8=Ladpn1AMg)g}LLE-iNZoL2OBwCs1n f8NrbCjDK%ueg0*3SzZy;m|ayIvTMF`Z>{h(9y8C-NxgL|}l&4b>q6})x{ zMLDD7qjWr&7f-5dfDVppXYuuSu)air;Z#)Yt9=Q^+sgH=D#Zpx9 zN6;~XwoUl)_NyV7mq+rM`Uva}9V~eLVig)G$G1-?u(PeSg_IN45 eEiY1bWp&Q3kBqzAPm7z`TsX`yJ73{b+WP~y$7JjP diff --git a/sd-card/mp3/0064.mp3 b/sd-card/mp3/0064.mp3 index 2c28f5ed0cb4603b36c67faff07caf1a476bc4c4..a934824d201f52a13de627e0e467970356417d4e 100644 GIT binary patch delta 453 zcmZ{eKS)Ax5XXOKnx^*0OlxSUS(Eonv}oy74-xhG5Cv;-simbw8d_|K){>K!-yua1 zXdqM+QRGk%6$I5%OG`uTEj2Xsd(ss6h5K;aeed|zfBW+;BQ-Xz?m4ym4( diff --git a/sd-card/mp3/0065.mp3 b/sd-card/mp3/0065.mp3 index 36668ca2a38a50499c030cb3efa213084d296ef6..dc055e56db195eaaa5f609f8f1e7c6fd59892c71 100644 GIT binary patch delta 464 zcmZ{ePbhb363l|p`Czm-;PFT;>>h~l? zLV3%SKPk#VA`&HqiFN3NeV(sCIk*;t_d-91SmB9a8E1i@jCKRT z8}1o6T%l5@S3U)IF>fST$4(Q65}uj}wlPyraEm{JWn49LIKUnY!6DuVmT;z}%Y$S0-6THJ{6Bj<;O#~~r*i6ue-45OV!olGgC!7S= z_$#Pku|>b=bLr0aEOYGTXV3ru diff --git a/sd-card/mp3/0066.mp3 b/sd-card/mp3/0066.mp3 index bf991577491d20e35ee6ae7e2ce72a0de1bf051b..2c9e124952bc56aef3a1e48961e93e82e57599ee 100644 GIT binary patch delta 470 zcmZ{fJxGF46o&7yA5HDY%6_1sW=*b^CbaZbA0noGh=H}Z)Y8(@)RIHAmYoXX9wG>W zB7_p68XOudf_`W!A_$kJmX_AeCr*I}?!)1{=Y8+J+2CVnGYFr#kb;xk3&Acf0l_^w zEd+0PY*FxodL2Ot%gifWttU9bz6J$Pc-cVkjx&t}W&B~5aKozL8a+0GJ-lP?;6f9@ z7Fz6z)AsGe>ZrqXy*d=TmYt@{)ok9FXP#lKML{0FS_p1%t(9OM2igd{SZq_gIp;DH zewpvM-EMmQ9SVy0&_QsHi=6})Xmu<09=Zu`ajeUnd@u{R(yf?XJ?0+^%rqLk1gH4j zi>sQUr?iNkezppc7edtY;X0_0!Q)EBjiHDZO=RNHL|jV?zZ~)U!(qoibtW-6Ej&{C zy%i*!RXwR2Q`&4LAeGc6Vi~g)gVHxG!$B!!|NOYE)~cavF(G}!f#66WRO7kXs#*L2 D(kGD3 delta 262 zcmWm9KPOdUAlC#FqkYfMC(Wx` zDpc9w@M6Qg;$qrx!CqQ;mX?;LmKvhf(?al! zXBG|(R9gs6@LjNon^uAX4z_aG!>d+;63(>|9N@QL5py;UW%Sz#4)IR#7FXH{UeVIQ zVINB!1UES0AgJSugTp1RJB^L*PNT9SSjR+{p?`J}jEzg)wFoF05=)K***g^>9pyYuRL6ODgj$Bt-)udDQVIJEzU0m|v2@0TY_D znNO?wtg>JZ$fT9&MAit|h!jkP$0L#?4J}XE|0~s1CBdZNs5~ai(SOd}YBsZ9f{2VR delta 256 zcmWm9JuHJ^6b0a%k1B0dt6C%$vq_{Pu~ObPeXrurD diff --git a/sd-card/mp3/0068.mp3 b/sd-card/mp3/0068.mp3 index 588808fcaaff31fba58af69c76a23a4ce47573ee..6a11f9b0853ffade8c8f662b757a8c185bbcca57 100644 GIT binary patch delta 460 zcmZ{eIYCALfCbQ2U@u55<<+nghZlE8f|O@v9i&^Mi4ZPi10=c z1i>3bLH^j8tQ;8m4`TO6q- z*ufv>1FqCKd9+%|>SL{waJ-J#qtr_{!mWB@_B0SI;~R4lryHfad#K6upucGzZyJ>u z!#0yf#7sXaLL3Sqin+Nqb7b&3S8-d97*Q(|k6LjfEwolGsC9(B|J0P#KPUpKst0qh ze0e=#+5^UL?t^5)=!<2Xk&U52T@9M1}zqoj$;t+aX~FxFbL{I z7(-C}L4*}8mKCe4X!O4Fak=NhJ%7{9%Ha%tYO{uiT36_B6T&mof$+#iU_Ms|gnRbf zOYRN|r<@E8*KCEt3zvq37W?ik4m)}GM e!yV5Ga_1+6I=|c)FA;T;$emrDAb$BTe195lyf#uvjcC79wJ$`IF#$#3Kev zCB!2JBH}ejgheC_7Q4mjwCN=APtGqV=X>PbHLpaLq9oESlI-*|(-bC%X%#*P(-!s} zlJ;O0Fm2;gxq+ENrZv<$CEei6$*P(vVtT;0av6&*N$Uu?nbNpdu41g1X%h~Qq&IAO zn2ylqWy;{yE9n@MKBij)N|>&ZQ63^uDrp*DrA#r*moa^y-p}-c3%^vILxJq z+w3>G&4DME5E&svp*kvCITh+T7t7YWjd*g{iYF~&KxnaWLpY{;|5Q`Su3iz+wC08! zNngI+XWBi+U~WNwpV65Z&R#{M*3_#<=cv`Pjud4M1i`hi5Sh|=nN<<>*VzF3E5|M})EHuX;ze`hXh)Ng{ zqQX*%vD+);z-nr#poj+s+ei zb_vHE@(oSi_;Pi=TX^M_*OEeu z>0aT5m(E+xqzqg9P6>H#_X%Gd4uucaLvzc;^#AwQ+2TRJQ0HjI@Wf_D&Xob7!C2Pt iz-m^wW25X$7qLzDEI?*`DH}_ diff --git a/sd-card/mp3/0070.mp3 b/sd-card/mp3/0070.mp3 index 603a6f20d882543d64f5eab024cdd6f625115850..c7dfcf0a07183d4911912f727124e437df6494f1 100644 GIT binary patch delta 381 zcmcbpG+$+cDdUxiX0sVPCtjMvxPEdz;~d6alX;ltFdm=0l4&;M`^m1%a~Stcegq^p zO|EB|!#H=c4C`#hn8`a>=P-Iq4rQCeC@}dGkkpvm&pw+`VX`&{$b=I>^4jE7&N+;S zCNpu(W?VLT9@iYkwUe#6=K$F^xMwr2nq0y&hwtA0|R3nFyY4ol@D?a@NtX;8xRuY=gdg2pl4)hXk=o7!?_Jm%?tqMJZRPc delta 173 zcmWm8tq(zQ7{&3O>)pM$swqq(qR5iUpJ3AznWAH7isu>m7C|s(Q*35}4V#%{imobt z%4gqnc4oPAc38r5R2Ga!7sZBcDE4$L#g(2V__FF#9QiVC+4CzrrUQZ&R{=$xji4gU zkFm%>NKjxt{9ia2Uu;GcCt6X#g43vC&5Et2@3vscj-z-m8B^RiH||)Ai}~=5D<CDKhj;w)nX_9x1m`&9H`RMT7klRd zX8$XAhqYdla*zw>bC6&km-`4d(HY_*|0F~(j1&E}ifU*DHKmoBPR1IHF}rx8jw}j@ zZ>zFKJf)`fayG4J)e@8CWJFeyo_}glpU$zcB*h~ZbZ<+~YsQQ^YXubY>Qts|w!#?A gjl~m^Bn>W3y4qO{P0cVV8dKz0OlfoOdTW^d0UZr~DgXcg delta 226 zcmWm7JxGFK6a`?;=WlBI`8%~Vhe%K@E)F_5ySO_jNQ=7&uKmdQn(n2+3d$jH5pi%3 zw3Z6Op~0y|XmoMyeV2y|_uO-bg?0Y+9Gk&~U^nq!mN9GKy=4vSRul KvWhd7a^63ml3H*8 diff --git a/sd-card/mp3/0072.mp3 b/sd-card/mp3/0072.mp3 index afdadb070b2c9315d58ddceed7a45793c17bf4d3..bbf5c3d2bbc1ee5d959e1e0a8d4d2d877e038e01 100644 GIT binary patch delta 467 zcmZ{eJxD@P6vyv*X_}g8ra3g!><8~@A4mLFr&APUxELqWB)wA5llIkcplYUp01 zZ)gz;iXbo|upokHDx#&KmZlnlrtTA`z(1THm-GMMbIy%#qdVUNpM_oup28y)D!(`f z4BpWsFgU{_fx<2-4Gd23jdLHf8U|(T&{C-2xmL}j8ddj;vxI9p3a9AOGuXsi&Mlm3 zVvt9{KzXugU~q~3Mg|A?WTdcz^Ckwk1`F|N0&8OcWB5btejf3}UmHR|jXE^yDT?izG3DB>&U3NE%&NTb8av>LLI zd&Vng4kw(3tQ=R8a!^S>=m;qhLNvU%wyWge?W!Vie^3rZGm%g^>^!ocGDInE3 Jcd7Q7`~lx~j935w delta 259 zcmWm7F)Tx26a`@C=P9qR)vDGTEM^l?OBX{;W{atd#e&4bVv%k_oi=H5Z#s}jD%G(; zqKQa6i7>D*SQu;uX=3Q&|CevL_vD<~d^C5IN3XIV$X8y(M1~ts{4g+zE}KSh&ZR-c zH9xH_?hPsKIpqnSdFi>#%CP(XT01<932rzuqG<5Jdcw6)MV-bMn{4=sh=sV~f^Ts_ zliPt}lj(%wm~HD8my?1bzmu+We@yYp>6D_+Zc6aQ)wEmuvtF?}?lQ9(L7Pt*cVC}S mJTjIQ-0?K)wia`WGk#bPxH~B*F&C;A4D6KOc^j5l3jKf1z-Kl9 diff --git a/sd-card/mp3/0073.mp3 b/sd-card/mp3/0073.mp3 index b21b0bc48700e0fe1dcb3b77ec19ef8f342c9c8d..d20b1592c8d0745fefc86d316ea7a9c664012e3f 100644 GIT binary patch delta 453 zcmZ{eJ4ixd6vw|~nxw>55W_D3f^MbZ1@oi7dmz-q*NeTzs$T>UpysFJ8bL|C% oyfU3B8J6Y8rBEy^35_mJ*}AoAs*+(+D3VAdq;Qva*E_@P53pl~I{*Lx delta 246 zcmWm8El5KF6a`?;o6pT{&i%MqEM|)fS}c~OSs#eWV$fo-m_!hhqK_j6@6jR*1Rc!e z1P5vnL4!#gf>>+@i`Dze#|f8nZoj@abuf+3c*d|0zY6bMgK*1yAYAY~F!b0+2`~I| z@3=XT8hd4 av#M~+xmvRIQB#;HZ|ua}8rw}pQS=Yr++q3v diff --git a/sd-card/mp3/0074.mp3 b/sd-card/mp3/0074.mp3 index ed5e154d9ff57529c09f7da9e39655aa7499c0ca..b5853b658592b49b53dac96ebc146a782b09b885 100644 GIT binary patch delta 460 zcmZ{eKS)AR6vpqd?3J2n|7>VTXj9}p|CG_vs~#ezK14x-u%VU+S{kCoh6tKcjze%y z5!FyaLJ<`O(G)F3G_|xu&>Ai^cAqo_9=Ja)-}%mWPDT0g7enw>8sd;Ey$~$o5)f>o z%|vjI$0iQfs5KGX;=AAp7Mcyc-^_(_WhOYnObbC7Yl82%C2`n6Pbg?z znk*bHaMxlaj9Lkb_-f^l#bq18EIQi>(pVK7#F(AK8GhOco^ZW`poVUTvHjLzY@Bu) z3EzTMEO!!AQ0X%Kk6i>exX?}FTB(P_B_8w;?BiH3!3Mqw>bUCS=8xdY;|#j{Hu26S z6;xeIs}U{xEHPGPj5UiVYOhWKA=fLe2O?@Tk&j0caW%^nzv5NGVe7wcCNY_09$5}} z>qyxC`YBDHQgiiybV{9w<&Cc}`jSC^NS5V+x$)LUR$WtLO!fs7WkeZnaPHdQF#822 Cqltt7 delta 252 zcmWm8F)Tx26b0a%$Ma~bT2(bzI$Kn*SS%*9MMwe%jyNNTB}F^C}1 zG$gG^x^$8z7K=^9!XV=Rmv1<^Ip_Y&TojJx@LQQTlqy}J%xwtgOawxgwZPEgQlIe5 zH|H()`h7kbnoDj%;fgB*!WqAuA3Tf_^JdOE=g}5NT#0^F6 zB!n%dhlLH+ozq-S8XA003O(+R2!G6{{CXqh7gy81;oEt~lTqQCg^a)NWP~<1$K<|^ hvW7=ivqGJVe0cVi4Dzdt7t4PZbYipNSlNNBEL_z`B*w2HGl($$QBHrmMJ; z`G#WS^p0JdVclyNl*6l?(*fokf>Ll*8;A3f2}Ei*wc@iz&^1II#_+Q8`&`wfQfYZSDDH^~JoHgU?J(Zem?pTv?S zgey-LlOEf$7S|Knke+(7GL~nInPke?D^fz1ixG_nhqU2XIyxMSYANOqbhrD%e*3>} zGB!BM+Eg{zUPR1M>W}I15pBF^kQmbjB56ai4%Ii>84jtc+A`H&S#C70YZ0dU0^wjV M)Kli&mC`c%0}jWF{r~^~ delta 261 zcmWm7AxJ|}7zJR?oqL<_&H3is_JT#LMa8XHEGDZK(}EVuifMu(h*jrN5dNPbnpOmb zi5Lb`P*HR+P%I)?ghk7$_m+#1&mGdx~rRS>L&z5GW^;uK8sB;M$;=N{q%A^jP=Z^jx55@-q-z zawDZ!WoF17Us%grObe#?n^wGWcUbYw@lec5z*Z=BZzZF+W8d23(TIDV&I+#hnss@7 oRB_KlPVmh0oXhih#Tmb>JFFH2Rpuk{CX!2=UXkr+kLAe!2a}#=8)GMMY2j!3U5_R-B_w-vd~h)w1Ps7q8E($m_G5X zS-@tkoffK7bcCxqraY$VS#Ra?D>}xmpXn4m0o#1i+{b)_(!UEdGEL)9GXkrLDTt3I zEEtwKWW>#pTbB@dA%s&OUE4)!=;vaktZ>{&q{mZiZ{Wjm0sc!60HdWj*2-mZH9Yi5fcG zjtQ4cjtl#2I*VM58#eeE7go48A#8CjAsq28Vff-^QW)^h*EW!l_#nHizN3ul3ASz(^vS^fh->S{Is diff --git a/sd-card/mp3/0077.mp3 b/sd-card/mp3/0077.mp3 index 7aa166c14d12065a1a89f514788089f9ca61b188..ffc3f19c0d019b69988613e3542f7c54bc202406 100644 GIT binary patch delta 465 zcmZ|JPbkA-7zgn8nLop3{txBAd82n`nq2n7FKITvWHm}ITwGk79Q-L)q3_i4J{F1E zMd3h%q9mo{ASn(~oLwAwx17|TdOy8=pZ9rs-~9)Hl>mLNwksN4ePFu96fqsZX=8ds z+NS6MW**Zn-n2=~@hAMCl|| z!gPgB2h$Fo9EvV6?POX)MJZE1_O#;&yA)aYa4|_NmN7k}shsH*=jH3R+aHWnFrDCA zdyPaTQyRW1MO(P5VmifSHPa#NHHwa~UBlXto9O~C+7xDL70tq3#}vY$HVLafF=trj zkkM<#@9jeD2_f?J(Y2XXqvLEvEPt=j7afcAMI%OB_++cs*Wq*g>&Buz1HvPv-zR=+WvT`)9gbce?>SJ5n62K<45l(KQWt0>n~%QV75wzLM@g2DD2-<|&(7Qf?M Bi<1BV delta 257 zcmWm5KPbcj7{>9QALqE=Id{$-%3?N2XJN3I7%WP%8dw-i7K=%V>$!#ZN#vHhqMVe) z8I(U=s2gNqxI`JHtlnEb!?(Vb+{egn4!`+P!&1I3yl@3VmGMAmuoM{HIoTpq*l>&7 zXcZ1Q5HTF_G9r{Y+tw^U?iF{Uh6fI{3nkvW+g$8uzCp~q9K?iYj)lS*Uqiza*W$t+ zQ=P&bPu*2cB@7GvN(kfJ>=HgXm=wNQP3{M^kn<_wmVa)Yg>K=3!)e0_AJf7emwSY3 hMl*&R9%tlEWQ7{v+%ngD4O`6i3DZ1xi=62%`~z)EXA%Ga diff --git a/sd-card/mp3/0078.mp3 b/sd-card/mp3/0078.mp3 index 821fd4c358d0c5a07a10fee3d559eca688ebaea4..79f3ce438c7b1a5c55c068f0d490d1d3cd693ee3 100644 GIT binary patch delta 466 zcmZ{eKS)AR6vpo{O;an)EC+|0HF?j>jFw*Y5;64=3f5vn4Yjni6od`YnsREor-*8) z!6MN93`H)r1QG--LAV5sEv?-rO@SZWA0Ox3^PTe;+zSl)~Hy1p16P)8%4~aY91vhZLm%}Xfc*s<oljX2qj)ZOhx|!r;iuoid zsq{+FPf5Yq- D;cAQa delta 258 zcmWm8AxJ|35C&lWr_ar|IXCC#3oGVX6kV}cw3scX1r1s(7A=+`7(^exraNR{TG)at zjD@L#y{RC=l0^_fR%Jynd9Qr@9Nd3*z1&~AT)|IJ67+(B;(-THJTvJiLe?C?H`m7$ z1AdtyPsSBzEVzO@wp_)CTN8>lM`p;2n4rsITv6wf+2rn|qDRLQ{PD(9yl^$4=2b?(n9 m`i$koyv-@@xt3S^^TRyn@tk0fd0*Yncy}Y@BIV86=@9s diff --git a/sd-card/mp3/0079.mp3 b/sd-card/mp3/0079.mp3 index 427350bf9015e65fd3a1ac8e952d906c57c345eb..4e71c07b663569dba071a611e9983f28f3d1dd08 100644 GIT binary patch delta 460 zcmZ{eKS)AR6vpo{%~C7P%!Y=VHF@tzrL^?Qmx%cXQK%?fY6+sH$srpeXes5?bPpj^ zkU|bY(I2p;XbGYrB4~-At)ZpH?vtj#56+Lvch33Ft;j!0VGy2+LmWcIM}jk427&_G z34uqx=qXgh}&{Aee5!u1YCV23pn{{_1RMA&n8uu&o8Fbi)RY~qdE zoL7@tS`BMc4`#-yj4`7qp;Da!!mcZp^oP|*JR6I|W9k%><$zZX3|s$oGx1P@c_c~k z))BY;^;24MQk|{`q*Lm6G^;<^faFUkLy}P6{8&q)(WItEndI~PgG$if;Ja&o%j_4< Cf{8r< delta 252 zcmWm7KP*FW90c&(<9W1It*ROYg@+%Qvq5gJ^Eu*aAu+_CN%E;!jC)Yx`5 zx!Nh*aM(9I^U4qN*)HLf-_9d$1%_RYbPLCPbXHmH30q#o+;Sx%w^oP>`+SWW7Pt}< z)|u=Tiac?Sayo8k^D{2IbFEMKVKyQ6)kqlbIhPc!`RlyoPQP%*T*~mkr=P}t;~bDpJ)`Cs|c)Dp8p8@$Z~|1u(F1poj5 diff --git a/sd-card/mp3/0080.mp3 b/sd-card/mp3/0080.mp3 index e6fd4f6df5da0e8c1d234455dc54b9abec296b73..88f8eaa5b1e40230fa19c71ac57daffee51e3b4e 100644 GIT binary patch delta 183 zcmbQCa7=N7gh0JxfNO}OyKBS+b_Rw%1_lOZAO?a3U=m0Gg~7l#z}Vf<(=XoF)6ds2 zg2B+pT*uJTfCnlYqM=o;vm8yf(DLW&ayc9r_x@d2)m-V6qM Rre=l~CYCsy(*V`Y005?1A>RN1 delta 7 OcmX@6I74BBga7~w{{nab diff --git a/sd-card/mp3/0081.mp3 b/sd-card/mp3/0081.mp3 index 0d4cb5980112dff80c71667540e689052c406d12..12e2db49eda934ba2492ee65b4cffbcdd7336898 100644 GIT binary patch delta 440 zcmZ{eKS)Ax5XXOKnxC{lOBF{`ST6)z(#C(bNvLmT%+ku5R^$Ye0)tX0JQQv5Zu#$+{Q^7@f>Qth delta 232 zcmWm8El9&*7zA+cKj(Ct`*0u0vOEwMTv=H$nJspUAQl-|6k)-p+f5iehlpdrpr9DU zu%Q+aG%SfQ!-6)^l~v!j{J3x&*I)WuJYL3cqipzS41_OkLwI965bk*q7|ys7651R& zZ@E7u+;Aamxa3_}IN{o~u*;x+DBI2@qIYHS{eo4DK|U=<}B zha)_;33$XwyTQ7h!ws%G1k|v*Q@}G8i4}}FIo*Cb1#IDNmjE3{U50z_;?!!f+n9fd z6)f}!xIm?s!y!KP3OL4EA4TR<6Ki5Ivrz^RFQP{16PiFMS63fro# zrPYX*eX%fBWQ>{Ukxm*U(7K_x9*C&XL@pjp#MLZQl(1KsknR86Oky_4JhJTfHjr>M w{gkH9s|$?}(kXQ&mNP~Q`jY;DOd465wzi7uni^xWZ(JS^E1?#3cbdoS5AnE++W-In delta 257 zcmWm8FGxdS6a{e3=jPlW=jPm;U@@CSp2cD@Syl$azGAUxu~=r_##y@;K}EqKFKmYC z;Mvroph09HhzK@=2v+Y`eq8Rk7fvtuTy6#UZLbJ|_OrM<$89LSnQ@AU=T6Y&`k3OH zALa%3V~ShO#|4iZ#QjZmT+!o?dC6u%aKyz4zqvOXtS1$_bSc3p4^xUKuBN@OX~7kD zGKvxNlZqFHX2gxG*zZ?XalpouqQ-@sPotbztIfQh|ICP;X+_ATg5a1B1;rV+W_-^? fQOuK~;)ZJ_#gK1i$h}#yCZ)2X!oK;=%3S9kb3tdt diff --git a/sd-card/mp3/0083.mp3 b/sd-card/mp3/0083.mp3 index 4bc9ed070ec6b02fa51c28aeec94336c92627be3..ebb287af04f06fa0793bdb77b16374555cabd7ad 100644 GIT binary patch delta 453 zcmZ{eKS)Ax5XXOKnxBxvbX4-x$wt{M8GmeBJr zPR1IHF}q9|Rf`f*wjyJw2`!l|WRls8mS>6*4J%Ws=U;azJHNm}oU36AS#PJG(~Y#Y sY#A)(w7FElELkqZBeCct=X`u^*41s)(6tof5mkxDxZ362-Oe)m1K@&(e*gdg delta 246 zcmW;FEl5Lg7{y`Fo!7Y!=jJ{v7PG|#Ef$MOt3j~}vSKk=#z0Mqj$;tsgUCeCi!2Dr z5N45yh$~BiIAmpDT(SDUmM0u|fA44UX!c|d-_?1;TD2o|xecMgq$k|-#xuNeDI&bE z=j^gPEc7|$n_C*b(B$ffaLvKL$5BItGo!)g?>utac^As;dDCu`;<0!yPgrQ`0G65>4b2{V%G4?c2=mflna+= d-caLZUfAbiLD=F~LH_&D?n;xD$-_tx1b24)`9Hr|xyNI9AIQR|X)hEo^Q5fb@K2l!InVaY93rNP5Aja%guMhloW;3$+d ziOoW$E3|u=PVnTFbchKb(=*D7n68mg?jlkwX%-*FOmWPWFs-4!l!40|hbnE=scMl}%qEdqEEbcci$$ykBC(jnZ7?uteP{c>G=zoW40VYZ z8ka>RNIF?8jj&r7{av2no!)xsbD~m0Se-O1Rhz;FS0NlR69{iS3k=ts=@8D@av!+e zDbzWVFx>MdA>4AVOSt5Z+vGvgyo_}VJA87txY#2cGe{XWSxX7eoJtD~zNZaWT+0Y8 z=6Z!!#%|2ntYM$uSz&>@eL{_+p>WDZXt?8iPWa-l8#C$`Djd%n_V|()A}$ZKKa&N+ dBTou)r;F{|5BHGkgN79rhlDaO+&`9wqkrQkWq<$x diff --git a/sd-card/mp3/0085.mp3 b/sd-card/mp3/0085.mp3 index 46e20fca7fdd8d252a408c0be17d988f89acd360..b4e6ba288b8b29616aec3312a184b75adda868a1 100644 GIT binary patch delta 464 zcmZ|JPbh8=9&D+{)ly>3bqU7X&G-vch@q0*? zLRs=Z%9V?fgOqlW?C#>~x8T>Nxrm8!rX@IBQf=OG zF`c8!&2)iRx1@c{cv$JJV7fy_xrb1tqz&XMnWmAdVj4qhHB%0E)sj9j>ScPvxAG8c zHTipjwUSbJtYuoqWL^Fer%%!q(mqyp*XJ`?Wg4>$lIBp~$TWa6-pM#5%7Xq|dnu+wn=sm3FH!@{p=dRu{r=eHiU v>>+ccupkySdqc_mS2YHP)e=q9TE=_I{%f@@GbFTtuD2UT$3N#T{0@sBuJemL delta 256 zcmWm8JuE{37=_`SOSRgnR<%ehW|K%Q7K_PZAQ8KP#bPqBm<;shbo)*u2uYU;N)T!M zB#6YqVzD%m##)2bcb8{4!^wMA`W`Qr@DolLs^Povz*PtzOb0@P)xhw~=?>wVP4|eK zox&xDV}?4fV!|!wyM!CI-6!tG4NZczBtf9@toY3N*d%;Ry)H9Yhl=+qy_PNp@mBb5% dM;;dBP8Fl&%RS-xfMJ`(L1B*P?gD3rD*wzsXSx6Y diff --git a/sd-card/mp3/0086.mp3 b/sd-card/mp3/0086.mp3 index 5503b26dd29428c18c7f01b2b7ddeeca20eba4ed..f2b1bd72f7c82842166d9a5c96fb9ad7e08fcfe4 100644 GIT binary patch delta 484 zcmZ{eKWI}?6vpp~iAj^#zcx0dL!Dd7O>EK>7oYk_Oa47V0;x-uI7!*|ZT-}fZ`cdikm-_5w9 zSIuLl$5;Y{ioRga!}JTqi%du8YQD!Kuc94H z`E2ErW&?Nom@07j71h!5Gu_Aa0Mk8u4=6f7CCKy|LzkG|psm?Ks$XgMlYXWaY6DE` zxH`yG!iPbnl{-UB?{KEs!S*oIH$+AhJ;ULM?X`NDsRr+;qK9}s%Cw6cAv@JihnrWF zP97Sw-QQ}izzQ>^aT->hjn;y@j^_9S$Z>bou*`yyG)rH-LbQbtZr!?`I@HjzQyJ@8 z(pb!vGmF`bQ4(@~b~=(s1peC=v$u0%O3KKzgKV(3&zn}-SaLonee$R<*RTm@N!yuvjD#OBah+EfTR9kcdbOB))D=C;y`ZF)%a{ zAx%UlgGd-^AWga;F^I*$>b;iVaF*wMjvN;Dhw<(g3>CjF9B=``BjX{V$op!Y)T+!a84K zhFdPig>$AlgiBVO4NfM^)jtx#5jQ%8W%eh98P<~Kmg$sG<*&2BQkU?|p|s(I59y#a y*DY)^k}>!^&Is2W%LcES|E~0yd!FqL>KD#=7IQ+8UpWh}mM6R|`uS}R=Dj~0@otm= diff --git a/sd-card/mp3/0087.mp3 b/sd-card/mp3/0087.mp3 index 5a6e869b9937e55bf44b71b216a34a220e726824..3aa821fe61400f0c8bba6f87cb75363d90643433 100644 GIT binary patch delta 470 zcmZ|JO(;ZB6bJBo#>Zd=uelty!asS8&DMY|ZUV7kPQ z_7m%sP9CmOw2%8L)=pPDJ9ujp9b>PC=^Wj)th-)a=j#+Lp|+lB2IpD{yMd_>Ukz9= zZ7XH=TceL&A+kbYC#NC&YnbTL^(Slw&+<(B*3Jryg3-2%R=MZ@MNa4{|1^Vj|1-f%ciWeiO|X5_Angg->H ihAY;x!YxZV`PSRNo1=z(=Ej5#p1U(F=Y=vq^ZWyp^l5AW diff --git a/sd-card/mp3/0088.mp3 b/sd-card/mp3/0088.mp3 index 5b86262767c641825a4720831f2d60a6755e7e65..ddbe4feecb81f55cb70808142c99c7c7208c726e 100644 GIT binary patch delta 183 zcmeA**loChSEAlAz%|6t-8JF@I|D->0|Nsy5Cg#iFbO1p!eHPVVC?Sb=@;+o>F4Vh z!C+`)qGM=b$pe)Qat-itj07tP337Dyjt4OrbPe=OObmcPA;pOUyGniU_yAW&Zw3QB RQ)2^56AK*9X@Kfx0068rA>{x7 delta 7 OcmdmO*lVzXR{{VG4FZ1v diff --git a/sd-card/mp3/0089.mp3 b/sd-card/mp3/0089.mp3 index 848b2ec63ccf21b804df80700e3244f4f8912872..aba960d695e50816ecc7e2b454e8c516f62009a4 100644 GIT binary patch delta 460 zcmZ{eJxD@P6vyu|%~C7P%!Y>A0wdnjvZSR~J|fHyM8R5YsHLT)sUaO&n)*1>at|RD zfk6&IQ9;rW)ld;lE-o$YEjD(ZI0gOT{M-_}nfn;&l#s!%PJ$!c>LPfXvMHbsL7&9)b(BdL+*tdkouDFTo1dm_xYXm6pQG(nAdg`?m4kYt3m1 zJ*Ca+nRlxYWg&!_52x55gP$9TCIYkCTr3-%i$%4JP!)Apoe0_gRMW9=T=*2lKiojf z(X1!+#Jsl95TugYOeAYuxp5>u8d5j|%hPTDwI*~eB9xK9xEdS}{`1{@b6ETW%)^R3 delta 252 zcmWm7JuE{36vgqJ$Ma~bT2(bzI$Ko2(#2w8wV1kCBw{gHEFw%a#~|)W7fahnlMX~A z8U!);NDN|WA{M)ajr*40bnpNCPBlyW)A*>)7#6CpLWgS*4w(vsYc>MIDd&2GIzQce zZuSb791ji8JP(BvF80Z9zTG5FYNHa-RAOuP-BoVFV_;n9m`4KjPFVF&U#9? zVRkUyZMjFBPa7)yN()VH4GC{7W`sX>GKLo}W#g?Mx6OlLp~X_pyzrEZ3o9eS1*5$A d?{Pk^&5jDI?7EZOC|Ib+%59g$v0YvjqJLpPW(NQO diff --git a/sd-card/mp3/0090.mp3 b/sd-card/mp3/0090.mp3 index 74d983cb77413c7e0bc5c609eca1091fe5d4e966..8cea9e8e66c5d800eb1b3232eccb8ace560813f3 100644 GIT binary patch delta 374 zcmdn0*sMIklu=-!*=$CiiI?UuR!z=loWnSEG7r-n#%gQp-)zRslb`d=VJw>5 z!as+RVX~sYlzPVi*APc{*N6-33=HcS7#LW97zh@CNgx3f1_R#!V|PbSzj$9yKVQcP z216qY9YYHXZZz3YKNo)nZ36=%9Wdd?1CiL_P7yyAn diW3Jer2(#v-V6qM#>PgLW(GK%+W^(f002ICWv~DM delta 166 zcmW;Ep$`FZ9ES0|$8qk?p$W=Olqs^L@+ZiOq}b58y@KucWfKKvw%eG^n8F0xC0Wr$ z$(`~^&z?cicgJPqy^2D^3j}?(A(+wD1U{FV!i}|F|u?2VL(t-=uf9sCIfp169HBBtbzI diff --git a/sd-card/mp3/0091.mp3 b/sd-card/mp3/0091.mp3 index b9e65d48ad74119c8db65b40b9f40f79567f8d56..f6261577df25b709f4dcc9d97252d285af4097aa 100644 GIT binary patch delta 466 zcmZ{ePbfrD6vpov#xV2z|2GQ@)`OmzF+H*H@Iqt8G|dz?W5LG8hK+Ulrf#bRW9igo8?SK-RMBHo-r)(?g^!naFqpnmv)Rz0-Xv{LhLaz@54Z{fibMEBtWAOv$ C4T^UF delta 258 zcmWm8KP*FG6a{eS@s!u6TK(e<7PE<9VK9(LSS=<7i^XD+h(%09o5S>-ia#+JG&O0O zXhK4TRHBoCg~9$7tM6NWIk_jf_a2Wo$BGm9EKM53rM6J#282h3p76#i&)|}Cox%aX ztyS)H3GW>8-Mr=tr(Eh5T8yo$JWLqOa->I?=Cd`&wO-+cUSM#?lfdPvq;SKJq`?C> zLw6<9C)-x6XIw}b?DIF}@@~Hnb2u%u*+?7wa5>{P{8_JgG$52YnsuMFvOu}u)@^PL8Z5F9$uEZUPaV9E_P7!S|5+twIRF3v diff --git a/sd-card/mp3/0092.mp3 b/sd-card/mp3/0092.mp3 index 3f67fe2c94dd8d709da07bb2b704eed7719fe998..23be0bb665c20047c1ceb2191c2873fe4c50a6a2 100644 GIT binary patch delta 497 zcmZ|JJ4hT+6b9gXCcCcgOO3D9wa_Nb8ksS(vL=la9b{Q|9b^S#qk-7iXkjIS79yll z`EBGLLdZi5BwHvxu-HOSL=X}iMG^vO1VIoRJMqq774X3QxZLxf%Q;PbeYu#V>!o%{ z#?pz@4(>2cOqU32OuP7|Nm_;dglP-^lxvvrG3}wzFX;$7em65v;(DXp#6m#Q7bHqq zd8k~$Xc^N8w4kJJd@IO445(SGoDIYNj6u)JV1M=NfmavzF-`f0RW`)k(EyZH#Ff z8_G{`>Y3(oRWGR*?;Dt6cot{>Z(l63k6-b5OvHmTmSg9w9((X-K!}16!l#a^kDdzk z>{Zrz(PO1^!`XB$YYhrLnQSpSjL?7Gfn0Z=Na(uJ;*kqK^!sh6*Lvf9kngv;GQ;jw kJl9+M%v4g>^`>{POCO6mwv`cjt7#f$oB7DPvk$}K9;!B%vH$=8 delta 289 zcmWm8KPbd;9LDjUulqTE_s5-c=g#jIi^XI(34=u`saq^&w^$fV76vI6uE+NIkP;!u zpYpeExbknHSXf95CI%_1?{|4k@28%P?x(J$0)ES#fncYg}$r?6V^!_~2d2^#?KsxPB><%_k-sb;Z1Gg-w2kF1+4<`fJ3$_a{`Z&Bpg7O3|Y%Em7F5X^BR G@c#h<={9tjK*lt)RtMJt9nfh=R39P)kcoQv^0NHB@q3;+`Z5 z0vn`15EbOm5D^4X4J|Daw70Z&-@_^J!1;0bzH>g#mwFM~i@|$oRKRWNmEZ|if#3|? zHiA2>+60`V-bQeb4aqtd+f5GJ1zh2^o!|g-9Rv^fBU#2>hky+XbrQt!Q8J3lT?9e2 zIfa}nPJ$9nxCjV8TxRl`o8SU{-DYM@@*EQ$A(d|r!8UI75bWcKm!OJuuYem|@DVid zSMm+Zy#%MI_yru{v!CDuSNhB}M?gsZIAFT*eu5f)N>*@vK)??64-zcmrDPC|p>k0( z^sF|c=U*I*)fi*#vSl4wl#sGjNkg5{W>baaY$~bcnWBt^mGP+SpPNffr&)+|HEbc} xZu%MBnA7I1fNVyaN)*gdE=JO$YK(IpT$=1`wQA^Eg7JvTm8im7yt~;vW`DY5iO>K5 delta 256 zcmWm6F)V{&6ouiOpDJxtt6C%$vo0dFSh|=PEEc;(A{LXyVzAMi#^4?zktS;BP$44P zK|+u)bP$UWk=P6ttNSfaZq7~KX6b8cw}g+%wBfGO5ni|k;hL#Pcwjv;v^d)(JoD3i zo_w=SbE{I>tvW(*{=RoO?RznTj~Cmvn@Q23CG6U$EZj z^y@)MJGcun6)@A}-02BPwR1bfbbc+CsR6X%Qu53U({g6+TG}fQv=@rT5Zcr3a!V8#v(@SpL3Ughs6(5VU3~y delta 262 zcmWm8El7g_7zAMM)493Lxj8o-Sj;BFRxB$^7Q-xdi^XEHxS(0b8I<=BK@l7{1lgPn z77Qxl4=h$I6h<PDD86@`&)sUw6RWsNsNfF`>o>x60K~q0As|i18>ME-WO37T*%)ZX|_U zX2!y#>E3ZMWw_y2N;u~Bc=%^FEwtH78#-Lc$o+FaxIYp0=d*?jK4*n9u1$tNqdD`< hlbmqRa$dORyW8dFl%c`=w6Mb~x5}l0@Wh`2{{bySX^;Q_ diff --git a/sd-card/mp3/0095.mp3 b/sd-card/mp3/0095.mp3 index b5e04f0ffcaa8124878acf53fdd281fa93f23a18..89e4c528ebbe4b41b8f7e89eee1d182adb69a841 100644 GIT binary patch delta 496 zcmZ{eKS*0q7{%|GCXG$vzpBxg4tD9HhG!mXs2zOO*Pt=+5e&pGS#)tRgOiIQQYsXx zJuMXPM-gnG1qrB7wS$6_Sj2yz;-95Uq0q&}wf8-o1P|OFm-F54+;bW$JfCf)%egj5 zeRD@lZ!ty^cm|Ormx66WZJ;B;xXQPB^A(E z!o=87e23vjOnJC`lCt>XV|sxOzs)nhqz@Pi*qN23tbQpfE+bJU=>&huY-hUM=FYqIB>lx+71KUOs+l(7t&#Kti#1F?(HUg=g)7BvjMqxq zLa>f$4XX~V5Ys&Vg`~=yskd7_3$v$TsrOYW4#RJd5Bnwz%N#J`X7<1U zp@OmbDv3avfb)Dmm+-z$;f#E zDjpa}2ueIkDDF9yRJ`)TSmt_{;E~C0#Vs%Yok=MU`I8dUx!q%5_511$6sIP`eDSxq G;(LEwaC1rk diff --git a/sd-card/mp3/0096.mp3 b/sd-card/mp3/0096.mp3 index 98e24d291b96f848724ea64b366475e4ea64d6e5..19c742ae9ae713af328cce3729f27252ec6be10d 100644 GIT binary patch delta 496 zcmZ|JODKd<6bJBohB3^H_xrJ6W8=~N`q0G2hc7f{dztZYzHHXi+LxF?A$ zY^In(sVO#W7?rbT%C ziYBq+XX-;+fT;)10Y%f84Kh6F8*0@fLfLO0<}A=3+{irl`hSm~Vm#crPV64zI40&}H`&QVgvbc%#F0jHd48=vKh zma$U76hmz#d%uRux&-bkmvL9&pSK))(CW5hcRnHFLWp#IbnT|p=ys|ij@fPXM#mz( z(TEijh7qbaLM?$m)1hcrzo?VatWOaQ{>}$%r_UNmEf^fII>Te`RW!)Pe#2~*QdW<5 YWd8Tmv8}L>jixlD+4N7`h2PKO2U$&+p8x;= delta 288 zcmWm8JuE|E0EJqVzG4UViAdi&>oZT#3GdtX(A#K zi8f&%O)7o{5~-nt$zos=-(8;J3@2}4_-SZs0Pnd$gJSMM*x?d{TLyhXjR!u1AC5N) z71pg6Ty7E$+3grS^2`z5INdC~@Y{OMO~1h<`&xu5Z>$9_vTuZ z0|r@UfnSHf-os}|IOa;bP-0J5IASSmu+F)NtiRR@w>xAT`lDW9H7a~? zvD4fAF=IRTVqVW^+_PpaGuvfw&3Hn%;)%7)$)vE)ucSeqYbjx#y=ggrGl{VV-ltc2 Hmva9B%>8v= diff --git a/sd-card/mp3/0097.mp3 b/sd-card/mp3/0097.mp3 index d07ee44e2548e034454a4996bd5cce792280d29e..29dd282a4cd327b2e864ab6fc6c8e2f83ade2d39 100644 GIT binary patch delta 490 zcmZ{eO(;ZR6o$Vuj2UJy{(l#&4c&XkZ#FJ&(wK3HLP?5^jg1YPv5>GqFQvYdNQ5-P zMiv%UQX&i4DA};Fv0>@EH!J1Tc{-i*zHjHEVWDZIj^0=6B@M4WFx_C1m`)M&Fg@bH zBk2KF3ez556*rJbWm-eISJFDpy-b_vPh;A~r{WfteNuU={7hH4Ry@FXxx=MzL7R z-irL``ETfDi+CyZB~07uGdrw-d!G9qBMT<0G-oc+4CSx?zO% z#@fK2Yk$1GTU2RUB5iY8x-6|VOQZW!;knah7?5{g}ZBm~RMCDk2{u5JA?J&@yD()$BY C*>J%C diff --git a/sd-card/mp3/0098.mp3 b/sd-card/mp3/0098.mp3 index 4b588a742202f15337f938363c1ff66837ffbe7e..4101ecaaf779598ac3eef7aca3f6ccd948e1b21b 100644 GIT binary patch delta 484 zcmZ|KO(;ZB6bJBo#*D#??{8VKHuQ#>p={>ig~p7RC~RgHY;0_-EU>UqzNWvGdrZQD zg()8)%EC&CL`jMyrO3v{)_s$ea_as%oqO-O=dJ`k8W;Wa91lpkir+AuVw5c1Hl|%{ z*(4pnNVD=6Wdf6Sre#z*ByHlz!IVT_I@1Qelv`MEN?Jrs2Gb1Al`#xwG7Z3%C8-OE zET%g&yO{QI?~?R{F*nmT^0S%FkW?NZlq1z!Z#hgmn9gO2qdJdi4X1gMZZMe7^nh>W zJC+KVF5oScbd1YFYr>HtOJ}jwVZGS;MX-eF5Rb|ejF(DPDJf%`$DVQmW;s&_KFg)! z)gHT@L%d=Z!3z7NZW_IMyU~B`6e1~vu&bqFHKjt8QxP^B+V##zG~5{p>-|FWH+sBH z0oR}1zDQfQsL`}~Pl|~9cfZFlyYzungWev!H59c{QLEK;d;OYEEbc+A`|iNE75xllp={l zNPk61%Hm2=3^$;#++y{9%cpN|c<=kZ`!`D~efSDXg6Hr_amQ&mlb+&|d!8WTV1vsw zbC+|zVw+uo;E3me;*2AWiUZcoV=l)8>+ES#guFK=Ink^brxzCtb1SZR=RiVn#g~NO zgEL9RDKjmK2OgRchf-pG%n$#7Nx diff --git a/sd-card/mp3/0099.mp3 b/sd-card/mp3/0099.mp3 index fc8b92d369cf7011c32c75079b3968fa1b85dc1b..b5f6748fe1193c53fcb1c408f376318d4092c98d 100644 GIT binary patch delta 471 zcmZ{eKP*FG6ve+=rL;w<7Kx!|G_U%Hgu%lLQmq%M1WO}fu~<5YL>ROe7`?5eti@1CzzV=zEPx;w1N%n|tm(x$Dt~*mlIW`kjqQN@bszGFT#}9Nadh z3*>E*&R|rqVoC7;Yj&nXv{g#VK#5w=Q#3NX#;+y8@wtOu+l842!9LHCW?x4Fk6{C_-MtN zZW<|l#F&0`2vHD1*i|X*m#Lt}WhKn;h(4A~C&rQqeOiRNW1XSisOyhAl^mWB9h%nN zSw_rWcyxIR-3NKNWP@wC;7E-g6G6Vj9d^Mlp@t(t}&7g{hJiN?Z_f8JgHJr+Od CS&WMS delta 263 zcmWm6F(||V6vuJDKj*kR=gzrvR~DNxsdJ0Pz+}0BMOFig#Uycq8Q0Ik`z3YLUAaO; za+#1~FiA;Slwz=0z5nv*_1^3IRH}^a6od7)YRNEPeHPZZ3_(l;!ZquG;fz!5azETU zH#&p{2P1}KUPQv5*-oL!U-y;UQS;46mr&uoTjoNyFv%ch80JAtnBjOlyuQQ@H(W`` zJLw+bh)wsF(@DbvKa;{CH+zMB4yA-Wwo--`=hDIt|J*O`_6hGC%@|Jjm=<}fx)J^>_`OityT z!#Ha)6BkH)9@iY8E^F>Nj58+R;GPXMvxEnvMv!+7W6I=pKvHG0H{Wc=caxv<&0#z? zxrKiY@(8x%~(88P>O*Yie#h*dj zz`#TYO!)CY<%3)Ud>kXe280ATI(x^1m_UVk{>Fv|K%kJ~#DPm`fUBc7gMprrv8jQj M1rFynKs7S}01IAnFaQ7m delta 198 zcmWm6Eoee<00eOE>6>3~>N6W=(O}(HmIc9VSr=T=V7CZM;>R%<{ExxHKodDV`Kig@{$dawOCb(hrF()ht^z?5?FNDi zJTq{pqS`?~_$FAzf>Gm;Nvofm20tT-<%{(XMay5?rF!L2!ai2ZsY(a1vbOm*6&* z`!v7o;_!rR7r`yA^bA)@$M*na5hLf9f?(bByvOA6Uc*YRNiEzj@2=~kcLWd`Tp~bl#;h0@# zi<`aP@zCd&q0r`HpOEnD-<`;?&xwBFf_Kg~R|b4%kTM+cASE<8n-bGx5ezrHmj+_Ib#&Ul?O)VY)w9{J;}u{P}QD+NQB4+Y_ct0Tf2qoTS0 e8%5!Y)lp%Oug(Q-lq{@%Pj;4B9^2+kDf$B$@Mi@8 diff --git a/sd-card/mp3/0102.mp3 b/sd-card/mp3/0102.mp3 index 671eb98f5bccfad71dfbe1aa6f0c78f9fd55cb2e..491ffe0d1b65e48c6c7c2fbcf98d27e62b9f8e7c 100644 GIT binary patch delta 440 zcmZ|JKS%;$6aet=nWm|kW;!+0tjL*vI%w(2L&Q7}QLq*p3Z$i_U5S>KN}fUSJtU~5 zA%}ya8f+|rAe>rS8fvbkweJq6zz5%t$NTf~zGDy4Q3~9xG>1;BPwm+B3HtaWyud>zhh2=g2(Ix-XyE1;0in&!p^hCl zK?@f=1fTfk;ldp+!6OF72|Cyn8kqNS=;4=-;12gD2(EC}Z{E}MbGX5^fSLaa4Qx*m zbTJ;}aDXpCf+lWF5v-###36&{Ap#i}!vtk~54RhNu9lUoT77jg)@6*@#S?L6DG+5V z3VI@|pj=Xx^EERSgR!EN dmPE{KEV+h_>Z+1wQfw|MC*~7F&TS5s*&o`{fO!A_ delta 232 zcmWm8JxD@f9EEYtP1Dp&^J9pX=0I$y#l; zxL{OJLrW33xH#ufYwultJe=t~mVZjEU2L@X41W7V_~r?OSEf86;J`BsxW6P^bLPC` zMMSveX4IUcXt>mug&7ylA+KVF4!7gNgwM`^%@yIEUczwBUP9=zk`#XVl{DvBO88`U zRq*LM10JLe@0_QF7hbLj58TRxf5sWZ6OXcC|2YG8*M%{6a)ut?azcy8dEu0?g5i)i O1);`jQD|^l?EVAHLS142 diff --git a/sd-card/mp3/0103.mp3 b/sd-card/mp3/0103.mp3 index fd80dc69f9de055bbd57eeb0a4c693fe8621d43a..12638d71908e3294d88b434c7aa3c76603f558f3 100644 GIT binary patch delta 423 zcmZ|JJxD@P6ae6ROw;sbnhgyNHO3QpFMl$cUiA_&Uk~wtwYbzALStZq4GmFp-0mq7 zLa;&lfrto)rjUY=hGv)cmbU2na0)tbJ}&p1bMN_?eUVS%@Lo-DIH-0BYPbUgZIm1Y z57=~YsG~kaK-d%3vFs!`#VHquD&D&YF3}n$=;4>Ji^py*reqJn1wISwxINO>;pMQ0 zXI_FsT=WrK;fIgIHSS6TO$?3_JYh@N!Hl1a-#`8R9V%l4H#id@xWjgULjyO11h@Dt zY+!Aipn__MLkV9)<98WZ8=lyA6ZUTB5ES8D9ZHK ZlIOoxQ`a&~iOelTV@mR$b@vCu>=%=!du9Lt delta 216 zcmWm8EldJ&9LDiI2PXn2uSSZS#gPnB%v#J>>}JMBWfTL2$?ehfdvrz#=Ny~P0JssD zV`?xslVcW|!D4g#DWCS;^So65cY1r6M>Rtfy$jc@Lzpq}gem);A?86sxZ$@mW;-eL zS@8{*eDs9}hHJtPf1O`EOPQ;x>%twMoiR_+E4_@N!>f#N!Tqc-Z7Wid<96Xz5iC5%u{H1#59Bh?bVdK!@6blw(lbQ}hRd zf*gvZsh}x}Aex$6YOSTU`yNh#2hNYn`M&er^BuY8^#pu1CIoCZ-UzO64G7w(*a$B0 z+$P`{wH|^?{E*zjs-2*TBMxcDTL-}jmU>P8NY-)3DI`NKf+u{G+{fiUf<3gk1=R4+ zP4Ixz9)cFWdxZRJT_HF_e?P%9wj|q_@d}yt=`}Mq2MBI(%x7-h`Giy#{pMA_k_Xrv zB-q7hK)?pR1PE4eb%-E~&Y*xeo(2h&>zUywR8>POswu7f>SV0N7_-Zkb!1UOj;+cW zv6PzDE4j3uQ_GBV9*#~XJ^$2_KA&eH&f{SVy3)}Lnvqo(tbk%coy%0rr!+?LF`2~q a=+dmKo7K?N4C9gUSb|R_yPR9=EVDo0=6y#1 delta 227 zcmWm8FHC{~5C!nPPyT}lA|{I2%|Vc2Az&g~oRvbc7;F}!o3N+p-kh7dLC_7@m~#_1 zH<`skV%R8VEgHSA{L0;XcQ57dt@bwlI#q)@Q{k0|5GKq7!V`Oeq0ikV;h78fk|z=2 zmZhk#^D!z6xEBlkaj$tEH+M53%=zy2*h~st1}Q_ES1Do2owP9KENy=4C?kxRUlu<3 z;7-`cnzwyr!_Lzc;f)))Fg4DZPxtfTSHJE(FII&+RtknQeiVdbw$_9L#*2nJZ;C=` ISX-~)A6s5pE&u=k diff --git a/sd-card/mp3/0105.mp3 b/sd-card/mp3/0105.mp3 index 4a4390235ff2b5e1ed0ed110a2165572ba18c116..4c277476d98e7c943475bbd4d7aeea0db51afa32 100644 GIT binary patch delta 466 zcmZ{eJxGF46o&6HO;h_Z(;OOVF5)YHQd;_|4-xYRNwAh0YH4X{ia?2qiXi0}Soa`W z5ESIl5KTf56hRP8EiElIx3qRXX$pMdK3vXu@B7||(Ia2p4_}3VgoDB>!8NV|!429C z1ebVWkWfXfh2RQ5nA?~&5)`q|#CE(j5u9SWRp&P|kB4T-9JCNT;Un`Hm)i(RXs}Ai zVcAMh!*LtIIlkE>=daoIMn}8eRb!Sh+9A39vxA_HTb%?I9O%-M>s=D|aNa?%i9gIL z7P|>{al|R1fX_~X6I|)h6U;8j`DZSDGT|oJ$9Lu;uJ=j^qT55J4g2DK3GX~RnDIWEJRHRVdTTAG|1p_L$RbctVZGsu}D0oW`q**4~>ikZU46E_;f-HDvEEY zfw=u|pVE>u>RcmWHl!s=g|v^29OH8m=f;Zd(&2?d*cH~05g`~qF6 BiVpw) delta 258 zcmWm7KP*FW6vgqJ$5URP>L0B)Sj;AZ#bS|2I$KO#x>(x5WU+`z;?>cl+#4YbYG|}W zBN9!BU}*=FNGuk+h0*;jpW)`_d+G}>^YJ47YD)sGy(u19f#R8=r?_L&6STQHq`2po zS?A8M;*!(8z4O*rbhtj^{4vjXkPvfrRPn)Av&r(9;)-4%hG9lEwja9N^Jc*r5JE`LUF^Hv@7@0f@5xE6o>pZ+dRrD8l1}s&e_i?uDLns3W>Z} h|03@$D+R>~Kg}w)rvz&(MC$K{rKbiTqCLh@@DIpXXCeRq diff --git a/sd-card/mp3/0106.mp3 b/sd-card/mp3/0106.mp3 index 9ecd3bf16adea3b2f3603e39e50c2d97dd24fc32..694f734bc12a5cad7cefb5b4300f0c09128ad90e 100644 GIT binary patch delta 434 zcmZ{eF-SsT5XZkWO;aR@8J~qhx>8d{qN7c%{J$1QTVFII2_b_1b4Ut1o!AP5nSVy ziNhHxLj*VYA-IoaGeHBVEW(Zt7J??`hjso4*6`5E1^qUH7km~x#;p;8BQ)7LRPe;E z=Px=4+W79^@Qu4pf)08{_57CLEv8%?F7VT(-&V&6syO2&SjV=T!zFHb2zvM{*ulm) z!6Sye9G>vitM}Uz1bb-paVX-skD!Z7lZ~>hDLFZz6uMT%T8uHXD4|n>0%B|^twj=Y zQZ1#EYFaKZDH;xh;xWg+ZC+i?Fux?t2Mnmr{ywW{YjV*D$Yte~R7r1zF_?)+x~4am YZG)_uBBz)X3`e5ULVUoxmHsjN1Is3U`v3p{ delta 226 zcmWm6JxGFK90YLgEw%hgGn-nPBcj0;7ndgImbQilTUy#%noGI2>3@irgDyfs5uns=QYn4 zg%&p=uFm_2(BnbW`|E7*DrU~DC1K20XNSkjLYqO{P-Q*t|BDIXgQJ9D&aurkG)?b(|FiA|;aM_qr zIJPM|fR)2^ig(Q=%;qv}qSmgoy|gp!BAUl^k1x%0tU45Jp&>s#`JkD^cmeB)f>ismrlW=h~& zGl|Wzv^C0=wvXjZ8W2(K7Zcnd0cFVv6BSvm4XZ%DxksT9`q7%@Xda9kXWG zikLnt_UsTMC4|V;sbeQY4c*Q}Fx=uZ{h`sIKNK`$!jP?vMti68kBx@<2StOFEsYt3 zTvZ1%KwdOPGl@6KhH#V;B= Bh)e(g delta 252 zcmWm4JxD@f9EEYtt=la%)68sWac(lG#ib<(Tx)5rrKQECAZmyph*FM4@IOc&Qe3{27AwUXUNqdxy6eZDy&9?59Xu7E5D+KQx;>w zA(O+x9qZ0-7UG5mf8xR|cSnRevkBpzjfCNV%SoZcKWE6wXs_l{=4vY?RJk@LTrfx* fPI#FXb~raKtg!7|;AY1B>&1<|EoLY7*~|q0m4;-o diff --git a/sd-card/mp3/0108.mp3 b/sd-card/mp3/0108.mp3 index 6abd5e2eea74866a55ed07e33a5e0d49799cdfee..e3511e4579a156e2e2622d24030539df455c456b 100644 GIT binary patch delta 434 zcmZ|JKS%;m90&0CGfmUWG_$FpX3g$2kD!LGIz-I#5Csb&f?8TyYBs_p2ugkq)%zu8 z7&PS25EbOm6hROX(cIGB(%S2A3jD(R@c8|CytmQ%*g+iJWtBs<{7UeI8$fV}l8xXB zFKir+Q12qx#ZTcO7VQLO9CC0`xp5HGFyBpZg5Sbh+;?)g#E^^N0Uw2@xYk3khc-8d z5?0+Lob(Vh@WaF59ycX|Ci;6xY&C@qOnEt6b@RqthJ|E#JZxb|?S57_=d}gvfm~LbO)dXj#b6{O d$7PY>)frc(QB&7aOpZiVS&gb4zPsICX207ed|vp73sOw=~o;)Vd04@=NIoHh7T4ugegbP7oKklq8ByzY)9ohhzWhpV}>!WgQ1;^e80Me!H+*k^^&Gd%F>0!A`yc~+>#O!iOXw|?+}-X zV2}_oG;9`$h{0m9*e#5{zv(1#lJn%`yyu+voEbkYTW$2Z-L7b5`-N#2Q^b^k$H8=m zQ-`7>Sb0p056vSi<}>Z1!Kr8iw@#*GBnoWxOLGsKE=6hh3+{i+?MwOy9JXJBRVzQcP9j+QhvpA_?nnrIeQxD!Xdofq1oabHx^YGQLSO@s#`!Y^f@F^jM# zr^l@5h&h@)5RaMt!IWK!CfOVbbhb$;z2kj_|BXg1Gbm(pOIM&(cK-A3Qf^uN0?j{$ Ah5!Hn delta 252 zcmWm7AxHyp7zJ?N>3O=%xjE+y2A54IxMHyggBi8h3>J&UB&cOYLA>)#5WdF{L~w&G z$}B?-B7+r^MG%YKqS61AUwM!B{+8Ndw2aTVVu<56;hK982F!TE3tOI{!?h9Nj$h6W zk4J?T=Y7K!d%n=+=2(*cIB$6#7$Po?C!JU4J$ELACcTuoT@tf_rqB z2&#Bt;!r}hg`kL^f+gHG6A<=UI27>4LU4tdR{iov@EMP+93IivMsSNY!8=@TC%8nD zjSGh}8$ljN?F3n@+c|9Fx`W^ZT^$5pSP}fdcqfMk{O%;U#=R~*$$(S$uR3+-IhQ{9 z72Lv;Zk@85Lk^$Z1Z%j`L$HBX4~GRj_Yh3un3o`o?_RRh*y4T;cY6=F%jL3?RMSdK z&AwO}t1!mQqJ)YD1;p4;A{mM)Q`&rDN=qnNCd*;J9F5rjZ8O^B4D(4+&~HF@=w=aV7;q;pj*()FkdsF(_FqmK(&?Wg^}lO8uUs7qc@Y>|tR{qW zemPs*>=n*978;It9SS#`@AH>`&R6aw4INJO3%7i5-f?+AxMGkpS2#`yhn!9ei+oEP zwz!@V#QdP}%S-1sYgxlHf3iZG%_0BEc+U6RIbUAL`{m!+hgbcmKNs{uZlA@LiuVpjLk&xWP>zXrtFg z(88w8fD2Un2rltMa1SeXf*OuH3^>MH2SEc1{R9vABiO)0r>Tcs1cZ-*`?x+paE&&% zsW;pNXE^5}sN$=~fF^Ev3GOg3X!^GV+nDwlqW|NHXv7?oM}^{Q;DfgFFBL delta 238 zcmWm7KS)Ah6a{e3Q`6M^lUhqlb4w1jyx8ol50<8rv1^Ho3J>}}yc+;&hn6<&D=A!0rdo;e5{9@$C=WB!;| zJWUGXTFT*;qm(dYXF-^9W)69g_W4Fe==06I=KiAa!XWGOo2+opot)6)XU^e?$9W-U zdCB)bm=W6rXZLTxub&lV>+2<9z)8tj|6W;`@y~qc<+3ngz2fk}xZ?j0szS(g&7sS? Rn$YC#iqPP;y&ZZ4^?yX;htsCW2G^mfXQzhkybOI0;I4FS(5?%>;xt zmw+uia1ms2%uTS2FKz)Jxb7kNL4S*KtV)(K<`uAy?_QF=g;s)d?Dr9z;hj%8F8K*6 z_$zsgr8dP-ySn_)uIkkef)jLfs>!2H_5VbG;1XXY%edJkU;_i)1ZljI>_)SPU>v`C zFsGZwygp+rJ~7X_mf0VP C{EPJf delta 262 zcmWm8FG$025Cw4FPv_<~=jQ(Cz#`a8hOJmE1Hodktgncm!DP{(c^hv7;rkGAFti9$ z6gTFAK`b+vEEbDt#h^C5Z~1WC9rx~~cvz?v@LMlBsQxNEatFc_6o&5^KQqi2|NjE<KJw`B`#X>iiT7ef zqnIyYYDHx!Q#md*H!)a-Y0I&@tv0*&&Mibt2$8H?*JgqmdY!0<6Ku2EqXUulXvFFj z0a;reXpr7N*Pdu=r>K%LSe-!B_j~WMoerxnVbI-WwS))Es2^)OLxGT#vV5r7^WUpu UTVWwbJI<4(%d>AG~K08|~t2eTc-wheS|8bWjl&9lErOrKsSd zmA~6PEou=eg@Bc+IJk7_QrcRfMd;$v$)$^H?+Z?X2kwum*Ls+BFu} zP6X?mh2WUcfZ&Kb0gX+%w*=8gMImufThw^Xpgg)+euAC?P7 zd0eis#gPiZ883}Lxn3#wNV`ho8^2chH_ucHP8h23H@vG6?69lWum3SF^Tl0lti30g z=ALnaUY($i7j+s5F4qeh*$|W9Y3}*bIDf>JI2Q}gJD!_!dfcJ!A*J$4sUq`CY!oz@ zav?HayvOOyj%9kY8D~h@_M>Jy)fTzd9n5z3t0v2eHy6l8ulfV7*XIlu7UTw;r|B_2 mRV`L)f4iNuEbGC?D82Dc;OR{IoTjw@z=V+O~1h@dmDvo-dd}iZ4!>?xCRT{b%k?|1cXbz z1PmUS33@rwEZneUt#B-4Z0kED>~O6`SYmfrm}5C?P~vn%_~M`SgIldanSD`%SKddx zpXb|zclu-A!hTFRV=C_5->lnQX*bq*hmhfkHO=vaFvXvQL5l00LO*+w^3^7mvom~5 IW|>a9|M+ZkrvLx| diff --git a/sd-card/mp3/0115.mp3 b/sd-card/mp3/0115.mp3 index c64c05cb5c13d3bd6a2fbb4b4c114e8053c0d2dd..66c867d335221c357c731866154150e86c971615 100644 GIT binary patch delta 514 zcmZ{fKS-Nl5XRq2lSY%M@xRdo+Qr2XK22;RD)_a2sKmq%QBaq3Xoq&`;t)!~4k8tV zDvyKm9^xO6Dl|}C)WJbfN<~^a6e+lL>D0x=K?m;(PJ$1-KaRWS-n;wKan(87O1`-# znwI8%F@48dV%kH%#k7SDm!?Hn516L$SGa%)H`5Hl9!Id{$-cm6k6EGz~Vltn4aElMVf#bPmFu^3KWaeWfKm&oD< zmna>>mAeIX%N2>F43x513=H0X`StpG_1^b8b~3s(gmht8P%1POPh5oJj$ubpWzi87 zInkupnkPZMp}_)UymZknz_O` VS1`aoSN%C>xt`%+CeLif_Ya;Rdy@bF diff --git a/sd-card/mp3/0116.mp3 b/sd-card/mp3/0116.mp3 index 76580dc2574fa7967ae0574ed19d57e1fe38d4e7..69b091a8701657cd96e8baea3a4248b2621e32f8 100644 GIT binary patch delta 506 zcmZ{eJ4j<;5QYDV#%LbC-$u}85e#y#i7&M9x?VNJWG_KMZM4{J1X);Yv{4pWuu?e| z%0IrbP%wppE`k=Z5k(LSwQ-AWtZZaqOZ{)K5| zd?%)3cpXgp*l|c&hMC4Ri@%CXm`Z2m_??n&@Y~6hM1Ka;37!>`m~%;WTbs#rh;zkb ze9K~5fx|6n2di$T8MJzs#&GYEbc=B>(;f1B(h+|_!pC%kNVcQ|9&*eZMfbd$_QyKEh+0hx1yYE4-{x`C>NN&a0ex}f mb7a_V)uL4w(ri_I?a2IS)iRBU(CX?#&B3PN2k%b59g9~oZktsA delta 299 zcmWm8El7i56vc7QtFP02&$;R53>Fbg1}$4y(P9xq5YvjqVlfRw5X;gw&k*EZEEXp? zQ4v&dK|y7(@+MIa3tq^%Sq%fTG2qqqt|?5!5-? zqc~v4c+BlyHA}`7ba?G5JTCVs+WawkEc?XVPW3Au`D|=)Z9s8K$1k|yppr5+fX}h z-(4S4T+o*g-0~=)xMMzPmv`eii^GB|CP!@f(zwUPlwzOXDM5*)v|@!5ql!Eqj5!uE P;(q-W=O>A=GEV*h*{*lz diff --git a/sd-card/mp3/0117.mp3 b/sd-card/mp3/0117.mp3 index 5b2ba0b9ff4e52f7f57c3b9a4d244d16b015c300..c9b7b0a3bf3a067d93e3a27ad6ca85b8cda3146d 100644 GIT binary patch delta 495 zcmZ|JKS*0q7zOZqqBhpV82`m;9qi(wl^3I8aLH?Zh~$qCiJ&fu#HEXiQ>p0SlBwDw z9lRf+h+>z36p9F1+Jd;WN(<7Zi;G>lSWs~CzThNy;QqLL=ibZxAOF&~)Q!K(JsR7~ zKLrPzf#8rqpWrJ$_%ycYlnCDO+_=N}Qm<_;(^%zRnV`UtM}kkhH16`PUz@hAT=1U9 z#(hpd7Oc@%p|QlZ3c(cn0)l@24rrWnHYoVT>Po>G3&zh(hBP*K5faRCu}a|bSy+(g zVOS%<@oGVq*T#LW)M#JmhS6G$D}JvP9Pw41SNQ8SPPkbw_`yWPoBT1Z@O6X60wYhv z_|f==ZlmYFMvWZvO@b6#p9(B~GahrYne(>mWbFZG^wh6ZK`B*gmdScigBcf-c4Gtf zU}hpcm`U5CDjMx-kH)RQy>=wiKcw0$tFygGCV1BmJ8sGzD?X4NwqGVEysdg}bqra( emPt$QMfpQVUB^x;t0UIc(;16D@ZE*G%jy;mE|%Z` delta 287 zcmWm3KPbd;9LDjUALqC`_s6;8xW!^I8HjGND8*o)B!k6rDT~QsImN{F807PG1Ep?o zDJ7OGCx22T14;}QrIZ+K^8GEZ;r%|%{S0pO;j7qhs1)yob1p!*V8j!yS@sP39Ig|# z`Q|*}a=m}E0mBZD1Hus}8-yyqohMumn!7!XLWx(-8t0mXJ$fO-7E2)^&s_h#xT#d4q<}box%XmoOhhf@*gSQ BaqR#A diff --git a/sd-card/mp3/0118.mp3 b/sd-card/mp3/0118.mp3 index aa5315c7cf80ae35bc3a3063e6cfba91199adf85..4dcf1e7c44961e7e0f876e7c6231b1ed703c76b3 100644 GIT binary patch delta 496 zcmZ{ePbfrD7{%{5jA3T{8UGmzHd3Vb{2gpOJkpr)5@l>yuwcVtvamr(*q}qv{fIP5 z$%05078bH0Y}nY?D8<6Y)_s$ea_auN-S3?5o~J;paix}?SL-A#u0AqtV4Rq);B_!v z;=my(1~Y+a2d|1TOeZpJp~xv|6X#B*1N0>^z2Zyp8p|$8vnWqyI>e3QF-B6DcHwYK zielT%w1g%P6XV$q)`m`Y|ppi zC)P5V-l1pN{@X0O;aE1)8C*G%?y#3*Z)nc7`J%XvsXR%K$jxWf{6ujMRsquxJ_;m7 zuvo}chEhK}7ixyuV|eh-W5VyAHY~H(=rH^5T|yiQArjS5+KE#^J;xQYsymF%@L;Gj z95VWa))1)B1NELi)4p(9w}Qq_=4UZqcu2aUzM@4Th}$E;^CI$ Y|E!j21cg>vU0c_n*Z)&@<~J;U0F#cG0{{R3 delta 288 zcmWm8F(`y_7zXg3kI(VlId{$-=d)NW77B^Q!eX&lObkjElu2Tdx>9$a$M(KNeC39_ z5`&Z*43cuifRvI&Da#G4{{Q7SJ?_ zamr6)iJSF`kX^3eh?lP7jMEK@Z`O=e<|Bd}dm9xOyfa>LzDZG};|bPz;3;-F8daR| zH7cgH8neHW&5CQ5ja5#@1vmVP+wNA2qRj4uVuO{0V1+YDoB1>Ta<5fUV}Hu#A5wPV zVwIx5 IbJ_R)0VTk5`Tzg` diff --git a/sd-card/mp3/0119.mp3 b/sd-card/mp3/0119.mp3 index cb6b376746821d5891645baa256e091131481a93..29e1c0c94cf1f79d53cc5bbd2c19f6167431303c 100644 GIT binary patch delta 505 zcmZ|JJxE(o6bJBoVq#*FYGSIb)q(|aF@%@IkANNeT3qq5(nS#k>C&ZLT#`Xb z?P9`TFzr1Jg?@m78SGLVTwID+D0PuSJ9Ke!DMHtt7cvz*aDQCRJ@?P4PXD@Hogja0 zQd6V$o#_Zqh-n`|7tR?*OZ?TTDo9Q)%Jen%_>|xqLq0^cCBi6Cu)${^m zKBo8hA~vzy#ngbyuO*@3XDVSj!1NNQ0Zq^GG{{s#_zDxFDIOr*ttIoU+u3BT$H~7H zVyfaGq^0vH%(R8S;tn=@nSLN1(R6~Z5od?TR~@~5T71{Xw2M3aOds%5Y+&`8rZ?!n z&h!zV#AmPvm{xE;pvCnYOeu^GIv$E6SQyftQ8D>F!eP@1ouXWK`Xq) zN2G}kEFxhriC|!mNGu|;x^MXnH#h&Bw_z{u4I*0_68NP@#WNS6xMS2&T(jZ`Djcm> zZ1deLbEQFX#GEVec+8 KR!4LfQTPY5SavP| diff --git a/sd-card/mp3/0120.mp3 b/sd-card/mp3/0120.mp3 index ce83b4dbb6c6d575fef2dc8972db0b5897af3517..e75355ac19b6ac0c62c6fff0f063c99c8eb98fdb 100644 GIT binary patch delta 494 zcmZ|KJxE(o6bJBoqG>dV@mm|s&@L_pdCyp_xb)dRL}HRhh!6!)K|x$xoLm%fak0w3 zEp6{1gjNSz$Y3axB8BQA1*KRhUFzWCu_jd5d(DoDZ@APZR z?VL0H#1b)mL(s!?fPIgqcd)CNHgPGw#cDOvYec+S{OWZx;}4k5aU<^HrBBm3x@(xe z;6yyYd@a)+Jbq0J$oZM3Fce@q!{30W3oHkj{-Cjr=>rO45%G|w0NbqNXo+O7TuL~(|f z;RpBu12+T_ElVP3n4n362CMfiPq^X7xwnPiiJdXzisKG`u_?T88Nvf&o=|7SbEt5t zP1s}0JmyBbaL9bfnU|s9GTR}v_+y@PJM6H_kxt=`_hy|7UBU&uh{GDo5n+jisPM+; zsKYl`W5NfM-NGq-v%%@OLyeztp~=mjpfi*Zs;ng(_BodnO8hgcEcFUk98Edf{FD;j zx!4zU!fA&S9;AgMPG*D~zM9uu?|0VE3`@zC=QPk|4-A0D3P`+T40J?z-&%(c+Z ze5FD7ZAO!al?F5(mN=OqMa_5Oiyr#ap-0XUmz2_$!>pBaaqM z#A_-ag$3NLFmzRFnOd%7dWM@mm+nwIgS$|$GL8UJuK z(-Uk}Ytak^SoR(XKVkkn(-$OaG=0Zz4bx|g)f%OzPSX;e)-lbYz1~#zg=@HX!Q4=P zk;Q*QScKES^a8&cH2ucxC8lj$yKH{FEiB+pP}4gc2bmsV@d}G)Yon%D*lRTT_*JII z@Q0Wlq7c$F0z1rZuX?|5AAiC#SPAq;U b(Ocfrq>gQ+R6Le!zL982o^tNg$*}qd2Zph} delta 348 zcmWlUKP_N?Vj_wG{m=7L!32i$N_Gi^XKISQ?2*Bo+n}@{+WNV2{aa z#VOwm*EwIK*ksHt$neN*{jpj_nO}xGT=s}@dh2ZG#<0N2dP|pA%+!WgamWFmO?dPP z@|^K2ifnFB+%sob;z*+)$M;5SE;QMEC!pBpSwM_4-mGTtx8W1lS`@GB3koW{3o4#D z)oM*oNU+Q8kU}{aww)KlJZIbN4dIBI`~$->N27`penbU-TxwT5u&2X*ePvkSL`-nY t-;4r7l~ix)odW#}&eCTrka~qwYx6MS6~(<G*-#`cMH=BG#H413wB3jvKqdiw;gxMR4%LY3eFqd|>_d><6daim%>!(WEed=%1{ zU`LH$ife|Scx}Jc25L24@OiBu&vRj`+6rqd^JYZwl}&YmcPtq`VWwW=8NbyFUh-ap z&C}i}Sm8>eMu}IN?EH`6Yd$_8m}RnA<2Kiu1$TMLrRsDQdQ=e znyt{lj4PG(y9eBKZZw&`ktFnPFJcY<#Z?ah&!<>R{sFAWw-_a delta 368 zcmWm9PbdRm7zXg3u?^eUurX%L%EigSE-p$d+GQ6Pr(Kj>mU41&aZ$oNl=)sFQHa?5 zYf5Q_h{Kem?68X*TpYOU;QKDWzTUp)?RhU^Uqg$%D5oN8mMdGTAh=-?f+C$3L4kV~ zg&d<5f-{!1MNU@=RvEA=tn<<;$Z*&u*yoRShYNOvMRr#SHhHgo2V2ed8FNCtXAQWpS6Nh&eWMcA-CX}*KUPtj(E)cPkYYgdciS! z8WiUE)F4>mc%vXoyI0|b+g|gh-zV7Ohc?5hCUc9gS@fQ)c877lAkA;T!U|_wOoy&k zGtX;tOax5Ba=>KMZDvm-sPwxK6g)E7F38i_VchFbxMMUVxME3r!0ArGJOg26jSmjE N5}XJJv+vkG$-o zz-&TOadwmGC#Hz$GeRDw4=8yw72up;nn6X(Bj;tBN7$!n8lQYjtC%=xI(Nl9UidXV zMQ07u8~hNJrmi zgd$8UC`GjV6DP{_3>EP%a_#K%s_*$hL_416R&CcAx06nG!>?3XDdm+=bebrs=iji zQ+`k@Jikfsn4vaozl5k@Q$^n`F4XB{LwDz%of2iQyqd; zE_E2KJ>4m&@wd9b*{y=hOm`XF-XO&I;%pM;>w`@8MjpKT^yN*Fie*KaYk?{;b43;b~}=TCl)I8M!RoEF`>9yL|;xVZ{JCg+V7 zrwXIRf;Z`;)5HDgk-_MHw-d!$=HvM_SiQ4SX-}xB~|cW(M7nb)I{5t=Rbobo#bn delta 358 zcmW;HPbdRm7zXg3F=PL1W@F53l#7#tU0hri<>KPv+9`%-{*aQM|VfkgLuuv6f&8rV3TtYT+wL}oHJ)p zC@@|p*kMguV6tA2W5}wo!DFl7h!YKhPkw1%xN1|ntG`jO$4hO2(@laREp~-7X6=Go z4mku*d~}$~ywjw3U4m;C{~dEHl=lRe#j0jGdizsb8D%fUCJHup;SY1oOg=I#2SCIb$ D2zQg7 diff --git a/sd-card/mp3/0125.mp3 b/sd-card/mp3/0125.mp3 index d69f5b7765b7c520c9534b486281ab08680c64d5..b0be1f444cc911611cfcf46f10d0376cbfd930c7 100644 GIT binary patch delta 580 zcmZ{fPe@cz6vpovon~}iHfmbdq+?1OMdpp87;SphLxyR_hbU+pD+jeDD<0Dt-$WF&EJE6Kz4J zWo!v&@vwqv8oo+R@32(K^a$5NES?`BP46*ov9S6yQxQesI#N}dUgNOJbWNRM`i|x> zQvsV{O^iF$=C(h=uUM!t+wEFS&#+U=^cbUOnU)Z!)AR;!>x}byy_vZ$%wzJLrcKnJ zXZnCO;VQfaqyB0ze|dg^X#!U+GQGezVG(yDnzr#b!t@!hE*XD!qn7$!quDanWIR@s zX$6I-rZu?DEIbgdAbXi<29Xv`!`NtH|CgHk);{jFP9xkJ%sQSs;Pkpfy8)$&N~v;r z63ZnDq+E)$*V*eNGb8C_ChZI<+qT>7o_Od#bue?QPqoEjiS`mQ*0J92dbge7(tv?} o=Vofeq+;yoOT=Rm(FZqzCs{q$NvT*zJaIMA(|N+VPmY__KTyTF^#A|> delta 372 zcmWm9JuJgf6b0~{QmUzh7q>II3LSkU)Vi1ePKq8?zBzQMMgpjEC zh$usww9$yKp_4?KNC!e3$i@X7MbuUobkhBZY?wj-Wl-utjjo3vHgmA=CX1nO&Az1&i!y6CALh z&2r3Ac;%lZc;IHc$;UdBJ}-5cDalTgafbyt9)%SOj7LOU(dIbcCD@`BRhVKXD)z_5 O33r*}-HY^gJN^Ma+?YrJ diff --git a/sd-card/mp3/0126.mp3 b/sd-card/mp3/0126.mp3 index d6984e90d8e0e1723641fb73e3e557f77832e4bd..2261e9e4a2a6b4b9620591f9ca65c69cd79a388d 100644 GIT binary patch delta 580 zcmZ{gPe@cz6vpp2InCru{>ikg$;KSoX!6Es2Bl4}ddM)&_z(q)K!b#6Y++&zAftv{wY9N@Ppdu*3O%k+m6D``bY15*6AFcuq+#i>7zVqFW`!oNtu$aa6 zQch!GX-n{svk<&tJS3=dJ)}{gw@l10W&QIF~UwOVmP~~^SHQqj=vB8d` zf_L0B+@RlS6aMP7nHP=;=6L$J{eicJE1XVfyyxG9RX#c)xX)adHa=Tjf@hpPDR{wn zQt+DVNsU+Zx^2!K!{;oY5@;rRH14q0BjH49dSs5*QupYjqGi|j#@%6W@=HXis#2=K z98;?S4NNQ#~J7gP>S!)$2@<;y&M>f rkGU5MQ}(LPIQ^r!tYa#9{X%pvsqeW3<@9IH4h{`u_K16TcUk=d;nTUD delta 372 zcmW;IF-QVH7zJRSWty33W@edbN=r)(xwN!2gbgh%O$N0DVM9{{E)oSn$tx)CCjt>N z&?U$)kU+2qDsYIFC|shUrKP2zyOwV_9`Ao#Iq^BOKY{mbTp^on2u_)T;GRy4pw5Ct zp~&eL!5N#{BGavc0)tkCJg=;RbI!M!_iybH_iPHg9JQPLowms3c7f31P*~%ML$Jij z4ndPI9ZI|I6!_`sG#5+%CR}<5KU}6E(Q;E>f=6)8A8nln-KKfmt90_g zD_G@9kLj`bOmogBc;i&B;F7P}b8h$*D(vkOJn}+Y;atEh{0f-OJN<$TM+VFf)Ute# MyUE4aHpgQ2Kes2A0{{R3 diff --git a/sd-card/mp3/0127.mp3 b/sd-card/mp3/0127.mp3 index d377d4ceac2855706576f0acddf24356ed86e2b1..43eda60cf8cdcc6035cd682cd1d29e2d8f262f1c 100644 GIT binary patch delta 584 zcmZ{gK}b|l6o%hFX=5{!X{KdOHfFU^CM8+9k;(!HWi`uktBP}8r7cLS6 z9sUCC9tN=pY%!2RNkNN*iXxC%MbO4gt3ceiY0s>`H+aL}^@2y7IVMjc8is5F1aXxRh%BwB*u3ySEK0Img zlr5)h=5^(32CagR{L^YQ=j>^l_R<;q(oN+!@5GHd{Egc`Svo8D#&nxepGuo`n>;6Y z$yma=ttJfK(r>p8+sbto&Wm>99Y+6ZqeJk3`3r&+ca?LTzsQ0Y_@mx{KeiQioU-FM zHM+Ext5j&aTG=2u;0@*`vV*y-H|Ds>zOJ7B-speReD21u)9JdYt}1e|{rQL=40*Sz r2BRb1^~{9bs<3<5O}ko&yVvRtk_Nt)aoq0SRDbev@_@M0`-h!>E+f0} delta 376 zcmWmAJxIb)6a{dOWglv$nU?j>G^?elA(xhhoGNH>X{w)w%y*fd z<36QtAAN#6SGomhw)z!TdFVHjQvvhVhql7?9)%+YdQImu?KS6vf*QYrO84CCGi^uv z%}=Y^RW4deC;VAvCp!azcg8|W=hQ+bEjcK-pet-7uB|dPB>KdPD!r?6 QRItIsuwa@EtuQmfe~?Lw~j> zST7_MW(sS9IZi@QW;7sJ=3+o$p57jTu%ex3J}7ubJETzHQb_QcxxL0M?FT*xD@?Pi zMzFxI+A^=~6U;JDt8jlA+Rcb%y=eZbgO zFL=swz0x_C8U&mCt6kyTLBT5HjSBC$+9tpBD3$yGI36JatTPou9OiIAJS1=098Tg-?!~9+IsJ54qNAeqi#1 z;0>cO!Fw*ol-6q#d}KviV!mDQf_8_(JuY|rTqr=sa@e}>-Kz2Mckj=QGmUBAZ9Z&T}cI@V|7ly1Z$4PWo$ws&L zhdqDL9jg`?8FtU7$IVhF_6)^SeU9VAE}yH}E!FqjwB__963O0Fa+h{*Z6CJ&0}Fz@ A8UO$Q delta 378 zcmWmAO(?^07zgmavl-jWhRxd_o41RTgI!!)R%(=si<8SPF1t9$1vSda)>n$(Lqf8& zB}z-NqNJ6}yu?9il2RO8Tpav3P1-b3d{aOAX*P9Z^W9OM-1CASlvf6I?T6 zQ^+z}Avj=Jo8_EckfG&J*ks-zIOcez@ke{f6{kXqeN}=SZ?r|ms|7-vOJS8;F2N%Q z-GU81xfPO}_Lz#lMo?l-TV%|u@XimfnU<_IhJAv4Uig&mneYof`Kx{4YMtPj{q+jx zysZ~Kf2zUkbp{j)+zkl6ITRGU@kN{CY@^aSgH3`no@lQ*)@+i0HJe+OTLkm$Z51r= zO1r~JOJR?Hmf()-Z6-scU15u*cJqNmhv1Z+kl=!ukkUrOf-9D_1)_%qx2Y%Z$_*eBMl-kf3vTep0nwfx1J5N5*7-LnnB>EQe$S3NqwCc=zvs{)!E?q_ zf_I!r8O+nI7rfwx`i#ZHf(J}C7;UxCAmN`~i8~`a-#EflW3=daZlBZb4y;5ht7KVL znNA&3fr@SiBkyIpoorz+pDpB_0V|z8+MMpp#Qr<&FLd=-O}3qB4k*Mo&wE|(v~wnS tps&|Cl^gVz8f@ulYi+k}J9Xh?bgQW6IyuX3X+4%{vpct_JG^<>`UmX)xz_*y delta 372 zcmW;GJ1m276b0~{QZ1@lrIb&#+E`2mT`UF$u~;l728*RjEtW1FDkL<=K>j!4kw`y5 zf<(R2LF1V)bn!?e(t(IrEDQ$!&+^O7&B?tdH~2m<-;I|{Od*pg3RW10;D#=n;DV-2 zVVnJBf-FngZ6?bFCJnp70T1nhJq}k0uKA_i<(xy|jGa!wDX+8#9Iv!8wkieTa+M&# zo@&d7YK0UNF2N$bHG(bfXpcGQR@yhW;E}0XL7ricV3m1~(#a98V3yz7EEnno=j`%X z`&*x2k`wiUCpsDwKDgc>xM#25`u?mnIn}82HGh-fj{Dj?hXR5Peg>4zOg9VC>}V0) zv+(bzq43IIL$JxER;y<$sPtS6T0P@!f+M;@f=ikqg#!D-f)kdsIVRf$YcwKCw>pf7 Q{iiQ5ljc}7%}~_&2SOs3Gynhq diff --git a/sd-card/mp3/0130.mp3 b/sd-card/mp3/0130.mp3 index fdf5f43d61b0354118cb862b72d851744d461ef5..24dfedf84829aa38c6e7bd0997e795831f9f2af6 100644 GIT binary patch delta 470 zcmZ{eF)Tx25XZkORobFO(ZoPv(=_k3UJ;9jm!_#cFG7N)k+4`SCQCao)0`N57il9T z3>9f4Vla?kAtDBgNGv9c#p=GsByq|8a=HKiF8{pokk~TdxsZ^sQFtQQ#uXsAM8AvR z0*_o0N@&#(5WX`@n5$KDms`Rip1TPSaHfu+j9<(h-1JB|#=d%jBHl4exYVHN@=E@^ z=OtE$eFQi7;*)TOtA1hz8wswk%)G#KlVtUyiQp91o7JW#p!}Bs$*Jap1h@Fje8cS) zb)~neg!iokd$`=DGI&A~c5y#MoN%O_pn|W=Yh3G)aDwff%2Q!3pc5wO#m_M2Ovjow zlh*8`M~JczqL!PhSS5qk)yg>Wq?xj_nUtL|XN9g0MD)S9?~giTk57s|O^ZjWu>HUK xl;upAbJYjaQ|4GYt420PC;MZDrfJ=aqxJt;9m`A$EgBm#^o0IT-2Csc_yH*)jy?bY delta 262 zcmWm8KPbd;7zXg3ALq{9Id{$-R~EB{;cl^5Zn9V`c8ik5WMQ!L>#@C05+!s$g^~za zE-9zZVo|afWU*Mi-{sTW`>&_d*r;#U@ewx+aoiR5xB}sc`ABHQEO_=0QjsM-rYS91y diff --git a/sd-card/mp3/0131.mp3 b/sd-card/mp3/0131.mp3 index d4fe56e2e33ce5e09ddc101400408a474e09490d..4912b11479dc07e42708cf80f6b12eb317208e73 100644 GIT binary patch delta 550 zcmZ{ePe@cz6vpp2InLy~OfxNWvT@VKLEdvx3pc&=Au`SQ6a{Nzfo*_(gby3@0F1qZ1RnONMhilh(%kUs~{ra|Z?Q*x76y%FTkCoH!&nOD7}f z~&P{V*8z`-0KW38j{lQWd66J&b5z#!(f*T%X@x94qt}3;w9` zysoTw^hj#UcBI%lpjurwmyJ+#Hupn8c+x)=9T*<+d-7xUX@YG7-mwnXbu;59lG{bY Vz|SkUE$8LB+S|9$y|VdP{Rb$Zt?mE- delta 342 zcmW;GJ4nJ&9K~^t$26yefTL`zFU_qKed!{NJ^ejZsJ#9Kb4kk8izo6JIRL$6J6#ga{- z%yg4rpABu9^UcPXUE!E#cC#FJ2&(+n*16(TD6ropXz*Hlz^N9&7Hw{Y9E)zjGKV~Z zB|do+E;;KJd@|4~xL`$l&rzR3iSIsBvd|_tWWq0aW!0~+%ZY%X$RF(n*V+Z=Oa_(i zybB7BINf1-oFVhD9TGfnIBYgA|IT$PT^Np-&J*n;Gg0G5RN<7lF46XN3o5K>cQ_eS ry8JgLsIkx^xMeDC8tQSuI2h9yhcBLg)@l4FTQep#+R)jNw4 diff --git a/sd-card/mp3/0132.mp3 b/sd-card/mp3/0132.mp3 index 2d32247c781d015208d83cf332cebd0f4a3eb084..3dafebea9b0f0683ffb59c4e9980b69cf1e412ab 100644 GIT binary patch delta 574 zcmZ{gPe@cz6vpovoyMHWKbfZW)Ukm!n!KskjNrysJxr$=AEKabbWxi&BD88VEeeW) zl23xXC((jnCYnewqCiL?LM9Y9wQ)0>+O%=wCZTf&SAhrakHfv+Ip;gKcy(^@=|%cI zlhE{dW}9gVQ^fQEkpNR3xqzlccoj@UzDIz9(G4>~W@9#2Yld!1o5Gaojq=^ckT>Mld)jd^}P|d-y7QsDOFHPRmzcA zC{rNUXVli17iA4-F9|`V>g-8fZ zkO&o(YKTyVClQf|#A32o7)ac^{BnnL&-u<-A1V(n_2MVnryyBJaLFkMuIY0L^4xJL z957WOIOn@|g_%n8wk=)G6H9Q+(JH|UzqH$2bStc~t6Ffy8|@RvYXp0Ac})F=N04T} zSFp=xufi>-eS$9rYXuH-+B}E-O85Nm3m!OEC%Psc5S+3QP&(CE&`kfG0#}k4t+(=lIcqJ)#%BJ0dw=5}FnQO49@Oy*1;pQHvcQm@Z z+-Q~On*@veqrA(7y@DI;YqogJ56y1N<$Z!DjJH_5=YEUpI@#*@Raxe>{TAzNJs>*& zv2u-3o8S|FwORb&>_Jht>yY3D-zXP3owB;+Z_4eub6D_znRbh3th5VWa^{Hpdou0x zm9)iGh8=I=RjE+98u`f22E&D^{BR*3Oc-xq zsHbl*lladaFPs@M-JaLqQ$-=U<&TEZxnQz7U~Duvottv0IqLO}_`au+zIdu`yV58O Ua>ncRj|~p>``es*eXH6010?3NZvX%Q delta 346 zcmWm9F)Ra65C-7ydsVVlEf}%>VLs8+0Ly%{( zNpZ}&G0$|f;)oHaV22k@#U-5ay@lbB$Q$+%*Yb>k1yL)m}N9FM*L diff --git a/sd-card/mp3/0134.mp3 b/sd-card/mp3/0134.mp3 index 15b3c01082c9ad1e9df2e6d0bdf8ed0e57b07221..d487a60502a930394515b5de208b2adf4a1aac20 100644 GIT binary patch delta 565 zcmZ{ePe{{Y7{}jdxn^#eW?I&=&7g~ac-K-Kb?8_9n6kEhhyxWu4|?(9#Y1#x9SREi zH@;r%eUerYt-}I^iUNrQ>0lHis7oDo>Ey+Wmx7-6ho`^?-VYD&^Y8O2{de#|AAKwK z8@gGnGRvb81~<-h88zV<6goM0OAW4<5kG#fkas+H#7VoOUqeb&lu~u_Bp&+|$hRL^ zFPV0RaueC1T-F&?IyKOp7&sOFubR(YxTLzYPImjqMYik1u6NNH^B0T^JLfYKHdQ^^ h>(!|~t#xebT>Vb1p6g_k?oH}c;$&h6-Rs-K>K~vVwrBtV delta 358 zcmW;GF)V{o7=>Y{RB5YP)l!O<#9}h&VzD%uSS(#k76VHclch@+1~jLG=6*yX#4l+H zB4VgS6GV!nlSGKrAdxUwEF|u=Jk!13d4Ci8?unO0L0^_n*&ufjX0e5UGKyUkpiAj4t5(ml6+ z!5-(@1;=y*lwRKonDw!s*?Q7exzJ(Sf}Q5(k+#aokf6ekkish$yF@$KEx2b{TjF$B z>4u-MAkTb{V2!a}g&kIVO>L@Aa87qba7HVlu*7&&Fw2@Y!F0dab9IS|#pu8?l7A|`L6K#6c!)%=KAr4p@T@*wcTSQa@W6+`~ z$lGuKR(XikNf$=WU-6FWjP@TbZPS**}@o-eM=Udd^B`?NA*|=3uVMV#XT)n|#{;aoCH?|2@+1_Bc zy=yRd!PAX`C;X$FFY`r~gw$&po*5ME3dtTjX@R2RM z?3H=t9D`P?f3+IC<*nUdBzg< zuVxblll0pKm$;@Zvb0~2U_5EU{h`s7%lweM%?rtJ$qW1;uh$=5ia1Wiahw``8Z%WY zG+m8+knZ*R3M2WxLf#v8+-$nDJDZCBS1lHf4>%pJo9V2g5L>Sg`a!>Us=8ok&^wkJ lu~Hkm1~MsEBXQK>wq zA_JpekU*gXiAaKkXo;esAZTf6Y3SbN;c)Lc_q^P#vHQ`~5Z=-eg>?E+u)_odcl20- z5;K-UmeDG~A>XxGCaaCMLt&RWhp8uP1ULNA?sLtl@WuXG!3l4*!g!rvgO*F-lG`r9 zJcr$)d!F42SDf>Rwy9oF;IX#Eaj(KDKfPvZu|e>`P@{RR(5R5(RFmM0zuG+4n*|pf z^eMf)>=UFo(;`@=v(;qVYc;ncev|M;`@n@Zg(v#k&6aa*o)aCW{_apHbGcLS%DygR zQCnfmRyx(6EjZ?8x8Q)0fWj&t1Lh>rBPh}nG=G%|DjYEy5-jmu8)vdtFhD!39Me{; MGSBPq3TMNPe@W_aw zifK)y;x^MK%o5XEL;_4zlmnWU;2vO_$F6V@g*v8X#4Js7cw;fGV(OrA{uVCcaZu9( z^wcxG#7AKTHx4nC5C~~WD215vI2&eK!MCucb=;0H{Y2AYlTh)QY0y-}j|TI~y+)=r zbVZrop%&Ft!<8ncSNJ2`#KIA#r${zydWbL0Ot*0Js7VgCXe#4H3k!!@nZ9F3Si{{m zEw@^anS>2t6<#|_hhOblHuD`!uW{-)(-yXbRa}i};oq2PvUGywAJUzs+jggM&Yoo2 zKqSue4CT0{`*6FMu3=X=f3SEP t%YHz9(izUon4=i`ChW8=6Tg10em|?{IvHj6^$!fD&ZPFKJGXaP{R3+px7Yvx delta 374 zcmW;IJuHJ!7zJRbe16)ZRV}Tm{v;Mt2Np}0O2cBnU^1{+8i_<=VX%ln<%nqS8wnCo zLlYr_q3KV8h<2z&B$h4`iNzujgZsI|Gu*uQ+>uq z#H>Xj%Xp(8&ARrKYfXYP`VEB?uMNR1r<={mFYPI}TNL&=)GD~-gEr5FHbIUStI`Pv zR>3?+ZGtPl*c6IfwhMmfY&R2f|4wu$9P+)x{AHt4P+-U*s4(wPsB+dRxZ}6B#@#N# z5u+}J3@a|dIv2am{bhylgR-=nf9RIgxR3N9^)aw66@M2u7$-2&XmTY VJP!vMi>w%l27OqJY_SkF{s0tmmLLEC diff --git a/sd-card/mp3/0137.mp3 b/sd-card/mp3/0137.mp3 index d213009d639a3e9af410c7a5ec656b68293685f2..ab65dd1d9a6ee8fb05f2a0ca1cb38dac5e99d9b2 100644 GIT binary patch delta 584 zcmZ{gK}b|l6o%hFX=5{!X{KdOHfFU^P##IDk5Rnk* z^taINVJH*97PBZSDYS)<5d<+1MAXJjt03I8Y0g9x+~TFvXR6!7~ym3G<$#AQ|Z9Z+a`MEZ`!w=;KZyq#w z$+knH{x6iP4BGAHzuFDfIDOdGPaP2~ab3B}t8rWZ8yCFe{88(W=`iY6?GU}m#4&q^ zSi*8SVSnd$T8B;L5{t(LuNm($c)*n|!Ce+k2nyU%e&@_d7QMh9^#=U0&tb=@IF3`N zPvdco3N6&!FYol5oZuY|#dx zA4D1)ihd|8D98v}gb1P_Tv{6XzUARKj+eXpna|H{P9czuDwML1f>$OXxTVt~IA_tK zP-Lt{kYhtzV5(Jc$e>kWlNGDrjPW)z+0+)8u_^3uqFr#x8*P<|4nc_)yTUPd?Sf@a zI|L2BI27t!bqYS|=@i_tqn3#RSN<~HcKsm=)k+{3(={Imo6Qe`Qa(>!F(9zy*Kmb^XW(Z_qwn? znbVkGWm0*s^u*QA94hx=gc)NAOxX8N^jazK1 z7Chlc;|eeC5KJ)?)yA2qV4BBbg7aLDX;gS6F8Im1oq`W68{acuqfOVZ8o>+RthH~o zBm`Hvl+bw2b9I6;{}`WfYM0;@JL@&R@=Lwo1~2axJYb~3-tf4=)*NrNHQ$Y&cx{h1 zHtrR?;mbh3$xisYNt@Rv_6dIQ(0;);zB4XzEUC>5f0K5G=>vip=9=v<)|zd0{Gi}1 z<0b2CJG;AL|7C}ZJp-!EagMYH6yuxuLC-(qo((P-8gx(Q nN9|S}b~*;KS;s``!inmwN`24GE2krKG?&e0wix&NX0!SS@;A5G delta 366 zcmW;HJuHJ!6b9hDIj(vI_x#b`@vvKv=Twiv zD=R&MJ#O?0(scUFh6|tRnf05V4{ep(eM;*O2nxLYcR6ShHiAlD-|H9DIW{18V@X?N z%vL(XuWd3M4+<_B88Tl~hfFm-EGW@EV$6&v95EUaY_g_ZVq#PG$lf>>I<5HRRM2VIN_i<2>_gE1k- z2p$IE-5C7CP!l_-F^UNWNr)OXz{JAB;>5z@;^2D?lL~9SHlXg@3ophoyHWO)(LL%OvG|MqH%|}qJjxF?GwCY#qbSt_1f@vy+HjQr_KP>pg6GsG}xn#J^V$5dz8?(8lj|!eM)2>bSwRZahW5)!a z7>!$fIj+srO9<|B)3C_WaX~+0NgWDaUaCoOG5LVSWT@o&-jLhtjjV)~swky`=9rqT z(!jKvNrIpuVvPi0Scr*~QR?#^TN4@Z`^@&Et; delta 366 zcmWmAJ1m1y90l;4^0m~bRMqoSs)@y9(8XeDA}j_BCW}NYT{>C1bP)-fLqhLA;x(wK z1R)|KX`6V3COQa%M7o$P7K_2X%Wt^%1~6@JQPwxMkL* zkYlV$aKMT-$7Ho2OUF{U;H4!v<)mBiz#r`iS3C*@_SXomd9S_TY^}+-yb8iCuOPvp zI^$cNLW&7nut9&l;E2cCD~|b;_RA-DXR1MP!*HWuhc}H%C#U^_b^dDixz;4eb0A>O zp96vn=bKHlC#bN?y`X6uZZXXt+H)?nnzyyI2}(TE7C7E6DD%5r;f0GGf>-u+3hr3c zmN?^>jem~Wwb>;&WHhAo_HxMlK)hQ}q}^lk*&d~DV`0HEE800Gdj%78BFeI-itaG; JktNPVtbaQWlU4u# diff --git a/sd-card/mp3/0140.mp3 b/sd-card/mp3/0140.mp3 index 2a7beb99a63bcedb71e38b8eecbcbd99ef54908d..16acdd46635c2772f88a5564c43bfa1e0768d7cb 100644 GIT binary patch delta 484 zcmZ{eODIHP6vw|aW(+ff@ffct)}+fYGqRbBn>1$JL}7zsV`F1w!$L|4<8L9~A@V4t zQRH2tSg=uIV`F1uAt@VM-`uQ}f1O{abN=VC7I+UXHqdiCAZa%K$h3knV!DRM#+1aK zP0|jGG^P!_D(+y?&UAq?honuMIhgj)lg@OEFU1)wI3?|)Dud}9cZx}jWHN2T=8`mw zbr;hCn%qo>cy>#=#kePx=VmcoBB6MJNVZf{Z`n*sn9gAuMWvT%2uZJ`UG(QNed62V za$ahGZGOsspU-rH(E_F;IDL{5*zz&mAY90Fg%`zBOcY6Vy|9>R6-SEmFiV(Be3VFs z*FN}Ej(F)D!lm{}-88!OHlz2!DMUgDVOOEFVNpSqEk#X#o8BH9h_=U~dauv|q3YUD z(EX>|6Km}hRhm{;Z6W6Q?RObwhu&u`=6A_J*Z8EZPVdQGWfxH;oLt7+&Fq1E{7 OLydL*f8L$?9Tq<=*OLPP delta 276 zcmW;FF)TxI7zFU#^PbXHwW`%N3}%x-4VEsZE*6QHECwQxh{<9x3SLeO{zrsG6Kx`- zgs7;bgouR2A`y#0ES9eR&+^L+-`)2;yp!J;!iS$X@ckEIkMj^77804Z)`%oKa{1AnyjV%hqMY?F!W?J+^ zL_fo;P~D?AnhOhN6KOvsT&)_QXze?8zk+yM#=EQuz(v)nx3HX z2-6f^8J>pMEDpQP+VEZr(-)jP%Jc#s4fD7h7U#cV`Qy`L;*)6A^Z{F~Oz$yyTwV-C zm{zeI(ex5-8`B-^7-od!i!JL2@YnazMw1*KG(Ii}`I zG%({*r98XO8Ay+%2GS`fqpWzMBX%Yh{BJv&K0l<|Ez9mGAsyP=54+x=bFpMFGVJsw m$D}pF&Y@FXam%tIS9&V;lX|X`R92^LTM4^spSU;oE~|f3Ew?cM delta 366 zcmWlUJuHJ!7=@kk`Dv>ZEv2f}#A52e!qBBj!(y?R3@nx!IyI6m5)lJ+EbV;>VbGxs zkst<1HGWzw9k58Gi-pBvF}Qbma&vNW?(^oRp5j|$_{fYaWHL3uJ`)gB>9h&%n71hu z7;6+9v#wcSx=E0y-!l27B`9*XS#ZlA&0TJ`n0&O=BoCS;F186yXtOIMxn~y~aKa%- z@y((1JLxn{ce|j%qGpxTE`=L@x=eGmLvYGar&+k}RH$>#ZA^YOE8OW4yfD&j`p@0Q zcBw~@V~a;2%R`T`je3p4yXF;By$VP4_6b&arWxgo&n$fVl;(QB;FTi-f^(KN3!L|x z#sdnEtOW%3ObnSHok2mF`JloDVzWHp4-4Y-hn4l!5LugKDZIhu Gu=NkU;*^5` diff --git a/sd-card/mp3/0142.mp3 b/sd-card/mp3/0142.mp3 index a33fedb0399eb043e22e8121f3314ba6163577a9..35cf0a876489b2c1f10a3325e5fdca550e3b2354 100644 GIT binary patch delta 574 zcmZ{gKWI}?6o=n0CZ;yAw$@gwv8D!GTFZM02?ZCQ`m8l3eUuieAg!Q_OBY>=5Qicb z1rvWA)O#q3e;_J>DpaXh5K$ZyB!jr<&_xklTwEMHFLV-m;C@`rx#zp*-uFjt9W7_^ zvyw9?SL%XAPC>B5ct}uVHDqv~-a5eoSCq3Xg#~3MA_fonJR-QkvGsx{{H>hhwWz^m z_BIJ#@||*lXEq3~GSqBP;e4~;6!*mhGyEJgc+7KgL7ij3)zgHEHmSUXN&XMR|#(-GMogGLi4$>BATJDs_dEsYuE7y-|128~+%!teRz6 zVO<)x8&qhz5d}Xp=nfSp3PZ($J8n7Ybe}Vji~Uy}D;^rQdTl$?*FZ78S|9QJWA5>W tV06Sin4bu?)?T}RIO}9>+fJT7(6rW3-*fYp-Jfx6JC|N#+>5Kt)<6E9x5EGc delta 366 zcmWmAJ1oOd7{=ke)hcb3s+LmKYGN@Nbg>vzgvDYpbuo}g7gHl?Bo;9!ZzVXNM#7*H z;u18HHqj<^34=}+hAt9`MFfMv`7h6KPQKr{Y>hsQr26r@8c~qdilE3i1Z6r*f-7cC z3MY)#2=c6I*O@d6gno;{1#=d`9w%xA1%7IGxMEdEv$sxg$6IZlQ}u!knrsT&%-968 z9Iy*ge6}m(IOh<&($yfy@JRd4F{jeCH>co~phIaL7o&C|3f4d&av2H*^GzayFxIZrUeD~h7l-qRgpFp^h zF{oA=f>q8yu)=6S@S3%N!2+Eff>}1zd6wG*6~=-_eHRpbXKAP4C)ZWsy^z5j4zvqi z@U!}oS9S^BFc3Dl&1zWS@^nP-l;0u-&v-p5_{6T=ex|OjFyCR&;I9tBE8f~8s58+i zSmcLJgRi{QC8%*jo#%tSf+~l*4W9E$x8O0a?(+@x81+ey==qsm!F&EtAMwV1gE{sd z5KMDf_2~5p#`vetXm|FY;5UyS5;XZyo#W-0pZ^>4r(HPg|CQ-CI!~it@Rl=21j~%Z z1xu{O4Ia`-2(GcIx-1`+pc{2pH<>tgw_LJ4XTr`olWQT%s#})Trb}zFrJ~2JDtPIf zJzAV9j1~*_q?OF328Xkm$ba2Z@%#mAAekH)Y*CDE^~W7=%)Z!qU}D@pm!I;t8uW%z gr;=Ln>9g(IMLoyPTgjo+NH%>kz0J5cw=P@%0Q~Z^vH$=8 delta 358 zcmWm8J4nJ|6a{dOWe+veOiMG89kidn1C-6Rw!m_f>$OXsM2N<+_GR& zC@|6@$g-}@GulMuN%d7Nv)hGDn=zyTX ziZ;iD-^l;@jkVc9qbnRxdQUANxMy-mP@yd-xMCrwu+KW{5(8Yy?Fu0UK2STDY zAB)_hv1&}}&_qqtXsmy_I7lRog9{8@oLE>~960xd$%K>KUrx^XzI)ESKQ66ZelSMA ziYZNZi*=?|%o9@;R*2~X${|f9cuh=>PU9>C!T5y~|9wb;K~zt?31xbu(3Pu7|0NO<@(ck7~(# zjxjCcjW7?tmuV5(y;@w$$C}+N-n{o5XaYlxm@zLmi-I?sgDK%)@$)NyQYp*};`B&U)!3DW#_dn`n?)h_f#xT^pF delta 366 zcmW;IJt)L+7zc3Qs6FUau@B1B=CSB^CpVQa7+zZZTP|E{iCxF9W}al#(0n z5|I?emDfX=NTgeoVz5{Yo}cB@)34|0`#itZflU8mH{Rks2J!fVV1p3|uIc&&=S=tv zk__bvwwYCvjOGav^c;gV9y)>pj^qoz_^rNjp}^==XQ5z+muiv|MS>K4euFcv`vrIG zEfzfUvDm&$xi+PuL~z5@zk{U)Cwwmzb*4;k&$e<~pOhQjYP3Rd#UFK^OO=8rc2yb8 zx~;M)lhxLO8iQSK)(DQ+7ZB|8MZM;9tN?~P@7;ye4B1PlD4 z-s8nxg4+x=TfF3av)~esM?`DBMJ%ek8Wpv@MNnf+U1qM;;u(Lnx~`kM1z*|QCaCaJ zo5dTRZx=l0U$x509>H5CJ1p*VwL@@=m-h-6v>9Oj1kIT*+7Tpq!3m$VZ zZgGWvub{{cHOA7BO34fSaWCr^*TTlsj4=&*G#2VA`dp8EFqHL13RC%!Lf$KyWHK|5 z98N|4%T5$dkDC5OB0W&25Z%g;`N3K5Tz$a!n0G2S<*r&dIGRo+G~&}Io3<+rd@pAb SgQ@hf^zhI&-@U%oY&HQY8m{;N delta 346 zcmWm8J1m1?6b9hDKULbQR<)F0wFV1|K^Kdq3>J&UWRZwSx){XLNGv9rw-S6uLtLUm z8<)7Gi6%%&Dwa+b2^~lz7K_E;`<5psIhXgmk8yN;2yf}QLONX)>@o>Kg&s>#X3kQ` zGf^ivW=)%Cs@`Zj6!v)SF!f}E;Eq4qL#{a$zS!R=IN_sKIMXE9q~%h$VAdsAT| zfMA1jZGr-wL4`~12hFvSknvSp;ZnOv40V{x=h}HrgazCD4l882+9|kVUzgyC_u3+- rZG{K^*(PzT+k6<0C~Y-jy2&2VDW0g{h`Ff3G7~YuENj{Urg}5~rzwpV diff --git a/sd-card/mp3/0146.mp3 b/sd-card/mp3/0146.mp3 index a16c26f0b3c47f06e02f80883f93db3bb31c77a4..589a550a8d115d3fc95b66f65fee3b11c66c734a 100644 GIT binary patch delta 581 zcmZ{gKTK0m6vpp)Qd`@KMFd0(ls|ESkXMSeiHlEtVpCgQ8k!UqG||PxusAtb1_oGE z@@Z1MCyjpxjA^1KLL4xG#b}I#2?iKgj0r3{xG;Eb!(_rq?k^|jo^!r0=T~|m^XM!E zXOo&LvujLWF+oh95Vn}!W8Ts<2lo)u47P=3l2{2w$NW%`J!a0!`uP51G$-sDrq znBJi?!t?`65l!DP*1+@ve}!c{KF+j^c%!Db_}<90fZHdGq`yfEUpATiLbEZqDSVB4 zEn0+|Pcl8n2jLXFR;D6$TQyz9%qdejbK0C>Mfe%lqne&zFUoR)xi+JlY}ev*t=+ht z=rEhZF_X{7Ox^7?irc~mD0MO2L9|;_0gK)2+p(6fY~V)EG$K8LlH<7}&X8MN^($3X zO4Z4ecvho8%C*RPi6JMQ8_%Y5S*NJt1Bt$PDi!+gw~)I$ta@#Gu&;((c>h1|de@xM s+Jce1b15@!QfcfTwv)C*?B>P5L9L$aWR%@MFqlf5OB`T#a(`I;16@kGCjbBd delta 373 zcmWlVJxGFK6ot88nWko%X=z`kDJ@M6xwNzxgi8=XG}$0Ti%U}wE)BJmavYL-afv`1 zg#Jh(gbXTz3Y_{O0+(p0p{2#4_gx+imwWFyob%+@Y;KZ*`8ZQG-xPX9hJ^0nG=v(c z7)%u;I)sX7DVCA#6gq@yValOq5xT%am)`tUEMwQow1;t<&?)W|>qvJCRbkj!U6t%Y zDNH%^|7Qo&57wM)%y+=kBXonR;vV2ZfGsqFBIOKxhTu0j5Q42lZrZ zNLO&J_=J?nbbz)gs)FLM_Kk;Fx12htsbpQYW diff --git a/sd-card/mp3/0147.mp3 b/sd-card/mp3/0147.mp3 index 582f95b766fe036127e4fde3ab1ca9025b0c8d44..401bd823ba13cc17bc4a670989b06029824b967a 100644 GIT binary patch delta 574 zcmZ{gKWI}?6vpot6H}AeKh{>Ov8D!GTFPq^(}0UleKa*DeJw3imuApKggUrXgmiGQ zDA@2hw7thrRD=j-P!v%RY(YgTNG1_nx;VMGxHx!k=p^*O{c$+wJ0IuV&D`p_r4xu$ zG8)xNL-3h%5WHb5Ab8GNKx2j8F2N$V43}9B3RW2pX)N${Nbr=?yRGuK;WF=qHEy%N zMevxP4C}nSM^I%TqVbT`h+u*zqk;y%MKwP1dQ9-09eV|DSvTBZzE$HDf3^y4@>ZK* ziGA&Y3g5MB+~vg%!3X{^e98O!1kX6sY3pA)t@6r#!BvL4G}id2%T`WxThAYcPk7^i z#shXA6m!uFL!sXzxW->S8aXZ=60Gz1VY}Og;W|rkjT-;P?d-}C%S^9zX!P2<=8oFi zV+otr66W*h^$F&=WmsbQm>|t~Qirm^9OwByd55KBsO6q$tHs4d$!QKQE*xA)&B0|+w!RML_Y%2m zVhIUlg-HHfrjT;kMJX2-7YD!J<@s9i$nw-CIOk}snf%cfxN28eXHT8rn0MMTC+h_zS{w?8+;j*g z*zXin`Q}u3<*ZBaMQ?-PfhFxThusRd{B#RexYQ`vV$>tpX4#`49QO)d_^Z9)T9e>{ zy*|@_@|nr0X2AmOeuV?>_)TXZV0M0JPdMMAu+KoN=tM`_J&ptgi~J5Mj4{(DsIa@; z+;*+4Fcnfb<6p?U&372%VY8tUHo2xdO?Fqr)WwMYJSL)o46E81rn>}#48@c!X-#sD Km$7B0Vzz(xnv+!k diff --git a/sd-card/mp3/0148.mp3 b/sd-card/mp3/0148.mp3 index 282874977cbba6b24b029493e3ed0ea7334e2d4c..1427f1544093dd359aaba6ffc7bcb21d1f3c3426 100644 GIT binary patch delta 566 zcmZ{fK}eKQ6oucJM#p9*(@e`6ZKj}&A~WMS2yObypGn4K;Dz^bdFR~w&U;q)duCw((Q4je zsk$Nfz-b8HG9D7V;Bv@fk^U~h95mQ@h<%C~9$yOHsiLPsBuLzr-w_^GaN>%GN!CSF9=5SZubq&#h*`UC!j!3rbo7Vr3=-Q_>o;SzpVuJP&tt7^wV z!7N`YhZuAUa{SS0F~Ye+f*U-3Sg^%)47{E_1%y>Yj~m!D~(*ahu~w z*Doh6p3qMTZgW#P#mZ6J*!9y~41Z(l*nDNh3;Z#!;7@#x7*jLGg!O3Mt5c!Z^(qDZ z1#h@KSsE^vya|)Z=6W-O1F`?q@$%4F)00l;dh001xAjp!81c^47mSU1r;C%WRb$YX f?@wzbFP&=I$r|`x(WLvb`Al{&yF=WW?P2o|Ff6mU delta 359 zcmWlUJ1hfW6oq@L58A3$wX~{~#9}h&Vlik)ECv=ah{R&CuvofSEND&#&41H)Bxoa* z5RVXz5Fv!277+|xBod3oLgN3IFE_d8oO|xy;NHMW4F91wSsFFHOn0GD(v&aD=2WGPLN~VC-`K^r*O=U}v-7 zhgX_UoQRs=f1~DNw#9T##FWk{#{@T=X*J27HZ$kj6wa883$|I&oMWn8Ibs&er0tdQ IjwKZS0f01+>i_@% diff --git a/sd-card/mp3/0149.mp3 b/sd-card/mp3/0149.mp3 index 224532b60a41696d59bcf19a739a74d56aa9368b..b2912434b6dc266e3dfb4826ca7c13ccadd3d3a9 100644 GIT binary patch delta 586 zcmZ{fKTOk65XawDN|9Cs5fCj<1YM})L2ZS&_(Oj|Qd)i?G(i^}Br(Q>#mSh|0ZEvI zd>JI~LP`{pI{XIy7~)VPEG~)=1{N0TAPVsWz5NBQd5O z6k|r{wljUkk(8*#ciA2zR2J*iMVbFvo zNSa14kkl&{X*>prbTL^h7K8gQzu|KC-Cc2fIleK7&veWnoqiVVG6BH@Jq|&E8Hd3! z$I1kUEUCv_EEi-LbQ`30-|RWsbIvs?P>jo$WD zZ@0`g2wv!FG&;54Xzw2h*n|(Yz=bA*O9q;4R3c@%aWSoVy9q@!KlF;^HISBC%eQs PQ0#M!65UJO?Q;JEV#Sy( diff --git a/sd-card/mp3/0150.mp3 b/sd-card/mp3/0150.mp3 index ec94b57d1e5c07a9ddcbd5437300f9547268f1f9..72305529a908086c97b46a99b8a6854d4caa8cbb 100644 GIT binary patch delta 496 zcmZ{eO(;ZB7>3U`jA3T{Gk(T`jm=EDWBd#@E^eB}jGHKIY}nY?SlO`9lv3!G=zJs- zArVSg*hob3v%tp2#>&RVN;!A3QrtO5q$eEPnYPfA#Q{t9e)P0Ff+77kJOI-kQv2T17<;(D8Ni}si)Qn0m+jm4b{*4E391~vWxNaKVdb`nk?+{{F2$8Ce(rS_l%1$b32HW*;Y#@h1^;|L(gCvqSGo7Ib&%t&st1RKcpw+6GOje6S_` Yzo=>G5usI8*Mx%g!GFFx@q1bP01YaaasU7T delta 288 zcmWm7F(|}w90hRi&)@OCbMBlw?*0~w#bh91uoz^y#bUQuEG!lVU75I^lzuNMkxMSQ z(vipwLK%=wBy|Im#bQ!^|K-!`_3eFkhpK~X{mA4842t&A0`eDHHz G^1XlSBy`*W diff --git a/sd-card/mp3/0151.mp3 b/sd-card/mp3/0151.mp3 index 36b27f227cb829b2e7db146bbd4f779db8885832..387d978dce3e58a445bd7e38c7c61491863a3bbf 100644 GIT binary patch delta 565 zcmZ{fK}b|l6o&6VIgQP{OfxNO)R}?^6nW$5P_*e)k4dJP9-?4D*djL(NYKUzrobX0 zEc*?d?qLWjVOl8Uq6lgR8(F?#Jbv|NNY@nEiX=Za<3NifAV!#Nh~1h*LvYgG9nELi67X2Bc&GMwg}h{h{+ z)!T%3h9!<|u?$5uF0mXHT<77K;0wRTG~V;FBUoY6R>4~?7|t`-pmC2s8U(YP+$LCM zvQcoKD~&e!Oq1Xie;dwmcDrS&S>pjeHVdxu!VbX%BQ4r^9<>N2d8E~5emAV}%1({P zY~5vdTQWS$piOXqKijmqdTO`e6Zh}2SHCu#=efAX6aI7A|x3=Mk6a-;TZ ij6qK(?HVP{AFbcW8u(sLxjnu8eZ6USgLSX1537F!d9&95 delta 358 zcmWm9J4gax6a`?8We+veOiMG1h;fp1Xs*k z6!MJK2{No{b4=6=gh8u9jb~Otg;NcJdwy%P+-g*)a-hjH+-UQhZ8lnL3aiZ81SyW# z1s{C0D?D+*A-JQvMex9)w!{gi!X7`Jf&;F#3f>rT2@ZMTGR@O&!7hKaIi}l;gC2zf zD;~iH=h_8HHhPuL?0W@ij`~dJtM;7B9SSG(b((F*+Idd;1;hOEEB$pcAb4kgm-)J^ zEpaBOaLV7Hxn#NpMMgskm#l^a*Npd=<&LnR%zRksd@Lf^VNJWpM6Xy^982pjjPz}y F@DD#Ak`Dj? diff --git a/sd-card/mp3/0152.mp3 b/sd-card/mp3/0152.mp3 index 0ba392d5f8579efdcc2115088f19a600411e201e..97f43f807d5be3ac89fdeaecb750cd0411accc42 100644 GIT binary patch delta 586 zcmZ{ePe@cz6vprO(#D+0G}E#s8`Iio@@6K7qD`-Qn2a+%#DS@eEo#%oO{*Zrg^OB* z+E-EDlbHR1iI|AMOu|J$xJkk`Z6a_{n}Tpr8yDUCa20sq{y5xozQb>}|95Vp2R|n> zQ_jMl$rZs%jzF-?m?L<{vZL{g{uaR#t{N`0R3*61c(uk&&Q}X&S=cI=;+o+djz=^` z*;yl);F947UfO1}POZj6PS*;GJQ202FHwyJUWp05vthenjupeZ%+_f<;Ey`NV~*{x zK5g}aMK0EBRCvBY@P>a4E4;naPLquqWBk}C#`E$nYZ7VFxWz|Jwm;b{nC91zS9fdN zWAh#{`>zazL5tuyf3;}id1J5ODG%=x6!_lI<%PJ$XRgNu-*|t&V1}7iZOm6%1!ayL zunw_=WjSHp{5HXBt{TpGQfTMc|KtK9BXPs&XvH_;v42&;b|4!+I+ delta 378 zcmW;GJ1oOt6vpAaf9ld!m1-$ft=nQUuvofuLSktmCW}NY7L%om#UR?e9q=6@gs33y zsar)PZV5vNu`n>0gmhpr_&&>%lbmys-&gNaPoe|A$<8_Jq$rsaTyPYE7dkCLj%iEb zn9%~k0bjK*j28-K7_ceKGh-9%GFBv5XI{I)>0*Th+e!qBywN^#sMP#gc7+`-*agGv za+t3thr%7loPuvwlnJi5rCnvWOJS4mF2O!0%1uw$EqGwot&rtFh2WMy+ALF*X4~#j zNb%kyx^tw;loWdvmbmOSbHpdu;`6`b)e39$)rh{|({8ZOFF56gU+K=tTEQV(>jXo* z)J8ZMPljAw^IZL9{3c7ttmNHzC-jjhhyoJGctGa6c~R{O6qi-2LQwDxaW( zLbs-TVVCJWCWvVh(Ew8k#ek+Icqf=vu_v5Gu7YVEv7n~M*a$KeFnE$_0e^&fJPB#q zLVK9$0X_*|V*C_Khd`z2^Qw}i>PCd=9==C3Wic6LdW+gB!;)|nscKC{{HkW!#-r25 zNlOjWJ8aizTEyL2GwV?J3{TG(hjyJ7&6hf(xqsH^L-krTuj@^=r-A7?eh6nUeNI+H zL!Kj8U&?wxXj s?0|bKHEf>hs@2(_uoISL#m8=jkCXbIn^IP1*LBBsoMY@hJStZI0PR1yh5!Hn delta 371 zcmWlVJuHK95XRk0wc4sywUk%2+E`2mT`ZOkQbQMusRoHyx){XLMFb1=R6_oziLks0 zO{kAX(n?6g=hDey=^_#q3xmP^m)~&r+}-osW$I&MbCiDb2}PW*Gu^{ULYOPQ(qxPD_HAgn#53_uG|XC z$e4;6_%roScKcaeCBllHP!BVmB0He#?g-O0tcapIl2LuJAuJ&`$W(+GQDLZ4;Z=zqVS9HMPa1p}d$hR$P4AM+q_MBQ#LER6!RhWN~SibWqSi zH2oE^++%4}#1zb+V2eTr1u2xG1P2jZvJ?jw7abft_tB})3-{x4&+nZ3-Jhwo^uuBL zT1slVQ>rm7qCiX=2>F=aq2kk2hI4?4Q5Tj`Y+!nVaHFOY)*G2tk#AzE*h_?k7n#d0b>Qwea5$-rq{R?VqyCsruV3NOt)xS!%hp+3*2rs zCVg$@m5nwnoN6~S{|M)?*ugY|Sf>`}?M|iz+&s**41bpvKI>w7gbUqFOZXwYkGUgS zW_BNCdW<*1mvDQ`AAj{|N!~lgG>X&5jqOL_CtM9{+QPpuQx!`mm?}v28s}QC$rMg9 zeMcx_l9h-Sf2WVdye?ct@su$M_iLKNdcSUZ^;gZ7R&Z_L?!rKzXuHmYJ?2b)@heqT zN;Sxqwc=4A*WSvw@i9A8dce;AVy3o_$`y z6Cb?>IZpXRt*I1TGyU&imBA(7s{}`!tF|RkzrB+48+D|{?)*`gxK=BeW>3Iq^Ibr& z$%#6_0X;#Z?gRzf?5`K>@mXExY=hCA^^JmE9;+uD3fVt?hKx=wHVKB<)og1o)jUVT z1`qrV3(mRTA~6 zL%H@H{=rVUrjK|~VM>36Im}ivZ6i{pWo);~SbuVvX$t;oO>gnOn&}g|YnT@BQ@Dbs zwOW?eTw$8Tn(#Z^I%D*>PSY2>x@t5{*O>b7)#F%5(=YrBG3D{@y4luIZ@#f#&$Nv3 z8%7@t8z-w_EnTO9X#;uT8ZtMHKGdk?`eq~3bEI!EC2%4f!}M)rY}XmGd!6Auzfw7+ zRDnE+MUMh0dy#b8dhPzyXtF<*w1<@yjkQENy8>sr=~T~vYPPIci-%P3v_I&$346p_ sFf?f2kB^#E-LYB+B2h~s{P14!c~aN0TE${A=wh*SLSktmVz5{&3yYEwLH1G=e_5g`#pIyu{DCXWK(RL^tGV3tsxKnV?s`iwdE`<*c)d?>6s1?rF3wCL_6>hli7OZfz zL3HM&K_SOwkKmRqje=)pv;~egnTeGq!3o!z1(ytal~r*v=T*pZro~+SXmi|Y6%;w# zru4I7n^}FKU9d%$PvMNmKEWNw{DKT$wYOaDP`atVQ?Sk}?K`IeX3@`pLWvt)CNtPA zm}dUpIa}e2zqX*ty&lso8Z_T11qIh!>@|5$$ZV1dDP4_)1rIE1^Gx)ayd6<`dNv~1 PX1rf8%c^#Xn*;m}|06 zkIFJ{>=7(6(r9p#j~fM7cqS(J#*LU!-i`~J*}B(WtcJ`ssS*BYvKwZb1vPfJ2tIMO z#o#k9w+deJkMbFp_6aH+XfwFSFKvPcytUs}qU}bzPum4=d9FiH<#%O?cMcfc*>O#Yac1a;VGbf7iQgK6BJMi6`xq zO48sR{cf9YhAbWvyk?@u==EBU;64k-1y{MH%y9k$i(cSQdL#bSTGVl>j^otn(^w9v z&~g~LAT{ER=BIO``J6Z9xTiCH1HuBe-Ehd(N$H!5PQB3hR9J z3ii0vV>&jU(yvE8L4^sw;EJEx0#^e{@AUTyE_tCXa4smg=1)-Jf$M#u9ql(4RTbK$neM3_{(>s)fn@BfkdX1kAOm{JN zh-nF(jZArLG-|q!8%<0f@JILr51LKR-WDz0&n-rO=P=V00RpC6mc9z%uZrAh{3&%|6*m0%}Yzj-b8PSsVZ-l8ID<_y9BGsYk7b+br zJ`*RIiU>!|8;embdZ*LewIf_d?iAA-M7lJsVy%nmC9V~;5-d7-+jMmAcgBUrYvFES4q`iF9BwF<2}NCX2-)Bs8yqoD(4;Q9~1# z2pvdBTq-0=5Mk*ekytDS2IpU%<$J&PJm>7h-UgPsQ6G;8lJShGvqknD7V^ zjOHp6CQq@$kXNw8Bd;RGp?o*_ZQSN!fnbH5g^B}S8BaJ?q)5`^6YO!#rx;~V zu_DWtV!<<~{EAOjlqhbPG(NDuRB*wMQpEyi%M|Mjmn$}SQZ7&qS16wNW8CF(rR&)h z5PQ81xcTua#ViY|#eQv8s~PszC=U5}W#F)^@|0gNm{ z`4dxo7eZ7Zm{18sLmU`%F(w*A9E^zzi<1K`E)3jjm`u3gmzTT$z5m@kPJi#4yFtOc zt!XL0$MhLv#Pl9vAJZ1reVP{Go?@E8zOaB?8PhXF{36GWpQ(tU(@bCSSD43aK#Q`u zoM{7}gsZrJ#&r3Dnx0@O$TW$YA(q}BAx)cj7-rf(f<+_FmXYP zd)-B*1#AnK;nkZ>|I};gnz_W(gX@~a`g>R5+H1kK|n#QpcXMdZzJN*g=@faqW9{Jmq1nAoId{ZdrchvnJFr@T%thIrO0@xZl^apn(57EoMDwnSuIJ+4*gdh%69duX3J`8DIps^ yt`E3gpEFYWU~s_cOpltU2wVH@lqC_p*HM0w)N`G*vRadNBH5liVcf~%W_1K?I=e~$ delta 385 zcmWlVF-QVH6o!44S!$-4mS&}CElmx%v@|3HHnarMR6|RPOAxrYWZ;zcHzfCvA_C=p6G-me*h54z@aV ze$=Jt5RWdVQ!ICBquH(K0()+4negcRo3MyPw-URjhpB)Q;SpxMdeSeil2+QsG>73{ z&6==?1-}ySpP%UtxjsEnq+f}p-mfk30j3&k0lk4zK*>EC)EjFEuaF$n|M!O!rBMm7 ZY0|KmD&RX5L?XOunli`%4W%~2)_-!PnY91_ diff --git a/sd-card/mp3/0159.mp3 b/sd-card/mp3/0159.mp3 index 5e81089cfdfbdf0a2d7db575faa0de3bd6f28e6d..9ff81bef6e65ee4b6903b6fcb9534da9e64b3e54 100644 GIT binary patch delta 585 zcmZ{gPe@cz6vpqFv@vHi&9to1Mn~Hy^2YIBwDD;Vk#WX{IACq2MS-+&qY-VUMJ<9v z$IljWPohPF24jCfMaV^M+z2wXXyax!tpssnix!^ya20sr{yu8fx z6%7?kZ?RLMMfsqTDT@=~56oXN{?RH;dF)l0%ulW|eS*JQOUDn@OpEBPVOqz5a0Sn= zX<9(db*5>26XxL7GJU~et(LT}Zy1N$H<{+}Q`n1#Ax%&5FJ!tb)Uo`lt6oc)3H+wYA@gbyz*;#C>u0Se*h-#l8Ii delta 377 zcmW-dJuHJ^6oqp>_19LZQh%Q+EsM#Zi^b9jiN#{FSS(#SS%g?jG{>NMuf|U#Dk?!z zQgk2@Bn&17A{LWGEEa?NTAu0T+~=OJv8~aSLA+%W2ARyGV1p?L?&x;}*UUQx1tu#5 zyL?v`IOh@M=~fyPd0Htr;zX6;mfy-lE>{~|vcE=f&KqTs)3r9^cnyR(uOQ7KpXHO! zV3BFRV2!~#!9EX^C5{A)@*^O4=3>2|%6ZxdYbv(2dOQoC*F>k#C4r5xpy zYp}py*IvnXivE>|81;FKSgTZ*ZSY5Jb3STxt7Nxb>|J@qxgMKwWB)6~tjR=NFwgI} Kab7)K?&Tj*`1!z(o}}rIzdmB73Ia|VzuR5Rl{_ObHyW=wX8hf zYb7O+tdse3pU$alH?;aBa&_({-82&VfH9VH3y~5+I8`Yf*i=wsTTwGKpby3-ql2-i zJ|?taTZ`7EdH-xjWBtRTIS>f9*ogUl_i@7<(#LIsL|pHSOj@lvY#j~5=rUm`9@(-4EiMcx zPT4bF@xWGdnTiN{yo)FTu0$0+2gZQMj+oz>VZ{?)j9qSyD6VP61ou3PDH^QC75n^% z3%bQM}R12rjsqRjl*Z=&>=XCR)k~2J~}^ zS8k0dF6rb2=WON`9nKfRDPRBYjfdsJgyMlW##=5G)ii&Kf(_POiEi8WnqOsUvc`AU F`2)yFar^)P diff --git a/sd-card/mp3/0161.mp3 b/sd-card/mp3/0161.mp3 index ac0214012493061d86f60a04d150323b3780f5e1..7a3dea48292f72233ce8609dd2c6d9535b5c3446 100644 GIT binary patch delta 554 zcmZ{fKS*0q6vppK)u@R!)<3N_)#%VgkvtQn(xtEV86+k?L_uBRP!|`MPF>=lgQ95o zf?)4aTBI!z%uuU1Sx^TTu{e}24!YFE#idJ!o*SG558NM@bH4A~@1pl2w%kcqnJ!HW znKPzum?EZK_&rRU$ayqGrA5iHg;hrME>A1-0~g&ScOOI|IS)>6|s z7UnQfX6Px`;#x0f`hu4}v*EW-(`UT%Gkr%@1=BwA!UM!BHLc@MrBTj2WYIJSn0Bxq z(2{4YifIvlg&&Z8#Iy#hTFd^EYNj8UtYKP#w^q|UHfoLLRh>z7F3jWoV=Zaxo-i$8 zTQ~~0-sHcoH~qN=rc*qBYW{vG+{AcLi|b#|cvqhpWmlu=oHa6SW2%YiEBqm*4dg=R zMW>m`#ij5F>6UETcAX(R=8XLGDwS7C70HqMSfD`Kg-W=QnBA95CHj&Hdqi1Qq|JI^ z`R+`IlWzu8Yd9QjD(cwAu4BhlxV#;|Ju delta 346 zcmWlUJuHJ!7=?2_sv>_kEgE8{_!Rrwku#gYXuD1Bm6u%Tuyi~|TRR@X87I8NB4TGJ6N0yZh&iD<={PbIMwM*1V1_ZaP1&r>Q4+^&Vqda1vTX4*=kkR`OA;A-u zdIX1bhYhy4AGXe9#IAZ(*0|DZbZw+hu*-^ak+V^||0`&hxKF{7=& mnC%t^t(l73q!Aa?nH>_G)0YrbSW4Iz(@8;wP31Fl!-ao4G>(@5 diff --git a/sd-card/mp3/0162.mp3 b/sd-card/mp3/0162.mp3 index 4b21ebdf9d2a2e98cd9045fa412b3fb968533112..8f1e0d47c5c70b45b76561907cf267c4652e72e0 100644 GIT binary patch delta 566 zcmZ{gPe@cz6vpqFoJMCx)BInf%_y`{GuKQ8B-`+fJE&E(O|=T|7W z*sp1IvB>lVbHucdu#f3IHhh|v;htg2p(I>Fwu0#uB7RLzu;*v`hVipZTR0Rlo&_{* zqpOnX0H1|#akq-;HGDx$^H>csP2*aK={J6aR($zG%!iqZsH--j1z{1X8YA(mhG_?n z&oSi@t2I2R)gm-i$5g;yVGb|PGp!(AuW21$>zVH3UIWu60*#tJ;!UF&V4%s^{}gWF zVY8M@Q;QkzgHXe3Wg5nxR!t9(yTG)D%NLnS_$1uNY(#c~e-Y!p+{WV4->zjr#dcGj zyTr7MaFppCHlkX5-5Apgl!UjD?O=c8R?ETwVx3R3dB=0doTNMPJ)l%UDODjyVy#Sp zq|1@^?4&c4nMn_2($0j6+g5ixkqG@)9nahtQC*hR*Ih;?d{Q5Ey+qNX4cdl2S*6O)VN?AR@m7ErPFAW8e zS{6hbkcN}aHJo8a|#`#vk5e=(CiaS=p zB1dh4AHLeQje@~tyP!r_n;BZv);QrXBR(C1d#2h2SByAKOHQSOW?X_2zqMIzbqMkt zb}O9o$t_srLZ{%G%^rmp?s?1uVqUZUU0dc#m(mlj&phu`Ti~Q$Fv~B$LYiv(Y z!%?vOFHsObA`-tqq(he?h$56sBIuCC$;HJ*!TS%LgdVs*F87{$IWKa5CvP7{S2bf$ zt2PB6xd6cmV*iznOr9)6Igbwt>injxadDTy8V7d^ zp7D)xk-?C_<%lYN?h~qRI$LY{gJ!n&*(CjY-V&kn~wgsEfr6lc1Bz`J=#Vwwy~e_gK6(f`@!stcQQX`TQvq_6GvTD c;@pYO&8&g%IVl zv>k+zmOmw}I7~|9vWrq2c5$IR&*jzAx9|7q{Wo41+ZaMHJ*<#VR|Kz2LQtm565KFn zDdZTd6J%J`9x_!g*k{P5v_+fXlko<@H*4A>uGoC*j=`4v!jpL{aK>Cjp}<&Fu*s@+mZ@H`&0C2{H=})P Gi2nnhkdaIP diff --git a/sd-card/mp3/0164.mp3 b/sd-card/mp3/0164.mp3 index 48b7ab51b5e282018fc13d964c284781d8f4b24f..99f100ec14051475c54e687f2432a1276f479207 100644 GIT binary patch delta 565 zcmZ{fKWI}?6vpot8&jKDTWf2pu_hH<8p=!4hKh?%eZ&$IAEkln(h9n`bm`<02NeWq zjb9hJ$6{2}Dn_v2U&zodB8U=1T-qUvlZ%Usg7-c;2|aLsT<&+ycfa!@_xIGTQGBgr zHLg_}fS-wrz~w3H2KT0!G(x6Q-iI7 zhx}kz<;5L>`wX>dyy8-uV3kLsg3tUO)p*Idm>72M6uf6GV7^`BHGi}V9`WifnZIf{PkWY56zcLuJ28{IdAGqM5&rms>K{r_W~N2=b#FH zCg+Y9rwil7f;*+sPBxVu8;Sm>mWn6Os6ofcqyiLUoBD+3pLWj%29p!+iTt#^nnr&p hogH-?Cvo9;>sGD4=jN3&G<+m8;*4z(_sZt5`UfNywgUhF delta 358 zcmW;HO(+9!7zXg3F(2E^W;PqcM!7gqmU3}fl#9zQa^T?NvWt@o2Pqe<#{vJ>QZA-8 zGWj}8Sw32s+CfUBU6kVB;zIfVU4ECh_j#Z9cX)qjwGXd}h(aP!5^OULL4gj7;F>9m zLYmPE!7j_%H0LS>DF&?yTRgQ2jv1>GeDX{C!j)>Jx4LTt2fWp$IaMpj&|*`#WYQ)m za=-#I@cNm^X%~` z?D5Vc*yMDhpv-Eo!UK1_CV9{&xZ<<6$k`@^d;0vMn`E_$qX9ubKLScmmzvG3t`^fR zuPtyQsPuO~LBTmUT1}rwoB5*DW)kAp##e%STSI_K&G rMuweJ`EmcMg*`**K3gMs>11T9QqOhrmfh2rKAO(AJiGOxYjV1vRT-y6)H=F_u(q=!2NOgzH`q#TiJ!&!xSQw zv_`eECRpV-1WSws1n)T)(3qjOOEAR^!)cc51Wy?cYD{o3D0t1`-PZZraGDQ78k6h_ z3!d|{VU5@J2&xQ3v`MH&1SOu13SRPCRO20Q#sojvxYs7seCFykD%`BMzuem=c+2(% z!6z;^Xw&L)qs{zdSmERSV!HJP`|jCW{cxY!}V@9IJ*!?DiEN1fraTl7lqfH(3rq*P5Q zRcDUTGoJ=#>?>bP4!GIEXg*uWyCcd;rMi3j)6xI7!-eyMs>^Zux_uO4+xsD}c+tJ& s2b6}~bGcD_HO8JnC+!#|uAU9=WG#AbPB}d%`}&iok~`F$*nX`30dQZpr2qf` delta 371 zcmW;GJuJgf6a{dnRIBQ<6!ll7jm2cp#nMGW7`j+YBrKLjVzMxZkO-|~An%ei5@`)x z>MJCI#zzDLi%2XTDiRipL=4`u{Dzy8bI;+xdw;wezC>6dlXw)oFb2Ub-8R7m(>8@P zdy54d%xhDeDG}__vMa=SWEUKBsMI9Cv?;DQ6xP}46zuUvd&==LL54P$(hXZK!5DkW z1qXaCS2*XCTkyl03e%AJcc4-s$+t@LWv)ta#Vt8cc98D0so*R!{j`Im7!QgFEc5 z7d+udWtEq<2`UVP4IZ%)7M$hrh@i%`h`|S5iweH8X}jPZtIB1j8w_6aXM^A-Z|x9F zv!hW^=2D}0t_q^5Gy}QN_ z2%a(;7rfzo+^f4Cf|p!ZK4a;icXh1O=*-1VL59UH2|h2JSxFFwZgZ|HQgZTc!5MT% zS3{OnwJfVnpT?X|g_ixu=97cYP;M+cl*>A!R>Ds8BnJ8-|84twbs*qVN3SO9i;F?a0;DTw3 zLYn<$f)q>IJ~#oAe0C^2amp$9WNo$Jh8gWWV=jdPKU{((&esUG8FCADm~$%#M`{I+{Lvn8txoXH zh(}?Qw;n-~ll6jW+Pn&L-1UmS*w-NX%A59;GmT33YG@MN@#xe3TYqmv+#_!7ffN5pmF^ zTD~IcJ&4vn*a|6F{1>o;4vJ8a4AR9#1fk&K;-b*=9-V|9xIZrUeCK>0cd792^sOv@ z%;pT{XB&dIoQ9xIJ0e)&Ld4)9y={USepfzbwMp=TiKxM6zKIInb9{TC{H>hl&6vS$ z4m1m1@{{rj&+iaCW2D94D(|-l=6EbFxX!h>!8=~E1*`1XDOhJs*mK^_8`wnyHVeX4$(Y+ly^9{E0|4p8g*Ii6g=mJ-GZl#bs4kTqAdV>wq z2Sr`%q+p2)$sn_r3V%siVYN@9Ya>%7o2mYp>Z0p=V{XB#e2H0B&9bZ}Jz5V!D)c$5 zlAkHK!{y1+aJl4GtaLg*n9k?o|8>X9r_NXdj*}S-QMNbxqn8i^bBAAhB4)(1FF$#nh#X#e(MP!h3@tq7wBR zgrq{FLBddpFfcR{lSB+GB;L<*rhD%>xxaney>ngoNJSK~siNSLaR>@DEP@+mEDBqU zmI)SE(%fOPT##eHs_?*LtKg2q73SopW|p%yg%x&G3eI_@xy$h?!2vCHg&8i|1!QHKuFL-A<3v zmRpbDh!b^!J=(kqd9HfR&K{rW&S%X66ZH!B^fd@BxUadyA-`ah?|y|7PB)77wl@h% z%xe}H3n)GL6A)~0zFBa_NYHF32F(lc7SR?XBsgUzWHv{`|6kG^W3pAOuLH3`gWAqv*Uf`Vw{Mq0F)3KzL5 zJuZ^_6-Eh7#Iz{VS{Nlo1VPLuH*IDUE!wncQ_#H+SAhrakIVPx@SPvo<=mYw+hu~;K`z(iQ1%$2a<3CFey-tw1WiMJyfuh|(D zyx>Q}GB0hjGof0IdET!TT;}nZz~_2Qqspnc;2s;cTmQ1*W9I6#@%dgSc*W@*V%*y5 z?NzJw8jpCcLGYfx4J(}ADOg}nqs9$>ZWK)O$}YhaBTX7}eAr}X2ATzH{AO6@wcXkz zYTjehJvaP7zs1&nwrE`C%wEAD5A73k)tcc~o=<3e;-7@gwXk3Cl9^VUy3#6m!^s17 zH=eZqrKH9(y*9x!Rs$9f3T`sdt}(!sb_pMTdivB~Ug)?r*AXqczBlR)dE;LqN|lvT zHRc&D1~f3`KzTnk_hlTT*o7&{u>l@4JA08*V;s5{u delta 371 zcmW;HJ1oOt6b0~{QmwYC-pXGUjm5&iVzH1)4Hk)*7%Y|=Od`4%5D9fGd^bWwqKS$} zQU<9Y@k|Y!L?Wb%kXS4h65nU}4LA3m+;bdx?_FxgYrI1t9?uFk7=_@D9-H8b37bNa z-Fbo?=Cn!1@&yT6c7+sA?1DoM6bNqlr9I%HL*argPQfW}v?-1jnjM=jY_b1WfB}Kyb}yy}9)SO@1P%aK`Qi!7+3H#u^3lw3-ycJZTbp P>u$K`mm{Is<&g6a=_!{< diff --git a/sd-card/mp3/0170.mp3 b/sd-card/mp3/0170.mp3 index 70df2efb736cdad84186131afe30a829bc360859..ea06f1a4852825ec933102df6d1db2ae2d00db37 100644 GIT binary patch delta 490 zcmZ|KJ4jqX6b9h=SJzc{eZ{x2&{`vR??%mH<5e#r>#i43Fey~TG-=YL62uf1ZWAAW z2*Ejs5+Z7`qJjoN1=9pEAQ2=GY;2OoCXH?8E>-~#%*QbQJkA`p&&3y7@n^A3V}0?5 z;E1mwIA$auIOS$Q<1^hH!4hwb8=T4Y)B2#sGQR``84l+O_W9Si!}*XlX;Z%71HTy$ z`LaN;!a$+M0#^zJ6YK~JdiXP}alRcajYQ>EZ5JFEO(TpRZ}Rjskdsv5x- z_l(>0Y6b6jTdOh7be#knM#s-TGWs~pi@MN^5QnTZ8t~bcKE+^IMp+tnk>t1&XS7U=Yy`-?>x_5Ff{0NC&v7#dSbN< gSkEj|4U=8@4{h~aC!wsC*wZ#U7Js1KxA%wDKlzoFCIA2c delta 282 zcmWm9KPZH890u^7m)Fa^=iDE6+_Bh{=?0`*EJ`t0lnk;c-C{9WER>sgd!)D-TXD+i7KP7W;{`VzG3h|7qQsi5Du5~D8*cVlJ^rM1t&c=fFKjR0t;|iDAgrLF4 zM6f*Hsi@LP3a+@HRNQkk6})&g`dsM}Tr$_QI!6S;ABH8)*9jgm8q_FqBPe*r(W4%B3@cm;X)Lh2 zUhsq;4Xd0wCRk;l!Si|CAb813VZj`?!W!G0i3r}Y`MBT(%Z3$Z8@1{A)hPJHg%h3< zZ}O(9O&Sk)y;*RdyM`66vO!Ao dS+N^e>JMvm9XqS6-ee+u(Mlik-P`-aY7fG#u_FKg delta 348 zcmW;HJuJgf6a{dnRB5YP)oLmFUo0krE*3*;u~;Nx>e9v1rIW=%!lZS&@Ghw^=+F)< zB6MgQn#M=cK`at6Fo`gbh{1c7Uv5tBM{a%QE1sRiQ!b{E%e4drrXgt3Z4+FwXj3RL z(JnaRv$n*Q4ndI-yTUF{?Sf;@cN#ynRc<;IvK;Rcl-bZWnd%no)8;gL_MCzWr(A+{ zKDZQKm~jgl^z{fXSk_iK>ruMr)g!p)YOhHwubHZN6%M)R6YTI?TV=jaQ0IhS;hi^r z!5NqO1$jCG3K<>*1aVFW1xY??7q~p2bP^1SzI*cTTv+hKx3JQmZVU=;IW}bW)U+ig xBTCo55kZaH!-6ZuMilPY8Zk+F)coj<3bt5`Dx{dO#D3mZyAJWgT4TX-{Q>5fj!pmo diff --git a/sd-card/mp3/0172.mp3 b/sd-card/mp3/0172.mp3 index 22ccc658a2c3f227bb4efdbe8b0c5c7b907beb4d..fbad70f661f41cfb3562dfdf96c169d310967fed 100644 GIT binary patch delta 565 zcmZ{gKWI}?6vppK8&i|mzqVS9HBso&T5ehsBRce{k66;gM`0+U0mvr#YGnv7X{CKbP{^t{y5xo&Ue0hpJe}@uB7P4 zT-s1&Zk_1`W{BxEq5-BFssTd};5INlMO|1%sgbFISkTZSUIm$MV`3Xi&o7_1LWbU> zuZd|1Yr@Akzny6ofw0J9Da=&Fu?W*ue2o}-giBGTFKFArw2YeY26D|t^6$+|4{^PP zX%&f9rVm(YwN576nCkc~e1`dUrh7l#dHN5!ckn_ zWyF8iZl+;87aoV#Z8!exHuMeUJyyAQFVhO%30E;4vrhiRn5tOZXWx?UG4vAaJ$C!d zex^5w#+lxt8aMI}Zh~nVb)iP-00+wpI(!j{gEvddj^~a$S-1E(q*P5Q)hI{ej!%JH z`;qsCvd&0hDnC-lJ4L0BX8Mzv!;$~06NOV}RG-#E{XPoO&H9+@jXGz2f$=ftWNyl) l>X7yZQpuFoI)3g%(^jpX>*SOk7|bNoM+UdBdu?-A{R5Msv%dfU delta 358 zcmW;IPbdR$6bJCWWBzP2W;Vu*Q7$eV?83oiS#fYta&q7zwUm>~E)FoHK1v z*k`y*aKVx`$ym8S=(j3t^VBNX;aG*}p3i^RDit2sT_woyMtjETYC(z?n;y?in_!Cl zcEJiC?FvVncL-kSsu7$pt4(m&sdWA76dZ7|R#0HDPEcg7&Lo*|2|oCx%`s6g$gN6YPeF|?}ZZ?y<=9W-EA&1HuIXqO+%es*^dG`O!7NMK0meGSn%wBI%`w=y Gip(EJ9+3F} diff --git a/sd-card/mp3/0173.mp3 b/sd-card/mp3/0173.mp3 index 5a2ec7c56cdd17d957fe2cbd027e0e67d12dc9be..8933beb25d5f933a68bab148b5725ffd758de565 100644 GIT binary patch delta 554 zcmZ|JO-NKx6bJD8AGI-OGR=>&CL3F{agb-`j8cnU`5I)J@gWM<#)8_kaU&6J#)Sq! zkn*1*xu@s@0)u9eU{TF&prRkQ(yD*{rItv zwYagcCV0jv2v(Sk37&8%X7PYQjo>La)H#-G1r?_1w4E>N1P?jB&G}oM=gqiPyXysS z`9@vj#qEMSj5S!y@Lq#ph({A{{(8dV8>f>lv3ZB!F{|nd3yoI&-ssL}cRIV8+_cpu zt0`xjUFRS58Y{a5pV-@C@sjUa1Rr>*RdAQ_Hj7())F!yX)e6AXsKHEx664 zw8doxU4moWP-j^_SSkBqFy`ljiH~t(s>YaFU0U}e6+Mos8207;;nHMrxK#8fj5nCe z^bX__|J{z4hDJ=c=N-vJl#-kGqd|DuKNAfY8}&~XCf!yWdq%Q-o>uz8iTbTt!@w^X Sujg?8U~V9{#dl{mADe%s#jlJ2 delta 346 zcmW;FJuHJ^6vgqJmnv;lt6IvdT7$)8(1pQL1_lF*sf)$ZNKBS4B1~;_N+QpVNPI*G z5+ouL9W*H+44p)R=pvCY7%+%<9?NgIxjFym5e{ej@tun+NUkC{X9|KcJ(l2<1xw+8 zi5kHftJ)Q&YXw`h9r~Ro4#6QO>x>`T4Q8B5+gmTV=cTs5=?1|rEtf);>n_0r2i#`= z&aLptIgcs&8wJPQ*OoczRoWM?xi2&sV?MK1^eJ7L@|({u?JD!lf>-th6s~v^5Ik|F zMX*O_P+^;!LBSG-LZ(|fVV0pb!61*cj~ovR-uV_*_~mlD;FdidrdawnX`AAY pZHjB1f+FJ)g$q_9f@`L_1SNW+f*ls33X4p{1Y@jf^GtW={{XgzjgkNW diff --git a/sd-card/mp3/0174.mp3 b/sd-card/mp3/0174.mp3 index 08d122d8d9581fda22ea424c191711d91a1ea340..f821f5a56dcec838a4027abb55635caea0a60e85 100644 GIT binary patch delta 566 zcmZ{gPe@cz6o>Db)G=o=&Bn4un^9UiBS`lQLJNnjCj4F55Enu{PsfNG8DjtN5 zboDkeJ;zsJ8I!x2o+8v_=so6}n3ixl!nBNE5kqe=9c5wb9;VNz1k5%Y`hc}&rkA+0 z*P0|-Y-X{=&~IFAwN@L#In3^3x(TPv&^*4kF+IYK{Y)ze$Lw3rVoWb^w%sOIg%g-L zU}zHU2bl_ZCp-hc!%qIwVdxv~9b#I?(Zkm5i|{qB$Bl3^ZvE#vtxvkkPzBX4Yddv> z#WR`+ekoxj<0YAHp(ebBVz+6CZ62GsIE`e_{o;b_dj&V+jr|BKRZ&XS$(Hpbpg^vJ zmGe^>cPKxe8_MV0G37WX`Jfca6oIFm^jzCQ`Gm|tkV4{J?NZpcF?`O-K@3%NRhS# delta 359 zcmW;IKP&@49L4dx(w4TWR7`I>+Y!DpqT3h5yqhOB~hr$Kd9fCWK zI0cVSBW9aTNyK9|C6B^8r&~Nm?L?L5=%3Jdgi2ogNj#yJ@6tq;Sqk$kZl#M0dKvwOdkiBgY%xK?5CuZiBBDrKhy-nB(<*{CZCZ5i;41Kk`*FDEoc}+c&&-{jK8lrM zkH*d7s$iOv5WHb5Bq(z+q%lu_i{K$EhVv}cSjNK|MZON(dSt8M6@ME(;QffkH+Iwt z7WmQd9xrYa+-9gw<0&822`=$?RLsnJRHMWzF~Ku7ZWk=GWLRdl-X{L27tC;KhnTJQ z2Ek)4H)xY`uF+2ZF)VXtr|r5;8dLnzWK%Bh63jBvtc~ZBX6t;SMNGQk))-*R z9>IAo8ICb%6&&WzR*eep>=mr>&_2O0zBl~JiMYlm{*7Co+5LhS>}k_@&eb--5+@G` z-ZGZ3Tuf-Zq~9)>W5w_~3kL-m#*;eyA$|2$FW)8aav~Wnc!59c_4%VK5v58>sT%W) z7OFHb-&N&;RG-(MAJ6sYbKaYA`(H poyv~eQXO%+2HkGQC~@Ir?PjBa?`4(K)t&B5XHuJ-dt+l+{R4iNw%7mw delta 366 zcmWm7O(?^07zgma3T>>?$F_1yu#Cs`;{ z6Uj>s6KcuZVL3>NcA%7ti;Lp-yL@`;d7kfgt^Y2(+=aJzP$3@A2{sso;D%0{Aj_0Z zAk>HZw+HEcsD}1uOM6l0mt#GtdutJ+%;fU*Y!4!KP zqHmrY3K>p11;?x`6FhKFo8>^cY5XV`Y;m?i^jC{ZaKMa9>6YP2bNQpqGErr=9d3mL z@7<s5ZP56GYjv-8$9+<{M2A{%EucmzxAn+0`sq z;Zn248=h;iseT8X+aY+sbgOmz&}#dOJH@1m#5LaXeq8X818ssw{APHIQ@b?Ivu(Ga z#3up$cEJJuY}dy9+8)6g4|eF#${#LwSex;L#xwp+Sc&<)f``m?YSg&eDR|7ueS%kv zB?Ze|Olmx**Cn{eb;GNy>=z6$k<#JsrKyu~zD`}|cq&|ReXryWdgV_MrD{s4I`d53 z4QOD-K^6SWpgU9?D-0D2Zds+%hkMff+30`U(c+0=)$KT$o&d$z#(u=}&$wp;fzpV3 pJU?bzbJ#na%l0{rlbkqKzuBqpxq0RE9_r6zog5=2OoT7*cX0}G4AV(?y;QfBr)+BGh@6c*WDZtm~2S&ml-gqB-jnwxIH7`rP4 z1wL0Q?TkkdWNnp6JSaL)t&8xp+I*O+5nM3p6}<4=tB~htt!edJbhS=!$WEV0eDs-l zqF!_>mtWzDJAT0{djf(}zG=6a3Mx!8&>%?ixad%$po8CyN~g~^2|m~sQdZ8z>yU{_ zIto|(bIgnNX2B8TVTCj1!-8{8wFqwMi3oDcL=>)>hzbr^(5`T{RWL{=rtGibRIi_x Ju>~e$_CEzYlY9UG diff --git a/sd-card/mp3/0177.mp3 b/sd-card/mp3/0177.mp3 index 996572c7b7de7617275e41e7fc72f4285889d53c..211154c24a217e92c88b1a36ce8634863e9a8a8c 100644 GIT binary patch delta 581 zcmZ{gPe{{Y7{}jdsaxAJ%|B&Mn<-ut`OU30=+dwHVPkFn6bGw|33{lDmk1JJ5)leP zu5X9g`!J%qENDSONkJFs4}lOnMc`$Z@ZhBmb?DIZ4o`s(ydNH(=lMQ-pLZwuCbfEs z{DrutQlZN91@px87C{fwE0jH&*5DjqT0u=%M81LP5kifcmax&t^c>kHGx<|k#C@-( zWeoY4p5e2wg1LiCC3yUro}lDsn!uR=Qx)F=nm*x1km);G4>7$%MYxGnv!>Vh(af}n zJBOK8(bvLM!21?W_i&|^=_7s%i+Fg1=>sBdTJo(nrkl9lZdBe5Evly-#@pFW!);+1 z3rEc@oyVB2VO^Mm+r^Z{ZkMJoR*oB2gWbl%X1Dg#e&K3J%Xj<@F}=WAk2xmZt7Toa z*PJwe!k7+*nbuJbYssBHrkAJ*OUR#Ox{Xl3ri<9<*G)ZtVz=%VFf*|HV8EBRU1!Q3 zb8=t3N>!9n4e}%&*C~*4J<@J;%uZ&e)5%QQ&Z)>~d^nOw1pe!0GZ)6ykY&Y&>&OK6 y`jd`($-Z1)Fg0nPPfeRt21mxNxFr#uJ?GnR)OGBXvPMQ@iRkI*K6aP(#OfbGY`LHS delta 373 zcmWmAJ1hfG6b9h@r4L$z{yR5+jN^XKJ!DfKzMeu;65WQ8ee&*#om6PRq&8sjI+G3 zMWevhtv3J27%*rPB>1CEW0~XI1n;@4U5DQN<*RnPI1$(83x3B1PdT++P-3P-<1v>y z#5lG?@SL%Pbv|M9ey4qO#dw*;oq~SGlR8{hFUo)TGI^EflHsBk_yw=eANdqfs-%>v zF~h`6Rf9=aBNwFlyn*~^ZXloYMwILJcDwuB=zqJz`NM;%%W?K}SINg#_lNx8sCTT| spfKbe%8uHp+U@iVrqYgy#HoX|YaI=IFRPrM)ZR=wom%6$7gvkbKNCs1`2YX_ delta 367 zcmWmAJt)L+7zc3QKYxci=e!(_zt<}%lYzxzxe|*-Nd}971!b~WEK=O{weWk0NdC$V zl#apMm2~b5C`u#)rEalUD8JwGspnhIOV4U=x+mI!w^*k_EOsZ@-~i`L~#mBKJ9t4)5W9b|t<(8_E`;hFO_f_t{qD(l9> zWUVPi9Hn3I>j*BmQYScKXT8E1@9IT6(I80D*JyMbO&)17N55+qIomAgrxRB8L(X~n Lj~C$uPKND&WCWIF diff --git a/sd-card/mp3/0179.mp3 b/sd-card/mp3/0179.mp3 index e2107eff680da49170b22505afae3a29fab8f82e..276cb063e7002fef06dce3a82331449767d76922 100644 GIT binary patch delta 574 zcmZ|KK}b|l6b9h;PfnxGWSVJNlZ{QZQRGdNj?ucE%j zPJfd%WKKNUk@Z^M;*ND$|zg%0&OW9m=2VcUoOH-Bu6scNjSJDIW6Vl;^HK=%ckUD>v6#MlPni2 z)TsHB6iO@{c0wubz)8tP$-(z~d7YjHn!YK1HI)CkUbt1WQGX*Ld*f^f?vnCF1o z_~ceda^53Yr>|D9#{=ydN9&aKqfYS5WWC^mVXq*|3$N14DW4$CA8np%4W^{eufzG^ z7rb+}(ezh0nYNuKb8RpnIOVH0%Y|lz6ay`SB_3%fIUW@B@-wLL!sS*$ncZ!IUtVjA zjN1y2{IkuSjdt@(w8ONOIt168>oj^o##~5gV`0HD%i0tZU4k*%5oLWXP7gVG8ChXG GV*LXTJCpMO diff --git a/sd-card/mp3/0180.mp3 b/sd-card/mp3/0180.mp3 index e2008c3de70b8d931346f9c11df1e7cd248a379a..ceb04cf9d2247216b8f4c01c3a3aae5f18341502 100644 GIT binary patch delta 485 zcmZ{eO(;ZR6o$VuW(;Qh|I324N%xNbY+T%=G2v0D zjcFE*KBh-J_%wZD+|P81>~y9x#DvEPWoY3`MtpZBlW7&@Sxloi&(hN0pUw1+FX26w za+oe*=4v{?O)gUmBY8|a@aAi2+scpM-&DY~hbQ3?CJMFKf+D7M90?sb#o9IajkjX$ zX;KnIOXgvexF;>g?zY delta 277 zcmWm8KPbd;7zXfre(vYEJLk^1<1UNY!r%-Ri&7Sgl2KVK78#^07A5?8Z0|$BrTZJOMs9~AAQDK9_F`>rS znBk8Lap8ukHsO|KXO-g#bABco-PLyCfW1j!k=3NR{pplYXWjYE?GE9Q18GBrPif(j zbDct&k&L;u!%XALZqz^idk9E>*CLOBbguaqtf= z+W6HW++%6|2LVY%QCpP?x`-f1aBy+ypp%OZE(+coIte{+e;mH=+ETQ2Xg8xtLZ z$6V{sSmc>bo8-4)mGe7o!l5pWtNhp{#=Wph@S2fsjkmnlEvWHmkNwM6!w;O@tx;#s z9>D^i8U_q{1zG;+)oAegUcoOO-X~`JJHsl^#x-=OdPsVE_s1J;pP0vPZ6c+N~soeOf5EPVB}^M zf>h2MElw3iiv@2|x#^LCp-d+FpIa)P7*qYOn;K}M=xq4oesIb=-F#qT+&iA1vaJ~m i29qPhuIt9nA8XxgH1NH=atBk%Oe&q+B<`h+%jzG2?6YJ5 delta 358 zcmWm9JuHJ^6b0a%Ql+hGRjZ|{C9#+cx-eK8O)Qo!VqmaX8i~o$MTiA;Eb?9rktQly ze~~mu6B;yxp@Ue6bTL`FSP1X8Jh?e{IQRa>jz-r9@sWxsq*4{Z4igYO&~6gkF>6xD zF%edrQo8e}wap8blVS`U@ z(c9TJL75hh!ZY_gf^&{|jbGlhFI;F>xS-c3IN+IflH-0sj9-3*cdm2@{@B+ky1k^$ za4Mj*KLNohx4Hz6j06>KSP2U5ndlajXb%Z4nGGqV84U~OSk=au>=CoiG`&2`aPKPO E|9(i2p8x;= diff --git a/sd-card/mp3/0182.mp3 b/sd-card/mp3/0182.mp3 index 8917157c28673b89929a6f01dbea5b4eab5bec5e..8838255fd28e2ea33172dda6dacd90b91cd32243 100644 GIT binary patch delta 574 zcmZ|LKWI}y90&0G#>CVn*4El;HP+OCOKW-0M}pwuQy;a)q>s`<>QV(=L@0`jBE+GC zMZu)s4(j~~qByi@Ad0mp7E~+_DkYPP9lAJF7Z(=??}bi65AMTpf4+CW`+V}jsZtt0 z%NdPQxh|OJ1O%@c4+*NA4QV`~w@Fars&R_Nu%N_5L}QK%5y3rQ}B%6jdQ%d zQ{yW;cLixLjPvxn1Qq`3(zwj2-GVV5*dutv561UAo6uO}--KY1_xB2JFw?D#_3oh3 ziG9KU#FK(IoK0$@*CTkvRbz$4{bDi`DIE!`Ytse3OWo#jDpGWPZ^Rw&3d>QYs!FM_ z>83qwXfV@_mh+tfcQ8Mm8_eh2g0c@eefHr@?7!8~{D~peYgtZTgM55_ec1C)yJL-j skzx0Ec03rWgI51g+D==Rl{|l}X``*a=Vq1F?^tOkZEx`0o9l5G5`Po delta 366 zcmWmAPbkB29L90an6V9GHpa|m<>KUE7Z;b6xVR`ex$L5}ivx;X9LT}unZoyDDK6V# zCACIvWs`p)c9P<-i&8Ewb8zteU0%ocr{{Ap@h~1A!EYj@Ac=~g#3%&Uw3`GMOq&!= z8Lk%;Sk>+_)*uji%?df@&4Lq7HwudU(H?QzqLAcJli-fG+5#iZf<2n73WrQt1q&Rt z3F3UUDdf3m7kse2MUY}v`^CvtrE8z9f)}p02`UUYjDFE!T%2teT(YJuFxg?0hn)&b zd~_P+`A)$aEiR>39J>Tp9CHiK`L4}zrAy(B?rx)YrY&*GBgpXEqp-@&9zleIy@D*u z+9%F=6>9wR3huezC)i-fr?eHHu{7Fm{KxJW+%WA|+HgQnW>uSEY(R9=8&qaT-SW;1 LFN0fr4x0Y~u)~y! diff --git a/sd-card/mp3/0183.mp3 b/sd-card/mp3/0183.mp3 index 33b26710b02a95325e3c82cb85af9f4b7e73ab20..45d3c25f2558f0834db336001b6e54a7c5b5c1db 100644 GIT binary patch delta 565 zcmZ|JKWLLd7zXfrVq$6%Yis{#HP%GIrKNmHV^v)I)X%h}i65nb>QVbDRliOwrpSnBcncAxl9)nemV|z6%MyvA9*R%3ns|-LS@Ob~OlI z@uTrIFKrXNWgwz)iPx0$MOfw@MF8hC#bNmQ=7J0r(m9A`vtR% zC2appLgNv=q~HqIjd_+1NN6p%bmlsf2PaEK*Y}3qtT*x{tW;Gg6*SAtbX|ivu20_Y z&AJ1H(fmLm?~bTc+UZUm?T!9-TP&PDtGXN~(_Nf+-H$ z1Q&d=Ddf3e7d+C{Y;3aHGAA4gHNH6nw_I%z6c};}&RK9O9CO+wIN^^r!>v}q9tYhD zF+RAB=6sv+NrOk>nTH$THlmbgj}YC^6n6xTZa5^wU9w93vsYGHcpduJnrK%Um2?VyJH& F(SL8@k{$p6 diff --git a/sd-card/mp3/0184.mp3 b/sd-card/mp3/0184.mp3 index 41f44903cb452b970e0623a75c0668bf08116ad1..0e00f4a880dec0fdce2201606f69f3819ac49ffc 100644 GIT binary patch delta 574 zcmZ{gJ!n%=7>3Uq8xxyYTl=HcSW^QoE#+R)q=JiAz1A9&UP=Sir4Ds*unt`;NIKL( z9W=Z}tmm^B|FEJR6jUlyv>*=khv?v-4jFWEadC0*+)F2+ADoAe^ZlHMvvT{gVPH=;Xutu5B!-5Aat{2q!+px;Z5pAZr zqJleoYgpmQ4T4(?)oVQET)p5m_r?St_$8+Cm=kd^Y}zPz!G(ai290O@*&w*b=}o># zXQRJ=sZrx0k2eYK@{eJeGn)kyw3{`~@m;fTa;imemXTJC`@G#M_`!W`f=B#j_=b~P zwAt3SRgB3K!$o@Sf;s+b*Er1!+XO}K-Y&StWy9}0kM>u6NWq t7AzPZat`E1{nRw}4BEXJ%d(PV`=e{MdajdGR!`cp`m()i=uWK;tADg?xRC$= delta 366 zcmW;IJ1oOt6vkn1sZv#?buU^?EGC0477Gy;i^arXv9yD!3j>1~XkL+U4iT3`C2iay zEKO)q;x=>=_gEwnhAtL^?{9gwbIvbkrT?aHp%ZWMupl1KDb^W*;)*VtBFltLkYrDx zVu$a>Bqxg$3Ht4VRi4-t#~dnFd^2x+;k-l4RC|eHkC(?%_{ z^1e)v=7dX)mF0>Hrv4pp3o`t0D-JkaVU+|u*8a3daKhnA#Xf(Gt6Z#7OtHf&nB}e4 zDjD}FX6UFE9C5Q+k!N>};*`(E9AmX&w$;?BDLFJ|IasgQ)s51p^6nWz~DzxcUk4eTEA2I_Pgf431LR++wU|Q%R z2unYa;vT~3AJ}5V1Pd1~bWw`}6BljT%%)WcZrl`j?!#5!f&1fd&v(A>tfv0mex9WL z`Cd)4^LtE-m?EYvgnUe!Sn+8p!a2jlC<}`yRG2>E*YqBr{Y>wWuVgCWP`HF=0Zkv! zQN^^3ufihkR~wD5M$=Qgs$nYQT98HaGia0#LrmMKKg+a%lCXqyt#STc%d~)*I;Izh zg-zmC*y!)no82Rii|5R>@di!f_};*D7ZZ(44-jZFS#O${e&BkuN&h8W!}NJA{>>Ly z{MUs`a9fyi_|sw%pIl^mi>sHI4zMHqg7Ju!-MD&mcE!-%O90R)lU>Pj}L? dtmwTPRi{Z^$4)D&E79GT?C&|{+{Y)&>KNTPv=RUS delta 358 zcmW;GF-QVn5CveKrIwm$re&FFK}%CZE;Y1BK}(BElS@MpxHO62($Z4NYpDK1!Wt6N z${<3hAt55Dz!n!FT$*ZVX{ez8Z~3_4-M#yrJDuI0#A`05kjvEt2TVcmK$k^u$D&1{ z#CV(Fm@RFI*>*FBtO^xgSq0}z*aTI6Y75-5D_nBCLvX@-ZHY^rCS!3Z>@x2Vd~nJs zI@55P-RUvkPWTj7`REfY zbEQ|1q1|t~4*h~AX9A|Zsl8^pPwD%Ce$n?Yv;{5%1xdaKP2s6Nx`fFp*LK diff --git a/sd-card/mp3/0186.mp3 b/sd-card/mp3/0186.mp3 index dee147a6885fe29a358accbcbc717bc0731243cc..aae8ab34aa0646b1340267177da14bc6562d0c7f 100644 GIT binary patch delta 565 zcmZ{gF=$g!6o%g)8&i{5TWhP;SQ7=8mh#f-Q*rUBk2J)@M`@r6QU_fe?BXC@(xHO| zQNyp2_8x+w6e^OU9h43Q=~5IyGE@g$x;Tm8;-cVrk4{1l+>f7g{(J7vUCjL*esCP| zs$)>EHU;lF0l_kp5y5LNL=0x>Z4=CJLs?^`MKH%y)L@Elqkoe@PS7U2!3)^+2GZbf$&c%(6a{xPwDg;tg_iFc*BW9 zf+Z%?;V-3)YA+*r%njuvD~Ba&&*!JVGIQi!rR@6NsGIY~KE*7nVOds-9<3)K71|D~ z=nv)Gd}+LxFBRP}D?4QOXHT9;{5LI^&R?|pZ2MS$h*ENMKH~Wo+)Lqt(GmAtVLXs( h^apIm)=FPH+q#w2_uPVI4-7iaDQ9qtxKo>#t$%!-w0i&m delta 358 zcmW;IJxIb~5C(7_%Pck1Oe-_ff|jO+Tv}YDqNPQMrk0i>!8P+H zg*>ATf(&cg921R#0{v!%6i>~9bHF!$*S~(ZL45` zqc*`kt2Tui7wv*~x>^O7ENU+}=`cF$4#5?ZZAK;J6x3OADt*hO z1>v1ru*3Nd!2>NGg(`PF#+HazaK<-ng-e|ZC-im+_IRW%aH?Cd%#Ut`X|DPNFC6R< z{IaYqan`S}&u_nxGrfXCh64&ud<+O~81EC5X%GH?DX4Ugh6GvGv};WCi#d{=NWL;O Hu#VUtJ6@8f diff --git a/sd-card/mp3/0187.mp3 b/sd-card/mp3/0187.mp3 index f13e315f4cf91661d08bb321627287b74babad2f..c2ed1e7510d7e4733b8c288259448c31ae1db423 100644 GIT binary patch delta 587 zcmZ{gPe@cz6vp58avGbNOfxNQvN5fVB2TSpqD`-Qh>SD7!~tz%i`=xag>5Pr7Zw>& z4qt?NPs7kZu*ED2LJC?WWCTGBEh2E^rj?sov}w_KgR8&;_s8X)@0{=4b36TQX#NBu zg``2TP!W9Q6a;S>vjwlXY#S`n+aZ|ahH`=V8o?6dA%hvNgaj{{+ZiZ-DHr%4Y;cEN zwSs5-q%3oCm!L>HV(^5;h~NTGM+Fsrj~c9TIwttRhTVd9EGt);t}}SUpLK$poZTas zXM4S%!1whA_js{E@R5I%B|h96IQBLMj$axB{gr)!S%#YouJUPDiWzC z8u3aQI*^DEO9>*ebdg9b7K_3Ev;3x$d+xnYW2>Wy0r-;zy96`L VcPU0V(XEb{wdAsho;cIp&OZ%7mTv$6 diff --git a/sd-card/mp3/0188.mp3 b/sd-card/mp3/0188.mp3 index 8e77f330bc550bc02cc18589fd67702a83c658b4..4b3e33d9cb2c1e0c6850e523c83301d223e0dcf2 100644 GIT binary patch delta 565 zcmZ|JO-NKx6bJD8pPa^=$u!flCYvc}<0NmI)TB+Xd`vRU^biFL!h+nmanq{Jv~ZD& zKJ0Ih+>=;gppa3aMA0miqD>#z#*HopqE!TK+7xu&;41LI{c*V;=lq{#|DL(ij@9uF zi?Q()!F`TEu*6tMP~vRJVus!p!8FV242xBQhm40U?(tPvFvtF_fxpz(ydAN)!PaWQ z6Mj&aIJ8YL!BCA=r)mU~JP{RK=4#ZU%&RfMH`Z?#yyv|7j=5TkWqz*}yx`~#!6PQ? z0!wukZ+W3!@QJ_Gr<~X+c))an2It2H!7zt+2}T)dw3_^JqhOsUn*RtNHf{XmoY_Z6(y@Jm?yif3x%j#ENid#JApLj59a=&1T9W7QTuCxf=aO6PH zCzc4DO<3HdmlO=Ktfp8zDB*=gr`s@j=vHyg^}Rkf>kWK~7&C8-snVr2T~X2FO6C1b z*6k?_=6ecxcfh2bu2lMXXY{|@{zCUT)9N@!Qxyuajr(5DKkJ^aH0bMfPv-`MtvTZO hZJA?^R^rO3>dl_|o|`jHTc)$CJ(Jla?)8n&<{t;~wQc|a delta 358 zcmWm8K`6sv9L90an6V8THpa|Gxwug5;^JUYE-o%kE;~?K$;o9G7bUhH4!l1ik*Q4) zp|*o%X_~_hC@Jkgsa;%lp}hag=lDF|zlpQ)*f2igL4|m{DA;ENf;{aeL5>NNLXx3s z!3j&+B%?Kg1ifa3U0$07=bWxJ{L$WV!=eylf1M!32W_6=dV$bnRoXqP;D94G!5m+0 z3g29^3(9mh2%ebI7C70c@W`)5!7W#s1g8u*3^NXed(Joo&#Y)Kx!Ei@;$VvoXQ4%~ z$oW>m8Z9oR%MV?GPma0;X})W7Ty9gk$K7t!UTAkXM^wq#v6$8ENIQ<8> C_>sl{ diff --git a/sd-card/mp3/0189.mp3 b/sd-card/mp3/0189.mp3 index 882adb3ef6e4bf99fb9bb7f6f8ae71ffe9d7fc89..c3eb0007b062c12f8a53d3fc6c6fca509394abf8 100644 GIT binary patch delta 580 zcmZ|JKWI}?6bA77#m2-Y*3{Z+HP+Mym)7zUO;B8X>a&EH^ifiXE>*CL2z79=2yrOV zPPTp>4EHGYPlXf=_y-#u3hGh>K|2+}rAx8k;^N}qeUDB;58NM@bI-l^`v)zh?sKFwU8ai1-VjvOPd2yi3!vqA+bXWur%vuzV zI8-IrXIXp1`D#Iy0jolu1*_nMV>N;&RuOQs67bH1g zH@?~x7MXMiHt22;95Sc9WX!3wpH9IW7aIjdhFpRSi!P-%$K8Syf3z8{HwpIG=TX?= zi$@UWbh9AGTCc(?cfF=+&?mU%yY_-}EedD!wF*i+)t+$FFF4@0UtxjEZGsv0w2SV) z*OnO%C{*|xFq6#=^DEk^u)_ diff --git a/sd-card/mp3/0190.mp3 b/sd-card/mp3/0190.mp3 index ace6bbc6cc7bd0222ab227ba36aed0e88e191925..edbf7e1a99caf1abcbf7def59c791dd7adb92a55 100644 GIT binary patch delta 496 zcmZ{fF-Tic6o&6f6JwK9qtUc69qQ7>ATN!HK^I@!hm|z(5fs$LMHd$xoE)lyNOjTh zcTn#kh_-?mGSo#{a4aGgS{J)?aS;kGg1GkF;3VY0{W$#RKR@SQWWV%nq$yC!XeyV^ znNBfFOy3alFnz^=N7ELZM@*}@6mDYC%e0N>K21wF@-bB~+F+bF!cDCEHGM=^BhzP` z2&Zk%kZBEnf|^z^A7Uz@di5d!_`+3oE-`nH0fRUp8CO2tXF?J7I f8B6NL)a%B2R@bp}%IbdAlTG!e>YTfDzpVZPXmyuS delta 288 zcmWlUKP*FW6h`wsp7L7Ns+RUyEEbc22#ZA`v2?N64J-^Mi$x?B+I%9oH$p_xigZvB z>CnL>{v`}Z!(uU6BqA}nzvblS9yf)T3+oq_{3&aZhfntthQN?W@eJE`D<)&t5fmE!Ia>c_bKP+ zyA-#Kq+Q`Ct+?QLMsdb>V~xw*Vsoa)nX0kMeAfN)l@)AoJ*UXCKUDwCo4otsW4Ok} GF!%>v0(8It diff --git a/sd-card/mp3/0191.mp3 b/sd-card/mp3/0191.mp3 index b6c594acf59969bde11f45b0f6cc3d02dafe768e..9bc915e061d4f5d37909209268eceb01e5dc5224 100644 GIT binary patch delta 586 zcmZ{fPe{~J5Xawau50dQYyK%~)iu{Hiu~5ywIUDxsvjonu0O;>C*>y{rcf zgdpWBLA+^E%Y`j6s0iucp)MgPArW5mAVFQabm`K;d5b5(f%oAt^Ow&y+L$z+d4Kp7^vl z?G+~TRanHsvxc5ZEnWFarUGvH&4fL_rZr3km_DHT9MdM&g`bhE(v-)qDr3%`XWBta zkZB8>K`qYxYV*oJ;VhOeFl7;~(bS9W8m0)wYnkHk)oB{Ri#nFgd+UwAFDzjCqNeAl zzr^$j?}Y`p4NR-})1YYz^Ou>P;@TCn+ZW+WJP2tz!2gi>fUxEsBI3*5r58hongDr$$s-G zwXT#ZlOwTQqCnE6NV%~-yFWde>QATatg;f>P|)Cc)Z-I?QNbp&Y1xv>BM delta 378 zcmWm9JuJgf6a{dnRB7w8l=4TbWid&xSPYs-YOq*L76}%M$znlL3vz0Zdm~MV#Gr|< z#z&AKA`*tiAQFZSObjAnFnDkI4L9fAckbihLSnfazbicgUCAjfIR(WH{Vv4`w_So= z#tRi^%o|rZTck+SaSPUX#{D*MZiF~wB1Vw{};!4U5Q ziY`vpDEjHC6-;oWR!#H1pv}J+vz)0D>@ZlbxZ}Pt%b}2Bj~^jHlF0_e7TX(bw=3fT zM;*Z@{~UYcT9eK9G>b9Utft{qi=F$!cH4GXaKU&)amu_g!`W8FBAuvUkVjEPn8Pu3 Pe-{kgHxX^~OvilxqHmZK diff --git a/sd-card/mp3/0192.mp3 b/sd-card/mp3/0192.mp3 index c8f7bc59a12a3b8e321b29e56376b2bc85a03575..66c7891f7e1b8f319f898f95ee3cee8f2b51d6a5 100644 GIT binary patch delta 585 zcmZ{gK}b|#6oucJw6U4VG}E#s8`Ih-@@#AxXw$16BIAq?abOC<7Pe`T8)*^5v}lnK zn4JjizYUcoXf#k#NDDV@B1mFZZQ8gf2n3l;n-<-Fa20sr|G1oc@8R&j7~aZ0IE~0` z%Ahn`5q#k!1Rog-3EpujWH3*EmtdM3$~hM61dkc7H@MH$dcivLy9IAqRSIXq2JhL^ zAXwvPBY4VC#9)f0h~Ns(Mg_n5J^CcHT;)_uP+{|4Yg$%Tm}#^YzZwM}cz2&* znVn67GS`}n8eML-${*!(KH4uDqQAvpf?rz%F0UUDbTiy)v|nfy+~B!3!9D&^&hgek zgGII-5|p^2Tx8HLSm9>7)u#{J&?k=wZu65;cr|YDl7HiZpPWBxza!OQ@P?HRn`iQv z%^XXJDoY82*YrEB=Z12D#p8m9jCUE_RbsS&a&nYn2U>gWgbXJUf)jdIiT#r+WL->F9sceD30i)8o1Wy*1=w+w)OB81^pJ s48}&i3z>1->SEtWGU;k1CeAnPv>Nzc#&P>j4WyH2k~_q`vwiIR1Bha}t^fc4 delta 377 zcmWmAO(?^090u^7zn6KLF*7goH)goFQ0(I3;)HV9#mQwQrCoM%;Ifp1W<3t(_swBS zTV^G!MHC__4m(M4*u_a{2TBfpzssj@&*ACm`!$=M*&2s08B)k6OM+KMA$X+I65KFr zDP%cSB}lQX&2X(+a6r38L0GI2yfa)YxMoG0W5S_u$Kg7`Gas~PjMNK`Xf-Irxz`|A z=0v05moJUG)}h6iQ&6V6$&}``Wlp)ooPid>6|Y*9E}D0nk{1Zv&-ZgJ~Mi# zTd>JnZH^1J!YTi3!8cPq<{crw!X-<7vnSeXHaq)7C)qxQ3r+=0XIXo~wSGaGc2HrR S#h@U<@PJ^1740GugZu~jESMAk diff --git a/sd-card/mp3/0193.mp3 b/sd-card/mp3/0193.mp3 index 4729b9445cff6a1d129c1100f250fbbff253b6de..72e7261bcadce14ce1a2f940d903ab897e6dd866 100644 GIT binary patch delta 596 zcmZ{eKTK0m6vpqVt;P0%Ac92;7EzWq&hKCk;lUX^kCpAtvfzNC=6B)Wo>BFzDpM;^N@B4U-99a(}rw=X~EeA9If;R?-w& z&1fpG)|s|3M@;Wv`Iz3I;?uMS_Yl(}c7-Jr{Y*~~4QN`xMu4e`!eKMHCoEw(sObUX zO-#?RC9Glg2vZrpkd}mUi0KM0gqcS1J*?>`?paLl(bjDC)`YK-ZP8T0uNJ0nxPR1K z(%WkCKeU?qjW)CKkFbQt#|)G0n(p9pJJT#~A7@G+*r6$cXB|uzG1AFYzz^X=%%9M- zj?Rcldn>HM>td?ocbBFYSUhR&>_5eH7oUVLaWkrE1%IO~uC;EaugLUh@z#5oo?`B_ zNw8up-bzf<7r4D9c~@9N@eI=|MEf)?VWW>}9)+_^BiIvKSU#r%JN`)XCX)S2#cI{@ z+$m?wo!$v5RZ~j&<%!lC6iB(DoR=DN#`80|@qEsiR(5(YksMBk|LYd=qZ2A_+e3*4 z^45NT()F%7*Bb#-lg{PrjQOhgHG9C$*rM32OHBujdajdI_Q2rKaO!;OfO8l2&FUXp C?7dz9 delta 389 zcmWm8JuHJ^6b0a%PwS_xDy51Kt)E6>GU&o!>4Y$lh{;PombEr_dX@9t5v;xAKxEs>X8!?S0h`?b diff --git a/sd-card/mp3/0194.mp3 b/sd-card/mp3/0194.mp3 index e2636195fbb89d067e440fe9d4b3e14b5a3ab5b2..17b9fc55d0a56fe2e18b5f66de4a2fa7a8710859 100644 GIT binary patch delta 586 zcmZ{fKTMNB7{>3Z)FQ12A}Ct12pSVA`D%fbxcI4G2q`TMX_M-LgATf|7!r-K43@-6 z>%*e=N?QL#)O7GK(S$)47o%wyTwJ<1xwyDEc=v_LgiG$1%lqE@yXO|u>r=N!v0fR| zC|4SSCC)*x#z;W$o+|;38oez7Vbie2Qj6d|qd|=dSA&9QENm4t`P;C;TOnf%M zzt&;RXFIL)kKr{g?G!99(WO!3moC8(UfLyyGt{jy&xhUi&WRqIN)xsy+=&*AX=&SoaF8Mmks&UieL8VUc`Eo4tmt0Bip#(iWX8~qtCH|3u3 u1^F5GRC?AP)lp}7+DVN%juSh7vURhoInPZiXZYB7a>Pk(5_e%^Sp5S&BfGu; delta 378 zcmW;HJuHJ!7{&3NQl%|gw7#lZUyDgXBe7UI)nKuhIxw(w5ebV#Xb=g_vB-T$6N#v3 z<0~{B8Z<#942?k|L@Xu-i^bqx%Wt@O@Bg0naVQ>H@55Unq>xAy1xdyrxTDh|xMs?t zkY>0NZIx+?`Y%={avQYi4FN^s1DYO^HhGX1>Eltvlz#WuXEtxP2re1&EB(JkzbV981^aXcOeYmE=i#8>obTFQ&b0|v=xbMqGTUxe TjdY0hVtOV%L3A!M+2QyD9Br8) diff --git a/sd-card/mp3/0195.mp3 b/sd-card/mp3/0195.mp3 index 75076972db312907c40edfe59a2b7859cf08ca07..f9b1e59e67835bd15576b922a3ab8b08bce08450 100644 GIT binary patch delta 585 zcmZ{gPe@cz6vpp2X=BdhpKL7aWn)$wMP|mA7;Sph!(^QCAr4p@TNp$eH*IP&1uYT+ z9lng}9>t3O7%__kkpdS5;U*Lp5kVU_u7Z%DO^ePOTm>GuKMwbt^YNYYY~WXJ>J-*W zJr;LL>w+ncL-3yQh+vWP5sMl6y9AH8p`2l{Dx_0wQQ}f{m=|^n-f>Gg&B>_6cXreW z=D4D~&#QX`_ZX?Qc){6P!8M+ViS~SpS-jzmxF{R<3d$@i7n!ZIc*@PXaO!To;49l2 zLbc@vi`Tr|D0s;~%4t5>7fz*{ta|>^BpBoM{epnegoVTB2^&fL=GkV!6aG*NZym5I z)O=9X%7XGEgBHOuf3{c^oj4@A(#gYu34T(3<`pOG|LX|e^6?SDDtlV3o@TvOFw60y z;d=38c!K$)RV#j*-~%_53oITJyrR=?@sLaHf}1QH7hL3)a)py8So8va*z5C0)}qFg zjWJdFH0CN)=yxS@L3f|mpC8Tj=X2hONvGY;bZ;j1U$v0GFlahbDYvtNe0;k;i8m8LVHRxti8p*M9H9NHizLzzru8e!y?d{&d?(OYi^A8?fychrg delta 377 zcmWmAJuJgv5C-r(fAv*WAJtO+>Z4d#47yk>osd|Xh^dRkV(B23E*2BbV<7KEe58UP zM9_#BNE#sw9i)qp4kQwZSS$wjF2CGx_vG%r$G66ohwzq(8DugA!3Jj`xTnt%#*gauFh3L6bwY!yAJuT8MV zTlJYq*Y^Lpf-A1I3tkzE7=2A4A~@n~hds~NX^r%m*9aV^_J;w!3Eu@!47j# Q!6Fkqf=T|UMK1O7AIV>ti2wiq diff --git a/sd-card/mp3/0196.mp3 b/sd-card/mp3/0196.mp3 index a24ac1959366a02771b38d59a0760044c15e09a5..c3d5e17397f8e45fae0ee31d209f427a15f46e83 100644 GIT binary patch delta 596 zcmZ{gPe@cz6vpqFoW`8VH2-CdHfFgQkvBHJj9SdAo+9InFY$r3F`_nYbm7W{li2LC8i3H!nV7D6T; zuQc_ag)edc7}IkEsB3-Rz`mbuHS!P7^Euyl zJa@$DcSrX^N)?n+6>=oj%M?hu92xIozcY{>%M4^Q&Zx3_lbwm)Wca^VKYL?H#Vsq@ zSw=Q;@ILH%gU+q8z{s$3Jw0ZwDt^=IvU@Cv*u86&hn0G+lU7z&chbIe**?VX!-Hn^ E4?q3DTL1t6 delta 389 zcmWmAJ1hfG6b4{_sYlhLN-0&XM;B8ET`U$BM3*jNXe1Vk#bl|W3yD_qTbg?!5(bU5 zAwrNKK@bU&N)SS%i$O%v#bR*p@(m~F{^y*#Gm zg)9e41)D5rvy7DqGW1v#(mb#V_82J_6j{`sFj-;hy_Kf_T6@N+D#1Q2Hl;gPZGw6B zR|_WiT&?iJS-aqejvB!ObJ|A^*D5{zvsSRec%5LI{(8X$PwEw(IPMVS`K!%wr9rU4 zpi|+Hw@yKv(~W{KE1DEWxZWff;DAf;&Npp=bIoQ4SBu$aS9`>;TX4s3x6*qqvrBLByx1ghe0ID-?D*&JE!#ru~X0 z5f3oUfR&guL^b<|HnU+u!t{~H(XaT<)nC{`h z9;O+@8kx#iZdCLTSDKhU;g9e&9_@8%dzzKZ{o2f;-q^=<75)}QHeR$arEs>DsT041 zXK;7FqEWOSV7h~M!g<(jOfRwCrl^XkgG|fN53x*N5kAJXh@$)W8)4ae?l99cq}vs} zLA{;nJI0Q!uCvdp zeVo|DxE{m0e%sfpNnrf=BuaXqXxi40*=G%Z^J`j71h1oN+EbSTNxPA?lYM4?ZY0~E z%bLSlPvTTp!bpeyQwzBZc`a@jsV*0}@RmMk*_X`AZiAsg^L%E+d5W<+Z=?;0==F1f Y?X0$CW;CNak?KvJOm4GoX{%WK2TqE`7XSbN delta 410 zcmW;IODM!q6bA75hVdAT!OVCKW<35DR*Ko!SeQ~27E`ifVIw6)N{NL;DUVMw-IGXZ z#$w7Np(qq078Fu8W1-Y+Y&2!z{@ulIanCuOd%x?hWXED7N|pi&v86}B8;2mcWwupt z!KhUs%3z8h%(ynf$yC7(y*7nK9@zvZ45gW!-`WTl(iPTNmmxUdjrNTFnSvOtc7;8z z+Xa1W$`Zu+nx*i0H4E{Z7Fq&zuUvo;<-bf3&Au z$v0=~3zXh_Um&_V;1Ucn-L25eEw`YB%^pDm-?i-=FH{(%r${i%eeD4|iv@>F6e}Ea zu0-%aqg3?vOYJs$y$Z|x^@@IZxJAIk+F9IE(F$!GRQeM;vL^qW78Yp*z2 zDY&M$%Dg|SG7}3`3s(89o!~-^LO1ISvAHbgGf55`ekP1DTdPGM4%g0d%E6;3lu^6I^Aq$>J2BGzkpPHVdxvyRyt1`z>Cu z`GDXx-zry`ZwdIT#p-10px_}-91_g&gYr2i5*E++HxVS9JuLXf-d2lGth5SdIe8?| z$CE+Ag{0NL`)z{v+z44b8u%pIt@J$Ys4zhT+MdUyzga<)0Im1olf;_6Zh8EW%CaWM7;6< delta 376 zcmW;HJuCxI6b0a%Ql+gb)l#Zj#bPqBFjzVvu~Fd6)Q2T1gx6 zla!$Y3DSh2lMo?YOcH4%7K8UJUtVrb&b{wzdVgwb1n;R)g;c5{*kufYGMyH|Eprxy zJfn4jEUVf)=rDsWYp>R%Lr{I!j+A?SS#!tV}lZ`IH5eK^k$Gp*= zaz3DN!ry?oFw-M=<7iOfk(HoekFj2pcZSS`xscL-kA?+zto|GCGd+Qb(mln9V3V`` Mf(6#J-%Jkh4>*dMX#fBK diff --git a/sd-card/mp3/0199.mp3 b/sd-card/mp3/0199.mp3 index 9133fe7d4d1ab482ffe52fd80dafc3208770e2bf..07a3f48e224d49278b4fe7eb5a0007d79120bf00 100644 GIT binary patch delta 596 zcmZ|LPe_wt90%~{yIiwvnPytnvYF+@CGYEUEV}fTLu75fi31D54h0dBmktraIz;5b zRK60)^Fxdh+Mq#y2cQ%C zN@IL_Meu;55WHnHAXs84ps~Qr7QsB%j0-H*2p%#P)TnSVC@8bARj|x;V~LX?jo0j~ z6};pJV}+Nt*-Rj;F~hmA;3|(ttg8`?FEj7FuteXVqf~JMdKOA_uA7t_6a8V&N#*kF>QX}Z_K`ZcE8{|Q>_|bxzcLajvlc2 zXx!eU6tDhLo8TMQj1?9ST4U`Rb6ji}+-KpC;4;^Z=Q(*;2UU3a&TqOM_lo7R=Vykz zzRd9FkWv+;RE=q-=BpaaaJ6!Nvd`U5m$M3sDW zV}3B>pZ3mHHy9f9PGv`IR~-kRV~=zzV?;_Zgc6UTj7v1HDO_ILkypcIuN{~+d^=U z4U2$t)CLHy@xx@wv9!R--(Bu;t>I(X|9GS#w=ppHM1 zTX;B1u!3Q~fDAtR$!4%*`yDsN_RGy?Q#G`_n$U{RcE(zaF{^A@$0jA@+N`XhB-E5% z%BJ+JT4X#P35MfQ*S~8)Usz-T&Xu4E-QBzAG$W%fnGfzF>6zIdj{{Sgh`Q=_K(b=a-vv&i%fV3x7s-2k9Xjmb8_9WGY~pn4aLa zFgND~R6kqBXF zq7t#x(1l2d)F2@YU98^M@*8fr=WA*&Uz)^QId9l4w}dmUKzL_75L!G64A-3P5*lp( zyU{IlI2tlM^C~1faG^)IVaHkLcGz&r@rZE2duN+Vy}}8DsG-FDsBp*Wn6Es=3^lIC zeI?Z=6j^g_aV}x*=OZC}aZjEfr4)&Qxit5Ad5y&4;iZvky)v1ba9K1TW}y z5ZvItL%;zVRRpK_VVkWc_`=Q_0rz-TLvV$uT7nb&k?!M$Q@|DmT?9LLD=p!49YFyd zZUGBea1&hOfXAwT^$6*_mtYN>>Ir7CD1FChgMerJZXkHX)kcCN?Di4l@xmwI94DIy z4)IrdgZXA_y5bjbfp>mu!GNrl*;rs69%*=J}UR#Ye_jkaM-i zChjfg6NWjaPuL$M6Z&v8V_jJ<4~_S!8t1%gYRFZo)HL)c;~|xYd0)7~y36He_6Hcy Bj6(nb delta 256 zcmW;GKPZF&9LDjUmvh{mbLZTlEM}8*7K_DXu_&1=7K_Dni-od?>v^;Mo}^@8xI|ZH zaK#|0lio?npN>+-TdaPsAXz5iC5%c*F1#59Bh?W`}0~@NfwG@Y=+ABc=w}Iddy*7de zY}z<9Q6C~`W8Y%cPSD3m2Zv|8br5v0G)!=f--0bXc5*n!h)dQ_f(_glA=pEko5K#C zxk;qWdnDgI94>L!OK^&TQMs!nc#m11-1zB}^}(3TpY{{nVaLx!XDvYRh(CgDtdA4i zU@XX?i!VWfE8LnOI7DZN!zNyY2ozih%Nstz^{Qs-MJ=tDdrrn$j4``-LJf-oqHQT> zs%b4_RB{<3rbaO>pwE~I-Z7Exkr5KFn)wm)w bwZ7>3Z`9PaEK{QKjh_!&=nI}g!zZv^19V;H_xPm*jT{Ufmp!F*#c;RhG(WMu* zTRr0kFC%tw8?pI5YWuS>#XCnaF_&vkvE;9D!e(4C<#|Fd=VwAOW;v;7F_02e*hwjh LET-)b>vZ!E=SN?D diff --git a/sd-card/mp3/0204.mp3 b/sd-card/mp3/0204.mp3 index 84debd64fce855f1334138af1d2bc3ee4a8f2434..abe50b7cc6a175a2f547d907c27b81866eb7214a 100644 GIT binary patch delta 472 zcmZ{ePbh)wcleGjJXsZ*jg4=Zji`Z39FotiENt|mC(24b4vU+P~ug`J7+kyq} zjP&%zY+BW|AvK~UA8m}~7-OX}q_#{-NZV9gS0ZXOk%~tXaW%>O{%~u6haLZ_!-?L0 z=HpyxHIZ=s)dw}bPaQEA3=OJ1v6PXrHs0PJP=cKE=J9TOu~uDEV~n>eJj4T`BJa-s G4YObC*N!#- delta 264 zcmW;GKPZH890u^7m)D)UbMB8jud-a(Oww5_wXL^J? ze*C-BD|9*P8@#gV3vDj;3AgN8&$t&eIOcdNfDhMGfMPZe1)@5!F8_cp8$REpX-CuJdD6tc4+H8KL`Uj+gYQ_Kn diff --git a/sd-card/mp3/0205.mp3 b/sd-card/mp3/0205.mp3 index 91c2b0703879ac0122db49113987cb410315a2b5..d1a2b583b5935d1508e731ae0490e9ef1e7889a3 100644 GIT binary patch delta 484 zcmZ|JJuE{}6ae6Rs#V(hscQYi!0actszoe)coC^SFHI#_EG!la1Cby>LWTZ?bvTR0F4|FJ<+Ca2Ziz>a0HwSC{ zO+n|MZjaX9&8j&M)R<_l-+oNhyOch2K|H3kMFxzN)$+RThCq;WUOCvB^WUhhDiOx( Q0zAa)L;rkt`u8&X0sa}2U;qFB delta 276 zcmW;FF)Tx26a`@C=~LROR<*RTm`w&XSS%(6i^XoSh=hT~B4QxCoJJ!5Aw&!$nj&e+ z(1bxO44p(G5e9=Wbg{b6^5y27n|r@T_KQ2icngXSyFo*!a2~=36Q0oEspoLb@mArE z@BgkwgeLoahZoj-;fd32!Y!L-o!e1|a}Ku4d@z4F+aZ+b#lrj`CY*3IF5L4u9wv(k z;f(1{p~|v($%&-1_A?oNT0bXn;Q<+{-lBVbuhiOb_=uT(a$A@+cb55l#cQIYyO1p}|3~Q#tt!M!W zH`5UsJWMNi@+exuh?jLfHaInJ>*>C0u>#nbW`RY#z{MoL*a(B%5>Y0NajLRJN3NCraxtULAF GEPeoArj7>y delta 259 zcmW;GKP*FW90c&*qb;vh{iF2;i`hi5SS%*9rMtz#AQBQ`Xbcu@4$Jo<5;2gpk@ypq zCN&@tL?t2;i7?nZ)av&vpW%kP+ZnwbDUac^GH%$dG=&q+L-=OW6Pi5o3|E}$5U%<4 z?^>tO;y~2!&daFq$eEbXV9R;Vt++Xdx`b!mIcuEl4m&;Hu+KeTxZy-XsPZjgIOJkd zF3w!13Cj%@9gUK< zanxw#54)N6@Zy##bKPS(eN9Y@C@a1q;g$4+4=>X#Hk+AF(c8jQMx{m4H5PnKJNQ=I z#C|K&0S4Nnaz3;%6|mZF{pIpYb;*gJDUZ<(rWL#?PGX}|(g-@bm^54{24HqGh4I;q zoNgLvJ#NfZT|$(F5KdJ}MVkt$Z7XSp;`&r7o197|^*N!%V*LX{VfUYDCN(i5`ZO)v zZzJXTozEKPv_5YKq-XW-j(}MWx)>s=C8l2CVC!aGyotqQkRy1q= hSk4K%e0SHlH)&XAZb~Tg+MVM{URdByo_|d(X;%OM diff --git a/sd-card/mp3/0208.mp3 b/sd-card/mp3/0208.mp3 index d670f84eea499dd0f93ddd5a2a68ba9dfdf058b0..1fd3963d728df00d1f07744bb6637913d1a4f0ce 100644 GIT binary patch delta 470 zcmZ|JJuE{}6bJBos?}=iTT2rIiOolDRU3)L!;46@UW5dTg+++PWMMD~q5dZKXao_O zFc88bVi1uK5y4_HFj*{Cw=X7%liXi!&bjyGKO4P^El24w6;m{ydSKedBr!e1?_j#Z zo7nN;#+%z z)e@!*!ljDRxGl~0$I6)2;0`F7##Vr-2CYG+C%kGim@Zeef?x&H1dgW3vnQXaO$IL!&al)b|tJxr_q%hPIM&`#()Ti8|%W&QQx0wf3l-T)Jhqt zvq}1Y=e?%YZ4BBA`g)D__;B_r>SaSuQzR;-tQu+a{P)x{jku5v5gC(>v47&u{(cre D`kIZq delta 262 zcmW;GFGzy{7=&@|(>dMd+?<;ZEM|*_tyoq}7K_EcVzC(1qCu=WPKNRxLlA_CjNzZ5 zWuPF4=)!`CAjk|R%Zk-^mS4DVJgxk8Za;_5Qr=K5wS_uYA$&0w2yIpa!!76ggnM@W z-Rc**oD3P>c@q*^TpAGW*mbYD7dCf#P^j|JZL&Bd95aX*4q1r^7o3XxHHmwo=V6YnhEos<)l#Kulve_QK7-vl;MmosouUaCY&;y jHtg~wEzGcx5jy;E8{8Z>RG66%HhAe4xtJB6`IF@z75{2g diff --git a/sd-card/mp3/0209.mp3 b/sd-card/mp3/0209.mp3 index 57ce8973b020b0cb583e3db5a4756ff6c79bb7fd..115db298414c71e277c07168194ac1821e0bcb31 100644 GIT binary patch delta 470 zcmZ|JO(;ZB6bJBo#xR)i{aqHUeROB08H$C+ywI5O5`~Qo3sNWxNtp#3DW?A{)IEt< zpeRIaBnz=1NwKl9U}a-t>%Nzja_as%-Scs8B6J^K4bf9FtY|6u$h3_KVtR(p#dM8b zm!f@`X-ubhbDVcGy`wT+(LK)6nXb^6!E}Nz?Jib4iZ)P_$+V0c?G8q=>>gLPq8Y4b zGu@)e%XEkruc8#DeN1yG$YF}$K-+<6uHE^O%ap)Eo;_Qg&vc24d`0INEU;hs)*c~M z$g~N6k)l1^6)~-0te9yLo)Se<*eYR~L`x~tI9|1*m@QL!e`z^WBaXE`SQSjo_^iOZ zVVS)~yV?KX5#m4y;nqjThNDJbI}x*j?M7#ODApN|8U4cVZ>SB(2JfG0Up&$+YNQO- zI>ddy^&ZpeG6tLly*)--bjVIcoviN;1Vd8Fs^Qkm|5`26hzeOBlwlbN|MT6%@38m* DL(Gc< delta 262 zcmW;FD@a3e7zJ?7)j8ef+{4WcEM}8oD;9%-u(F`VZn0PtL=+7R20<@JMEDLvaAlw( z_&|dL1;MZ_3xUkF0P|zJq2u(H;h9@p0gO^`tg9j<$j1y_Y4O?lU#-(Aw mXEm%m&vpHd!m(Co^d1016dHw;DuxFnD diff --git a/sd-card/mp3/0210.mp3 b/sd-card/mp3/0210.mp3 index bf44c8ccb40d3750affc85eb5c55026ab7386a55..796f8c4921a8c0528b88cb3e43431bc7f73f6e17 100644 GIT binary patch delta 490 zcmZ|JF-V(16bJBo(HJ!`HKy7c9dy>p#V^K|F8!<@F~lSvA%gAVQb9^V2PYBY;9y*= z{4KQHt0=aGl$u49;?TiCT$G@ty0|##)DAALy?kU6^5A|P_wMff_Xn4ry&s_8t5HSE ztEWs~FilM7@VS@{QE(~RhE>C~hbzZiEz>nR-HJ}|)6H~%u{x$*+-eJ0@hJL?o_eNl zIMNm|*}(J%u0}=kSZ`#S!E-OuCtP?HeZ;JfX&r&4^8KQA1_{3+8#jKY4_JJ_w1DpB z^7LQLivHnsfawf(+OJq^VVx};RC@YxkZB)pA2NN1r&ZA=wpy7M@uIDq?Na*=bB`2F zq3tp2Bs~^MM{B2hhK|aLpA7bPq*-5Je$GtzNn|95wpb zsibYh%y?=d8BZn6aUml{Z}{n;_g;4_HJlbbQX0JuDPN_ZvFuUvmGeP1W4=sGl(*uE i>`O!%MIa-s}MdJa)bsej=>FQ z+J$>I|J~>iS{!x_URia8C(d^YcWha&xa}EKINBv#@X=c1a<|Z=6EG-nFCgSO9TblE z5;QnuE+iZ?+T-s}t$EIc4Ho$o7WTQ>D{OHj;=kWU41T!~72f${J?BoJY+E{J?0Y>X zJaDC7xTY63IN?EDC^3`p+up2eTpuvVF)=7xa%o*+HYsHJoixa>n37vLamuc#)=BFG7N)3yVc0ES7c{ zm~L_EkaP;8hUo(DHgisQ=&(+TbspRria#Bel7 z%A?r8bcG?0mGbJ5RK%*+O7S-`t)ZltN2*EEE*L03#U(^Z2;o$vbZApSwQZ%%__&_TWYft^TAvlMm==x=YMy`7natRv2t}j) zVH+9m-+an2C-gb{gXt-KG?lfU${3l94@Q-`7e?IGtfrx-L^RTu7>LKUD(B|^mc=iL C{*Fcf delta 262 zcmW;GAxMK^7{qbz(>dMd+?<;ZR?H^BtynB3i^U=sEf$N(qJrjqj@Tu2&@*i8!W+#L~~Ihhh3*+`jdEvJPW{y3Ygjtb|T&KPQZ$_T$)8}r?0 j)^Nwetp78Y^P4y4F*nBzJIqZ8E4*;da4|1@@jK5ylEZ6% diff --git a/sd-card/mp3/0212.mp3 b/sd-card/mp3/0212.mp3 index 7723bb294fc51f4ddd71ba09c7ec474166e8720c..171dcfe2c273228c5656fffe587808a21a41f104 100644 GIT binary patch delta 494 zcmZ{eODIHP6vw|a9y82%zaI-WHY43JW)KS(H)+hcX%sdr*x1-uDUF4cQjEWq?~q57 zM^nZI3x$o85*r(mjfHGj*!u3xO8M9K>-3%f`JeOEe9^KLpu2cb(p>zWX#*3)^Z<{8 z=@NSmNqaC-n38z48B1k)MP-_#8=RyuoufaU=?I^SJ6Lo|T7%ccw2y1WB*rqBp5e%p zbcXdzrbD#2S!F)DC2eBT!?b|BEG82Po1tu}^53$V-Z7iQYN|SyX&L9ak`6GGXKndb zJjF^rQv$vMsdn8KSR2O+nRekUvToQeVv3`!m}v=5imR9^v7E)FmgiV83A2o83m;{Y zrZHd66h%!1Q!B0%UocXMm~I+Ty~`N5a|)3VLZqsvv|&?0ecK9~{w}>cG92!Xg!KWT z1%kD{rl9*zw?ER^E4-T4P-`RN`R(@^W{*B-2Soeyj?l1GDq~%5qfb++8f|y|XEhBy TB(%EvK%?Iu{O8=6-(m3sPXm}3 delta 286 zcmW;GJuE|U6b0~{N0qj!RV}59#bPp$NEeHV!D2CWV6j+C7K_CoG)FA=M2d-o_z3Zp zCVdtp44p(G5e5^H8VnZy*X1{zd(N%oe}>lv@md@*Y!s`)A?F~xGv*0Z?s|p`jy4EB z-|8+l3N_|JhG(9Kghx&`2^H3yr(6vi_SxSoT=LfGbEZZ3q8BmTb2B1bGar@fe2N;5 zI3E+lWUEl%LEW*qx&Dv1@W-V#xvSoUu+4J9aK)))u=4A?<9fSL=0M8a)a_nQhu=a&?$#qK!{|h)gp*!~tz%i`ulYphdK?pn}?% z{1V7LgjO2`1&g9uv?v-PC_;kT)FPrFu!Wm8EjsVvD)7MlIGl6O|Njf#?}6Jne4NcY z+?rh%Jm3@rpO}gXs$7aWtkB;fc*1WHr)va1n29^Q=hL|0B`Z4xkGZK_;!MIRyJ`im z`9^tt}tacCA71kcS#W zzG!snbt)Zt{ZUpqwWPgj`7r!XK@^Y)wtCoF& zXMCirGH4UL#yucsxhW+sm31e1`F*W+M79uKi991dE4tpcz$hPtpHH#N zd!JyNGk!JqP>o`a1>-n}17f;f15R4DR#9Yo(6Ja4(=`@yy1tDzSL>Yip0MDA*I~sx zr|T6ddLquYt%%~6{ZYjiAB}IEZ4mPnZB$%v-)M6*rnuvKOmN7hCN+CHniUq$jhmc^ k3zqp4S7f=~qL^edAsC^ZP;@fWs_xIS?!^>CZ8@a>020H6LI3~& diff --git a/sd-card/mp3/0214.mp3 b/sd-card/mp3/0214.mp3 index 1605166e90d18b29443ad8a74ffa4c0b7b05e388..c9a1b390558cfcbde8236d2d1a11663d368c4bf2 100644 GIT binary patch delta 521 zcmZ|JO-NKx6bJD8pPa^=(fml$9K$wlG`Zt66A_`$dWnoPK15-3ql?qBaTP#zii?@8K%&!2NOWx%b?2K3{m${vd^o z>9oO}>2<*~jzaLAv5?>+=R*dIbPoyMa5HeSNbrmCu)$Zp4GUH|a9Hq~+tzoyA2E2! z=3>EfezGp|>Jh<1hDr=>@JWgP=lQ5W_#Ssw{x@K zqd4TVM{vo6S8>figCffl<2lECf=7P(oc&UxSV5beerL$~#XK<;aKb;vBsZHBlk5o! zc3221N}Ov}ywMH`@;nGRW%010z*pk~lPzKlw>mW!6~`lrJ$^+5X|A>@*4WjqSYX~b c!|AACfPYa%oavaleyeh$2@ZFx@H=Mz188!7q5uE@ diff --git a/sd-card/mp3/0215.mp3 b/sd-card/mp3/0215.mp3 index a753fe88bf34d3c591dca5954340a7558d314973..44c994f3f0bf8844d2d059a98ecd74484910de5e 100644 GIT binary patch delta 524 zcmZ|JPe_zO7zXfpHg(lq|7BX%=ATO!O=ewo5xn?WA0q3nAEKaM^q`jzym;!Os0fN+ z`shLDMTGRA)E<%|3JeUgLq&;D1YJ7hsY@3x9h&dMQ{W5pW0-g5eP*`(i}NUn)0LFL z;>w9&hZ7KdWi%vs$GwojQ@U3KFZeTXDo^l>v3!G1e3>seVt;|)IsaI9xg0jwU}K@+ z8Q)uvI94Qh!cein61R#4-`N)NQ@%xvy+0WhEV81+&&pYIOqUwG=2@wqI9DdvXG6I_ zcvx;Q&!Gyjx#!kHu3Z(pVZ74dfFCObGkkDO(9Lj_!73kD3C`GFEjZ>6YmN`E8=F#n z!=G>v=+y}J_`AknlM6QmuXyK{V4ZKR3ml0V%<*qbFwTwJ0*|R$1I?3K!3R#%txY+e z+vjw;1E0f6<&;u+cG=nvwAkaIGG3z7>BI@?W<gJx7L{e*AKiW}v*;UVV(n>ccbV|3RF8S{C#bfmk DVkM<0 delta 316 zcmW;FF)V{&6ouiOQl+hGRZFRg#bPq(!cc?BVv&d#EEY=_gIEkK7TTU_$h~PILRf?d zsSXt+2+_oEX(SRMVY1X>zfn=*ow3Z>CPje`w_uZbx8jpS9=lTU zh}qA36*&f)?be~O$T6SbmR~-*xYD9HW3OMa&yrt|;Z#6P_t$vOY^&md13|$BAA^cD z&b28f=?V$*+zBaum~XbIal^hY#Wt^v8=Q#> ZQv8c57P!@|m}WdC7^W3dRG93^{sZief-L|5 diff --git a/sd-card/mp3/0216.mp3 b/sd-card/mp3/0216.mp3 index 7768a5aa890890de3acbed450e079657ed206c1d..53817efcde8a67f9ecbb065b9e549eacd43d64e4 100644 GIT binary patch delta 535 zcmZ{fPe@cz6vpovXBu-Rb^Mz)scqUg%GF_}fm-ymhYaIP4{<;vu!U~ixN#+jB4Q+g z$tOYFQw&&v!px#bT)8li2>OErwQ19$R=IHF#zp5oTm>GuKQ7-n_dDM$IY$FeQuJ*- zZD@7hCX9A#I%p%1*Ug67QVq+*w8k* ztC?QmgRq3Di%icEs4=vR7d1>3+>9`7;77!WJ{z@4>=M&sRD{FG)f#$@Kea6W%XOC5 z>Y2V^uinre?#1ku6X7;C8tnGIMkCoiH8MTKbQ4no;btSrH=AwF+bv8V@l#mFT&t1! zmdo}pZ-r%eZ8qoMHY1*o+HI2SSD2pSy>JN+;)dpN8fUtXXIGhSA>Cof!(j(g95bC9 z$^^43gjm$U9?7-^e8TGO5Ua z-D2U+UDd61GU1~TJ?oFS-mo+78;p)PgSiQNRXy74?N6q()?JeW)#r_Ru9H)`H>uOQ MKYdQz#k0ri9}?WA)&Kwi delta 328 zcmW;HPbkB27{~EGvl-jWW;VtQ7bzzPOSvG5TwGR?gNu^OE>12kQoGpJN22FRi_=Q- zrDZx&`QJgRX#Rmf}g~O6d zP~l{q;+pUOuDcaACOm>?UU(D_OxG)}STk0c^9srwXi%K;)_BCFMn#D(pCHeDpW>1u zenpW_elhdafSrV!6uUe&&T%FvIOAtf&HjyMYi~&L!fHrx$N8|m@?$J>yTx7~jEHHw zk0{8MR>d&AQ8DF*QQJ8hQ#|t3Sm9cmnD?=E`LD`C_>d8_jRgkLrcEu1Ko`@Z zAe`Y}klup`T42y@`alX2jG|2v64}Ne16#Fe)22mt9F&bgek-S@n|=-_iH zZE&}=E?DGM2sRiC3Ep!lWbl;U9>Gg)1x{88wiyo_tZ_LkSmnrG!3+MjE^#_y@PHlF z0^tYiJ6_r+_`*<)!4&6e1P^&CDwyThsKFCniwVlC-!Hh#vejp%)?l7LYX#p}I3QT( z(K^9vzNs^K%=7hv3I4H;@%}-t5I4y4Z(PvJnInRBrV}Q7D|Dlf z=l8^I78B7)*Z1=7fH(RnqEuNaRb`Kj`9O<(4kGKP2He5icy=(Cbw`!%aXORTPV~R* zNbc;A>d-pX8IX(ZWX3C3bwYOyIVneL-9B-;dbd*Fb2CbJrF6O{ Md2*L=r*@jvKRZREZ2$lO delta 318 zcmW;HPbh-{9LDjUml@m4W;VvW<>JDDgI$z%QOd=EiyU^@MTx^MT%@KbW$TezzaJ$M zlKc+`g+ht4lUBqOrBJ)LIC$ssIe)+Vk++Uy8=g~P!F(#C*yb=4uXGuT4A%|8F*}PC z=gj>(UaZJ7WD{h0WK-O+uS9XiyyXFB?SdsXl`7VFZF$dFnIcESA(-K!L$Shkr(%^4 zPQfALF2xDG<%&4BEC<-_7Hsm}t@vP~Lh-}~kK&4_9>FgAy^0b3SjIS4sTg9bPtd|! zpJIW7RfT4|U+~3czv7#bfZ~#mmRXKf3vL*wDZK1i?yx7QP<{plX-?NFHrQCFNbhNA0Uw2uh!7ha3$4=bN+G^~?UyMrY}` zqIc<2rgcmb({ETlrhRPr6m7%FW7@-|$N7AwD}?-tPVmLg^c_P5OuP82xrO%uMW4}B z$n+J5nj3iaz+`+y#=I;l}SZ&^ogGnfiD;wC&p zotSJ`nYUeM(C&4He+Goe3L*0K(e=rrhJN-c>Bf5PzSLNn_|4i(AwgrzHRY delta 324 zcmW;GJuE|E7{&3NQl-7BRjromN*9aCpo^sjiNw;yVzL-mN+KZ^gO5bggyvk5=6zTi zgMo;T1cL;L(4-Qzh=d4(iJ?mugZH)krj!5kJPt0UH~R6OPbqBWD}qC2A^4=_5mdP6 zQ8?#No#2`u|1Q=Gsm*A`5(&?U$)Zubw)c8DHaDy{Ihin+#aYL5 zN1Q%)Y%8o(NhwukpN-`}i~SBF@1^>jfx<+7ppbXQR5F?FOkPMw|ErD`dWTep)~U{b zLhPtM?0SRFm0-chuyZ*#;isxgcMtWX(pu}YS1(15YxP_wr*wBpXLL{Im~-cjhSfhd C@}qwM delta 316 zcmW;GPbhcEE-8tcCA45v7%u zzs1VRTwEw`sYOa0oH*>_;^6nXe0mN~PtW7fa<Z<&N3m$4PcOhNHMpQW&ww**%l zu2Gcv{_kR~qRN;{@XSk>;(-%&iW^pq7hHD>j@VbP*yNq@oHGqh$MOi$-0>)$IOtVe zv*H!(a^9y{VX#p#&Vn(=5x-!YpMJ$D(@kpDx&w+LO93%X1)a4YW0Bcr#R2<6f<4}c z6o;H`aXz@if^Y7J6)#Lioc^n^%!O7lM%tWwW-M?ls>t&zDmdY4yQ0M24#gU8jZ2)4 a3Fi14Q;c$}Q!&XzT+l;1uBb58mHh)6$$*3a diff --git a/sd-card/mp3/0220.mp3 b/sd-card/mp3/0220.mp3 index d1a7cc993c0dd5d2f8772e30e5732e61dc712c29..38c0a5181a9638e0be47859d51bf2a8b08c5eaed 100644 GIT binary patch delta 514 zcmZ{fJxE+p5QXn--e%QZ|6=@$yAT_j71@_{S0M^M^&ztE`Va*b#00cSlO|ONm?8!e z1eK!@xT7edp!kE@h=PSSA_$_5O;T8BWs}B23+Fwo1PAWNWzL*)=dLx}HBKbxbTX;w z=j0jF96l4%IYK_B11$SAEyI4nw1rELBL>qY!hTK1*zq&%qvs{lCjJW-F&@w~gJ>4h z3XX(Z7|v$;2Vah+UCib%t>S%89w9}y_l^al&Y?o3k&(;@x}SMase{Yyz1 z(-O9Y8*s{*_Ha|KMg8@LX%Gn+8>Q$zhtTyWt!6#}@rnOT=W3k$551G)T-eo%- uR-YHp-DS0;2HmR+)^ygHF{!sh%~{WlI<}Qkv6^>@x_CVK%(~wn&FTTsVV{2h delta 306 zcmW;HJuE|E6b9hD*S$(x)#?xB-oauqbzni##bmKqOkFGz5sArSVURSTdBxCkA`y`= zv>`%hgs4RbLoFhahz=%(4h#&w+vOR~|M?v&jcyF#tuQRuD%2E*oQI;$u%)Q+z!IEu zyhTyvd(&lG(V*)HUU=px9y!&jxMst6!u5cl#KAU2g}25kXWA8Cw1R>=ZU+??90{qJ z`5Y1)aWSk=#yS)^mYYsQ#LWMQ_+2ZVYP$NPialON1>c;G`H>%Eots_$#!OtyoeyzE zp0f$X0s~3GK6jITO*W-?;;XU3rEdS1RFC46$Hps8rWN=6OpD2`^(x97=<~fX7MXJe TbNqAFN!XcHk2_wTSvSBn!~tt#i`oQXL8~_7Vlo0L zBZ0V=(=La^<2n5foxA+O%kbqD7lFZCZ5i!&Tsc`{QuWcfRw@WIv6}Ww18y zYRt^93(A~=;3E?e!E06`8Ws9m1kYIuS*#P(n2c(?<4RPp%EDH`GyXPQ=B=33_tsne zTf=3Z-zJ!4q(Nhviw%MYJQf$s@pD|8{z^iy$mZ>W53CwqX0}nAyFVK3mC_FD-_>N@ zR+}{L@JzE{fqx9kyuDNKo#_^hM||HRDDlEB!9~Vet;15QbvWK8c+4+`H+glp#$C3x z3#xo!_>w_~P57%r;~{VC5!~mYy@E@8V>rvRNp1KyDfq^PeKx1tX&u%(ZHB4+R-Z`O zY?YM8EBakxSTlUW;sL=DlieEkxYBJ?6?!E4wbXw7Cvor^pZ3IyUf_>=L;l2COsT3; zs?I!9<&XyEJFHxAWXK!NPv(a6Id4Lx({5jSFcbf8TF9RoRlSaLv@b+Hu`wU>gAwm^ sXfQtJoy<=G>UQY)Ma zD(!Dj@Wst;vojhp4V94DknAyeXRq0o?^U>FtWUI6?HL#P1&0iV74}#Rn^lR3SU*$# M)pufGnT3es5976*WB>pF diff --git a/sd-card/mp3/0222.mp3 b/sd-card/mp3/0222.mp3 index 68a2e21487fafa5bc4bf65728aaa2743d65cd875..378515252ce9de68d40af36994c1c877b4563011 100644 GIT binary patch delta 604 zcmZ{fPe@cz6vpp2IgL4!Y5pl|vN3BmWAb!z658~thYZs^AL4*TVT;REyde)T$P4+O+7t!Byaa`{Q!Hb3V@Z)t&Xmy6|y4 zr7<$TD7eib2tG3s5IpBhK;s^rHG)T6Dl=ap_`zsU<1Occf@jRF6+Gm!VTq$5jq6NQ z`kj}CPkCycV2pt(jVqk061?KRuwaT`!Wxr26A@fu?Rvp677a_xRBO!hXSLuHFKiIZ zvZY2);Czk7Zyv4nuli$H;>1QVlO1&$clf4GFwN8Tf(eEiG=_PrL2#b?8wFSS&G0qP zZPNJ8#?68cd|-HuUX$Q1e>G|2HoV36jBgd(WU1hE%hkZn0VL zf-ywLyXsgC!&b12eGq=-Mmkmcax=Z6Y6LAT8Dt)n@ z@8g$QhzIkw=k(h>&cM5nQbnayg?XlKlxbkv<;r@=9y{$0X47ug9#B^Ip02K}g@SYDv`MZMDMT44Hj_(jnxiEGp`}z|otveCdv@Cd z`+T%1>~hL32-8s}7-LMEWPiEB6+g-aS{roT~e zz?3$|5x3F_ziz=CH=4{3UCpLlrde>u$rh7f_n7>sN9jhrUehb9J>^WR;F@lqLW&8W g;EcgGv(0HETxnMrXTUGk;k>^28s7SsIP16m0T*|oaR2}S diff --git a/sd-card/mp3/0223.mp3 b/sd-card/mp3/0223.mp3 index 7075ce0a6a00ae3557630f6339c3b2127ca55afd..1b94c962884dd67b18c75d9e94d9b6fac7c08f0c 100644 GIT binary patch delta 597 zcmZ|KO-NKx7zW_;PL5;FWMigfO*Uq^(d15(lhLMIUPPulPU3*Iv7k0>Y|+ZixTr-@ zsk{~0`I4X?n0^LI3Ne_VAR_1oiHe|&i(0jDQJWT>b8!`T;5>ZX^PTU$=Y@Z1V6q#Z zrZO5ArWOQuI10fR#zKPEEQK^4(OV^W#>K#5mEZ>xVU72k3kzPdP%U`MC1Z&b5shhf z)CeB)jZt`VwKY_$G0vGY&-qO@T>EAl=#}vtk0mrt^KZh&o!%mtWTr*qITu<4_c^-N z&WR<({Hv7IxJR#5Fv~^bJd4}JbSB!gspr}R4_MeP=D|zGF;48zVZRdZRZO*CE*1)I z-WziJyy1@#rOHaFDznYH6=*P@gXQPbeQtkl#P84f?yyRw_H?Ft_D26ZE#!I!REOiF zI|FjDX@84`xSfsdgTAx^}0#9mh$YI8d`vYuC&AMJ2rhW9-QiH7(JE4y;aZX4oE;89 zoG%WA4URblTl5qQmbs_xVz+D6vRnLinTW{}6J75%Cfy2Y4tN9){LQ*pYUb85g=apN z2@)JG7u?cbp>V*>3c(0Fy{6*3_KV|{3UBmQ33hp;U1y(9kmQd~>7D6nQ?9wj6uQyI zIOtcH;Gf^rU8xla+XD)BOa%ld9IZ1s&YHi+Kzhm@WZA;Bpl ZjiMjUXs0;eq-_03cUKNuo9C=Z0kX3nVWya!Rn$x9rEI(Qf)&<%78?Y0MuQrkxE2(wGk;L9%-@C;J_%{OWN)M3 z4L1#IynRTp$Uu|E11>e${!&=*h~L9jc{d^`u=TKDhZVzj%rtAm-DbfmA081@+1(;| z#q}0#{BN{c&p$pFj|#pr*`^`<(k6J!>0^TD47F=a^L4x3a=F9me;8JI@3_W1J5C6e zxN5jeuhTmG>a-i@PultUQ-WDm4R3NPs!giDQS0#HwBQ5NUD_n6b=jZJoDsZbBxYHT zX?&#HEqKqm;R=gqZT@JF#xt(<2<|W+mtb5?ef~+Dn`J2;E;^n&;S9Tlt&ma`rBs7C zrb<2y%-FB2ml}3Pa+BGST-GV5WHQ~Cyf_g4?>3*iHl}(LiGe;JxyauAxa*BN*L{PD qap!7g(zZs}Kb9Iwn2O!H(zu`0bDfMz^j{bpN~O~K#J#`wSp5Uz0KKdL delta 378 zcmW;HJuCxI6a`?XRMDzd6{Tnui%CKki^WufrHiSHL@bTOqKgHIiRK{W-H3#UCNx3_ zh9+$n@td>;kr1iDg27@jc+c|X<>ltwb00<)BFh7KO@jVG$IVvM8K$ zxLlB9>EGE3!8iR@g(sd_1$T^A3bOpxX1HWiIACv;;Dndj6343rJG9g&B)M5*-h+0* zIv?$3a@rx7pu1M^#kBT<5vS6=I|X^p)d`9Wx&%kexRlNxbDPYce^=@SFYNOu?DN(m z5KcA-_Gt4eB)IK0TZS6V{Ij;mM3ce>8=D1b9&584^_hfEpV_$BV*Yzt1*^UGNz>NCrHuM2YVWw5&%b8wbNB9iW5ks@+ zs$g2cJK-wE_cOgfsM64FJg8*)hLch2@-=Eie>KK5h1vtwt0XKTTV-e-zp9v)Fa55;X*A_5r2gDaOWTsqqok8a=nh}F)kmnzL9!ElXz0k!qW{**YHF56E_YU zac^j3TEU|5F8n5@HT-TelJM3Mo3^vrI<5&9a4~M^KK{m;#xdK%^50Ct&_`?}SU7Ri zYGO&I_n1rC%x){wb8HFUpm5AG-eyFz)Ml0WcAIcVcm>lPCVZ;>`b;A>I}(ez&T1++ zzB}R!xTBvUN|lsSWpbn*1{6p;sGOe~a0b1x+@P0pMwQmOyZ1yo`rkC~^$)2otyA3r zyx8u1*!9mj=Ys_!!_L|4n7yjwx@V{_m6lz}OJ^$flKQTbRk|moGrBLcN8FpckJUdI Chx2)kb03Szz+$l|C9+sd7K=s7!r)34le->W^nV$Alv6Gr zrDM24NeXof`6wU7Aj>TlgZFRw4R239&$~19G#Ky1OQOplnaBzDI03;sT@FExDTl!^ zdkX|-eET<6D9F?AGHehb)SpH23u?|5oCF(-f*l`kfEc@V3})Wf)93= z+m`!sqxmV9AW3h9?MkaB?5{L9;%BAcg7a?M8}x`?vL1t7j(7#D{82Z#R3+GEN43%9 zb+sVP@fzD(>@$dS!zXG_tzeGN>KA9~jJDU-3$A#mu5+kCaLexoqYD=r?b>LQZ9G@A z9Q7Nd`0E!;a-~`H->!hcBXa>!Ct7UA)hf7Ss?~1p4GIqUrk*p_X7z`RW-=k091h!s Xd3BbH5##I&Psi)|8VMW@MBVrUX@jAG diff --git a/sd-card/mp3/0226.mp3 b/sd-card/mp3/0226.mp3 index 2a7acfcea8b25eb7de0ced05b6265f033ac42d8c..55bc7243021b9993c459766cfbae2b1279b905aa 100644 GIT binary patch delta 610 zcmZ|LF-(&|6bJBomD)-x77+xkSVYTEBuAT8jShb5mxk1~AEZst1qV%x3o&tGse^+u zi1^2W+zU}uOik>dQHdrD8j)y3f<|3jI>@90i!pKVeGQWdm)tLxyZ2t+yFdB2{Wp^I zc|2ukbbOiV9)^kOD}o-Td6Yed9>Lzg^bFOSr8=e`2zw1JV%E#_3dM~~Pq8YNG3GO3 z%+K@&3*t1+ZE|aR>J3d`s-Ecu4hNX-;9J1ZBrXM6?i)8VokvBSMyA0~6)O!)UvOm$ z(?@iKnC37SGISrO8<|G&TYQI!txTWL+hoKKO>WGEZA=yTnho8;-DaliINHK=6W_%V zjBGcwf|gdMUw9%e!f9ig!p}BC4{>dW+h=sAJN0YvG0udIO#L^^Qf+b)OgXiL}+18_M?Oveux|$#{3~ z!2^N+UW>UCc@@(-(On}KTzem|os-t7T7klVbv!fV9@T!`laC)tYONz@kNMXNb!;o6 RbWcL3bUd|AyO-CJ)jzh}$jbl# delta 402 zcmW;IJ1oO-5C`ylOO-0>(Rvm2s=;K?#bOZ>10A}UEEbDMOcsj>6Y`ZN{4Na<1BnI= zDMJ&QAQBQC5RZ@=Obj&$F}T0wGhFVzxx2H__rO9YUKWD}k;SxNhhq@D(q$8*xoI;v zWKWLZgqeS*as^rX><0Ht*acS%=LwFPRrfjXFlu|g;F{-Zj3WhBrp;-v%BWLt%C17e zHtz}zHaOuDt(O)FCKyv=>@PO>;9IdE!|4*i0~_6f3ntwLyBsPNMEIrNaH&l2%#L!S zUY1)uqZNW9Iy?qzT=xi8*zFZW`J~QpveMv_-YUTl%=;Xp;OdLG-g~^1I+%Grx|Ia=Dy?=@yugqp> zb1tjt;oKI}A|{FHDV0sE)gQh7wZ(yq6T$t$@zJ)c-V=BV*0?h}RCQuVrkZaWR4nG^2e&Bu+ z(|2@5ncib9s-^2%Gt(XX7S7_yA*N~cw`lr;&n-+fOdK{0wrWwAT3IHZZ!_E$-p1V{ znwHRZlqLC!a2;+t(@Xqn*YpaHkD0WIf8xTBdJxn*5U| z%-bVzlcXBg@3P&&o*8au)T%$%0bMl!)v!bXAvL;j(B z2X6H|LbNATwq0k`9(2YwgG$wuQhr%diyj4X>{Z@P58A_p@%(ThZ;vT!Al2JHkO}{% zmI{}ODrs3~dOZ{(yZVUZUbe4#7mSYB7jxrgE5^QJDr-r_Z(IoNWpy1pr>wqI=4?8h L-6QVwZnOFaQ_-T8Ce4JHGN#iEpiLCIvXSfot1+#*rxTwj!aFDV&L z5qIU^Kqz+$!&#(A3`$vUAZ74=me2Cu>wVwLjQ$L-bmJw_W3Zmc2@W^|!5iHUL5@j> z!72L-1Xq0hH|7-N>311C^28;$<5;2Kl6m!v@gm#bS#0~W>KUg>1Uqz;8q9OERFGnC znP86(Wd=#kxdn&xmJ6cXS5q9UFu37+h2V?xm4XkpdITAsdyGy^cm<36R=2oXC0Jos zwZSuQs|DwruCW%?8m(^Eif-)lS)bGeM(YgH^wodpI8U9?il z8oE(hX1a$_V)}wui0K{XLWUl~-Ny7B>j8@uOg|708~T8SFw+|pwlh7$hVTg{BZeL$ zRmoJsd*NrCuVR`)sM^pK%v3X7!?7q!z8W>exEy1;fVv$_6DSKukgGBD0zYe5=3L#$ zbQ>+TOp92oHS`*1>X^#-BfNuKyX=;9y%7&f_159YZtFkNV3oTKcJJ{DC zq2FlSYp;7MoQL1U^Z>t_j5xo(&)$;UZm?Bc+|@8|OA_3FL%eeuxCz+x9Nv2KOsSX!{h2n26*SOjVASQL&K z%okiT`){;BkfYbC@XVxD@W61P;DR~rITvgSXABexR(Y$tEv8P0I z{Zyi`%~6M7jIL5aj0x>H`^prq`B5f%&vdz9m(5PW9nYN#DGs^>C;ZkXxKv@TbXF>z zAysJ-j#QcaHn-_)xXs<(YQYR&weOs$QTSnXt@-Xyd&H1OaKJB*(#hxQ%rE|W)49@~ zaLB84{p%G>a;3o}?`~9jXSz}F$w-qRPKVF*cYF$)3^tqDtahK#7QsEetqM0xwhB%e cZZqbzTU=;Ym}0;$)=XYUe~OR(d9M4df1!D!d;kCd diff --git a/sd-card/mp3/0229.mp3 b/sd-card/mp3/0229.mp3 index f6a38876b903d17934b1bf96fbd7ffea03156459..c8401b41a6d4cb225ab92278d7e999f5f1260ee0 100644 GIT binary patch delta 610 zcmZ|LO-NKx6bJBkCa0O4$u!fnCL6QH#hBc2nxSaps~#fb%)G<_Yh#OoY9m2Jw3&kF zYoqeF$nHVRBA6&4C6g8|>_b}=F$6`>#zn2#xQU=e=RRBsUbsIl=bm#o_x{Z<9v@E8 zmyxulJ0oSL#~380uL%2?-lOEx^c>Cxrdcd|7Au&3BI4Kd33Gm?1r#Lr9OXkN~lXHHX=^UujbQh26OipJ#(>44MFX75| zEibBXVET?3aS?8#neca`rl+{Q!@M!J)A&Ywf>RL_{}*Apfw5gomyvGLG>meSnQd@4 z%bejT(>O{|`8+tyOb@UuPNKMnX&#XlEzY$ty+UCx(+pO`X$W}~*#F^K z!_Bw2(0U81HCVJ=r_b(k`acIOYs#{$3fZzIJOv4RE9)k^?C#t^wmX-#`>jOcKwIKq zDzxUdkUN&Q;z}jkJaXaH`(DTGu}^pn`g-l7nE`XO_N(@M$Dx!`Dth`zaJ^L5u``xx QPpY))NUt;Q)zxC_KPd&skN^Mx delta 402 zcmW;IJtzcV7zgnF$N9R$`8s!;Z_Z$0aErxaqA*xY7K=rS#iFDz$j4tw?~{BNol8Uv zPCi14a#B(xipg?|l)>{_e#6uMefmG|W&2O-R6Sm&Lk4rxF~K%_A$X_TA&7CqVQ|Q% z1i>la{vApb#2IiJJn_IOxMfF@;Dm8?kCVwptw|Bw@k))bJJs%Vq!}!7Ax&_>2A5!q zk1m5X_Pa&rS?Pj)M${HIXBfQjGefY-kxW6DWgbD4QIAnOvjj){R&P0*ZQs`A7){>f z*i28Z-JG0fu*8)-n`!h4Citulb1>iNMP8ragS+ZI+x)iSm)~HY;|2D{V4?L)-C;ZV8Ynt=+V9#ja<*ik7s fW?YSOvdUnYH9^4uuY%%y2>V9Q*;hTr+Uk@)-kPQ? diff --git a/sd-card/mp3/0230.mp3 b/sd-card/mp3/0230.mp3 index 0edc96c777525a758158d66a60e4583bd3073908..2ca02a44e3967672aeea31949bb8a58340adb6f8 100644 GIT binary patch delta 494 zcmZ|KJuE{}6ae6R>Zi0ti`MVJVrj{(YC|j@UK**^i)ey{fyH7GgQW%w3vvvSd(!x+ z!Jq>n7$iu9!B0fO!eC&sFj$@UVv;z?{c>{7y(i~>wVk#vh2dd2qF`b9o*;vhK=6nz z8^Hx`+Z61eoD?f;X(qQ*eWac?4&e%qQ5#Pj(j=~|<5}abNOu;Q)mk~QrZtiUND%ikHFYzWFKH~hDP2)_3S@%_%f7xSq(WoNW!;dP( z&Q}vmV?zx=63^LH9IeHaX6OT2R3E&v3y~E<wwA(Nruf ze<>lBc4#6ILWDsgvD9F7pXHaAyUXRSR{WXV8^>E|!r`FQ6slZ-@WDhNG+M2|g^0rwFCs#Na~(pBEi>e9)ZvVyoj%{2RW5Z2I}BnD8!X3U+lq1TW8C4C zs|i6&cMD&vn0K5>I^6OzDZFy4M|j|HN>&L|&bsqyKl5ugxZf+(IhJvFW<4WwxzFDm x%{o+gn3Y{Jm6PQc^Mq^tzMdQKznq&PXY;}pzw^%A9uzh?G9)bW#ysLefq&Q?c7Ol? diff --git a/sd-card/mp3/0231.mp3 b/sd-card/mp3/0231.mp3 index a29a0201752b783f254ee1cbda29ce21b7ef7aaa..25e5e42b31969d133321eab0670c6d60b47634c1 100644 GIT binary patch delta 587 zcmZ{gPe@cz6vpqF)G=pLC)2WCHm0>vCP}zm(>=UwH`y+97|)LI4elJ( zR7S@!mgMh+75JTozdJQ;;=ysV@7WVfU$HLS#5GIHKkQmYFP>ycmFm*;2-{sucQJd4 zX&%wI*|8MY^buaS(KX>~&Zs+rw59#4Ru!eKN6TU|=wh*SVz5|D7K^2eNg`cnVi1pmMDC|)A|zcz zh@|R~AVDNV8lfQ)sljCFVllYa^2-@cZq9d~M$*H}1Nc~pD{QP31^Y}w@JY)iC^BbL zIA)?+aLMw&sTx6rA-lpW^LD`l#~gwSR*7R?+1y0rpgf^GLGBYm05eMCZ zEK6>s^Ry+HrLSI)TX4WoR3XEBR4~G^ Sm~w=zs{XW(=$YqZ%>4&f@|vOm diff --git a/sd-card/mp3/0232.mp3 b/sd-card/mp3/0232.mp3 index 920f7010df32f636e8f807e760ac7e3caecb66f3..d6354220ff7a680fd56dc69685d55d44b06679b0 100644 GIT binary patch delta 584 zcmZ{ePe@cz6vpp2X=Bc0nrT^+jadrq&piQrOh>SBn!~tt#i(1sijjJ}(!c9S# zeJ+xFim*R4kP{?{1t}Ov5Lrl25D~bjRS<64wCLW4tH1;2$KjmseCJH}{m$RY;L}9b z;KszN;2}pK_{?}ju*`DAV21t{!5r5@mZ}6ln5Z^b;e54ViN&pg7hJb2b3AG+J8A@X z`PT9|FKrXtVWigJ5%1NC-8va_ZeL=qGa46+vSGU*V8wEoxjJM0f7S`Ub8Ls8!nS(B zA{XinUh#Z`yZ9&M0i<~PeZUfu1K&3gn- zIBPk>phfVKzgmp-yuMeg|B-!y1-`L-%?k;G5B!@DyyMh%UPBVIwsl;o^ih2X^Mvgm$`0vl;ek4@&bR*>+y#^MwO~4rK)V& zxF6EOw!_E==^n4QFr4o#!4N*(V^oyf%gn-&Xa`&Eb5>CO;^_{My|5Bj`wp~2vQ pcP2ONN_9+k^>?Q;TI=M+(>0q(1K-Oj-Idl^-JRX!-D?}m>K~sxzi9vf delta 376 zcmW;GJ1oOd6bA5o^`=Ty>-ATyVlheRVzG2WV(B6Vi^bA~#ZrUCq|GOi=AJa-F?4A> zlOm}g#3Ky|-eUF+c@%Pd z^qBX2tGU0%tB~ThSCHVCPmtxi_L56&Ch2Pz9PvAFSPw86YCM|(isq>nF%OtG7=OVu%JC>yw_-j6wa6nnM|}# Mu*i~jm}~v~2X$SXa20sr{zUMhd^Lv#ne?$h3e^y`d%CuV*UYYy(S~4RHzA4jC!d zaM(8bOne2u(SGblqmi#pA7RNqag^yHR>j8{iyF!Q8)aI&F{D&U zDODy@#zLSV+rh~Ai9R=#9nGY&8FyI4U(Zl>8^xM>h9z&?N07IR{sG17Qd(f delta 378 zcmW;HJ1oOt6a{eS?@`*SR=pLaSWE_8ECv>j#9}d73@lwt7K=q9XpRW(W$=oMh*SuI zgocQOp_34a)L@cG7mLC7S$?_0%{lje4{Y==bl^Q66)eTmibKYr_@LKOq?vF8XY9#U z+%WU+RGuQsP`==Wr}>H}4izY_nKd4B-X+*(TcP5T_r_h07Fiv)V3zA{#W_1YiUglM zf(=f16}H>D)p0re^x25%5*yv&5B&LevDD7(K{3UTL3_%0 znPQ)=azT=t<%%_SRj8?XGbTA%DWv?FE|ns zlm8o1ymF;Z@yTetnEiCUVvn%~t2b;-garreX;j=XW4z*2lOn}XM6koth+>XI&5AK* Mo5ktP;d~4K0KV6oApigX diff --git a/sd-card/mp3/0234.mp3 b/sd-card/mp3/0234.mp3 index 2837d96fd8958b93ec4ba0515c1f6c723b15ca4b..3ac89efcf4826c01e806df6680065cbab5688bbd 100644 GIT binary patch delta 586 zcmZ{fF=$g!6o&5~8&jKDQ){c$SW{bFT1sNtRB-XBk5ba4kJ3nVse@fyx;TgksiMU? zsP(Tyy+;X(VibZJC`GV?g(`wjqJxVLS)5#4TpYaj(MjkJ_v3KSIsZ9lHuH7#?jY9Z z(;Bn$H9?8f5PV@IAb8JmK%-1|i{K6GK8p>4I%7eNPh1WPR+!%^c+KC272XMHJY;X9 z;3+>E);P6Iu)si*##KIQvhlI7;3mI?ZRN#?pum>xf*-6HzF?+V8~$t-RC#@eV3l1_ z!DFsOwedgOB3R-d!)ILBDR{tStHvBZw+d!>VV7m7P2(D$v&zZ*X1<=ys{ z_C11ktQs!S>#+O(>aY{%_S*dq?-RV@s^J>X#WX(fZ_M7ixZnDuJ2mdJ)+uJ<^Z~&w zM&g1+mgCyExn0(wZdhgUpx`BA-5R&K+%1@5zDI)5)0b*jh(kA6>IoMe&z*3F-NJfE zsftpn!91gqPXqJqE9<3(osryRb|jZ|3M!dQ_a%=Gg#XLtbEn2sZz3_!=OY){$d9|; vsB^|Qm>73XW+rWD3j4=WLkXk!`4f$sm3pp|QHlN|gF~rQdXsf$Hk#ExBNe?N delta 378 zcmW;HPbdRm7zXg3F=Lw*f_n~?39eYwUU12#u*c4F!7*>O1&&n+wrQ~|B$%?BxyK<` zM=cQgqunbkgV z)UWW&U%&Y`*=%wm0fimr1EL$FErNAAgMu{EL8a6BLMEZ0%`(<1IHy0XkYXk*nBq{I QScAjU`B|cUo*QkBKbC=;eE=E%tDJ2VeB-fLaOG>vs{T@3FvXUw!L6#Y%6yZ>L;h?MyyWDz;BHs5 zXlJq6;x5m$2rB%eyv-Zi1;SLTRpp0P!8|YS2$az_i%H&V6I|o*cEMSGSI%={r&WFX zu3-16@;&_y!6JWk1P)hr2N@3S2{OM?)_5*q@sWQMqMX?qFxP1{&q}9Yj^q0Tvy3MN zH@T3s>hE<4UU60Vg5~{!TTFCYOt981@L4(_IKnmMYfc|z+4a2SM3q!Ta?Z>x0=7l+DDH21$X@Nn+r>AWAlPTDQ?N@{P_WLOpwjn;LxLNYw0Xw61X)^Pg+=DVf=NcZ M1w$-r@0sY~AL7}Y=Kufz diff --git a/sd-card/mp3/0236.mp3 b/sd-card/mp3/0236.mp3 index 0406fc0ae3e39407ab075663645d85b27b7a5206..3f13aa2d38a252fb809b8f13855b6f8e8779abab 100644 GIT binary patch delta 604 zcmZ{gKTK0m7{%YIlp?JNA}Ct1h;}KGSDH2&7oYkYGPv~8f3sI@lQxZM57M6bkNC##l^vU8zvII>gbCxSCR0)1D8dO-}d{FS3`D(#4u4@)K5mK6o z8o?XB*PP{r?Zz`ut1!ixTEPNOgavo`J*+UzD-qHCh8=>7ENRX%Tc=Rw?>fO(-qBoLl#7L4(y=2edF zQ7Ex_ugN^8`HFsvx#drbaTwcYvc>lcUT{fsf#;%T{$Et^gwqEE@0e*-SmIi%aTqyh z{v;AJnTs){U*xt4W?0sI$igAPOGeuj<~ZMOX7Y!Px~_Sh6Gs$=n21Yob!D_#_$hvq zm*c^L!8RvaFJ2Syg)Jx>KQsZdWSjr@EaUZ#dWE<(xq)nap$~kEO$#)Vz15 z&q~;Ks-prgvZ438ey=lBF&OA~PG^VB);el;_H`Xk+qNA$f2wAyQQvj4mfe}MGj>;I Li@4V|4qN{L{N=~} delta 396 zcmW;IJ1hfG7zN;*Ql+ZuQO~MY>C(xdi^XD+h^33kVzDrE>e8i)NK|uZBKMCFk*GxD z5lM*}^oG zXTCx3#)(G35p8aT6|TAk>l|njEbvvEV60i;fK4r?^R70}kyi7{SF1^wZZqA2?SgZj zX)ieDG4sD3!66qr1osSk6>eGZnuN(t^Cu3U>74N?z0sgwu)(5shw(1KH9Y}^W99;8 eCem$gOWIi`dlbeQ3X1jRCRQQ53(jyVX#E2h;H8TI diff --git a/sd-card/mp3/0237.mp3 b/sd-card/mp3/0237.mp3 index 2bab656a6150769b57a6fe5309b4279f087c77f5..e457b5261abc9f33753e50c05148dbf877f91bfb 100644 GIT binary patch delta 614 zcmZ{gKWI}?6vpp~t*K3{t+mx^tf|#nNG`jnVO_X@TR}rl z5Nl#8VMX`_6Wf?(5ok7a4drI9KN(`0!ncs2GOmPK*tVUih>CC+sTL!uUoA`@FtvlJ zj-FPgH&|>n^cWY~m>%Mfu!xzROf^{TMs|K|XS$8cyO`z>>@bqzeg}(_Q=Lq2@m)BL ztGkV;I`??mr#{^-&;Rc(&-0DFOjY#lV|syi!uPls@iP96cq#7gXIezE+epS*H`8lO z9$;EUILh<`^HC#Hr-!M6x^NbSgI=ykuc14r_IjT4hnQYsT`0eF*hnAiW4ebG;dM+L zVR8}ZH}neS{+WX9IyrmD8Cwe~RZ&Vc$dQ`!DUfTw(r$dn9?6WSM>1)9Oj(vSV4X;W z{+s4AXGc{`>-d0=On76Sb=-6Id4EAJYoAGtd#yUE2S<;`6I$!&rPEEDJ#`&BrSxE2 OC-t%9CU%P(V)YLe6v~wV delta 406 zcmW;IJt)L+7zgmae=o;5=l%VU<2dRj1B=CCvPdj4SS%Kc$#Mn*B`cG1dg|Bn>-&8s`*-^m+VQ^FVX(BA5bSUaf)DyEL4uBD zaKP>i!5NcjBbkB}?JR?5?q><^Igl+lWlBBayvN{};T*vV<7$#4xdMll*I=44uRC|< z2^RR2XRyg}pQuIof>~~>{p|4@-TL$k9ywJY_+dk#;Fc$a21gt$672FtY{ z=jsE8ZMWl}?Y3C172L3+&S=L(o#2{d^@11r0)j6(0i&SmS=s j%{WOP!y&;2gYWx#L2L@OO#)Q;_xVUt2GBGSJ4xanMWWq`AFE{7U_nmu}M}Fs@4q$sO zYw&okDp=wa1m75s2-aDS7?kNB5WHnAWT{S2W1`+*i>vj5H5LyF-f&l0;cV34IeQue zFS)6#^7bKtFw$r+&BaE+2VROZIpm}@e4!Jkcn9X>i@`@5P2 zpSjj-RR2bcV3~iEPdI9Cq~<>@=BXvo|oGN5BNj*iVu(5x3r(I zo$r)i7<5?2UmaFq=A@myaLRgZDBtsD!stKzP1xU9I4#&WpB4@uXme z<)qE~U4oCSDPOa6R>EuFx*Ew_R~q(f4SX->xPAQt7t`tNKI=~J4Lkn;@BF?r delta 376 zcmW;IJ1oOd7zOa0QjfN(MN9dsRyW4A+dBZSuB<=CW&-GVrrWslDc0E7&Jl9 zNXk(0h*!c;ix7!a!X%L{7K3{&zv1S)=iK{!k8g~n2l1APDy(Kof_)|+c&E!IC^2VK zIA*L$aL)3-$!bA`mR;eQ1-syZ@fyJyE7~GcwF&n?Au7hy8*jzG_c6*J}RaZ!?p5?F%OYrtvFa5|-P|-M$Xfb)miF zl%@0@el4?&Y^UIn(V)`prJ(5iM3*2(S4fa$E@aMQVZk-a+EXUG1$kN#g?Sbtf+@y( M1S70yx0&kYAAAX%qyPW_ diff --git a/sd-card/mp3/0239.mp3 b/sd-card/mp3/0239.mp3 index 42f92d086abf218fb611a7b351ede3dc7359750d..49bbc17f861aafb77b50868f2b595585074fc07e 100644 GIT binary patch delta 604 zcmZ{fK}b|#5XZmST-V&qG}E#+TeJ4ECf~a3B6RUp50P!34{^bQ&_f*xLV^w%#Cqsr z1#15dif@Xj4((QZD6%CeFrm5>sX;+?@sOu3Ub=K>-osPi!1v?hKQsTC`Qen^nKXTw z%^12myTtSmlmdpH!rsI*hZUcN8m3=}1`T~eHOTZ9`C6tISQC~o9Wug1 z9n&3r5I)1{%}hlE>J44Q-Fl{1I22}@!nd%InhOyoM$;CibEpXWkZmw@5~~fK!x{tHlJw4Q7=m{RQFwLN^m1zP$g_m(@ zhoLiQ-N|$l&xH)Pjp-JCw;Ac0+Qo8Fe7Cprf^Z&VQBVIHWvb%#9;WZev>RH+QajUo zjPLbsiNw5#rI?{I><*^uSP_;{*vIq|(M}_7)lR0z$nR%5k2PTq(+3RoA`$0cF)(rC zH5TL7Fc}XPEY}{k`t6a=A*CuxsTz3__k0RO_9N$}`mF(HG&kVntPz#e$*yE?I{aTY z?;IUe39VCIKAgyUe#mx@S;u{Y;UVitcGUZ-gSvaLCzaM($4(ut+o;sFt*p}BDV@k!p2HgaKI@DzUZA?q*zd(VOMx%#x8i|aEaiG1?@584yElZ6{L8tz2tbAAVHf`AE8R2g5+v!V5X>^I9bkW@!UXe`RzkGOdJVV*N4#_^oO9G8dY>QdHP@=GiCr~H z=Red4(wwZd>JG0$hPz&Yu-7M8{5a=-?)l#{ojiRw-A}(~ z5{ll>958*sD`GlC#K*LQC7+@atXoWL_~Wry#dMAizoI>C_?dnpSIxAFE6wki3Ml%7 z?jX|wb~Q_QUc)p8Ur5m;=0mP$GR(v{3M=}G*Ab>N>S~#mQPx~UDyrxQE~2jf_O|lX z|Ha*y+ib;@KH_PeyL_$r60`M8Y4kNH`hZ^zOviZ9$aIE4lcH^WZerTRV6$s|qPdR3 z9i`da!uo?1&2>1fZhhJ6>hIc^zTsXw(>%5{9ppQdKQ9XDNl@ohF=sl~m^osN?+1h^ z3n8j>>iFnUL(5)d9Am^BwI?#8cE%hRa$umR?|}^eH_h3@k43kXMvn(Oax>3bPTG9p s8H{Dkq11$1iu=;(jT`+^%C2XRf|Z^+mYEW=*N_PrPgFSf&CRm-2Zp$urvLx| delta 299 zcmW;GJuE|E6b4}Lr7mq%>uM=ASS%(3OAQtigQbax!D11K#o{g&22@#Ua6Zv8p)a0u&#NI*KZH9YL9+ z^@=OL|GV6vs4)`~Jo7xHcwnwkamkwTnt4~S&;BOG5pRrT&NeHy=!6BU+zi{D*@$9? zPZ7Z>7o&;`CR!8~9vF`~78Bg@D`xYRR&hL+y>Z)A;$ohdO4#9Fqt8;CVv+-%V1xIb z;*)djiXXa3!6Ubmid&AP?CKX|K)*wbsZKQqPK*I3(zgCi+kCA{amK!G#U2CWG-omr Nny>T6mx!J`a(}=dcz6H+ diff --git a/sd-card/mp3/0241.mp3 b/sd-card/mp3/0241.mp3 index f2cf516531e5a10553b88e50232606796b3742a3..f1233c3666ef7147540698a64b8e5a4eb8964fd1 100644 GIT binary patch delta 585 zcmZ{fPe@cz6vp2-X=Bc0nt#fgY)osT$g|NIp-r!Ph>SBn!~u)I76s8pf>v!tLINRB z`D_vQAZCAHga%3oY2l(aAs8}k+PIlTt%7jV#zp5oTm>GuKMv=7=R4=Fj{eCl3}R<7 zZSZulB3R)x1m78t2tKnEF(}bLBKW{+$YPD4%0#Wf7B^}In=Bj^yyu>Bjq_2f@2Ru; zugW#vJ0^I>NWH-fm+J*@cqJxS;E$Nm{83!6%%{CW%h*i?`*Vg zn~es~d8^5K{#Cx?(n-Nj_BR{VY&Toa2Q7lPjJ6tl;;U9Wb+ygvca?K|e9B;!ZKnmV zxvnfTXcsK;ce}xT&YcmsymVIdUtg3J-bq;J{e)nZFU|>GGTmYDk(CZxnLaPNC7u+m zl#&MP^gC@{RhC)2U@u8@89d=em*6%F-GTw`b(=`bKIbp8=mq|SH|$UDL>;H>I8Kc| zjg^oJEr*c{Qp4UzekwPT&v}!Mn@;ui4-Llt>lX4i#+)A49q0{_k012M{b1C)85&HC od)Kp5HdSHYSSICaB=26UJ4_n*Ue%`_V7cOw0NzOKWLrsHMdrrwUqJnp#>SaA}AZ30j)e-U#F#BKjFz zqJK{tpP z#?uDZ#+L;TI0C^ZMni%ZEQJha=xz`^!cHTLon%>J1+AO}*eA&utUjX1KxN2B#bR-tI=hBYv@* zC|!Ufn4;%LBUvc|N!Fcq(S_k$+=?U!2%2_|9~*zuj`P z;3h}*_|rz?zHcdR?Eknef(5PwEbbM|G1h7@&G}Zrc^38whPZ0k&C&fVI-Z+%dfdVH zVWrASsS0~++zn`9WuuCE#!{(t2V8Z?E!MpwfunV x^*KYq1Ni~xSZ3J2s)M?t|4=fewT_=YTD4xO=Qa0mEe-)f0NaM3cY59N9N3eBBM2e3s$rRrYs6s_SYJf8|^LQbpoNus<6V0RdB~4 zn_!0zHia!t+XZPl>jjhC(-t`5P&)U`A$a0^gW!;XM!`1=jSBZ1cZ$ya(&m|N5}b0t zrEtbumtdDu&4Nu@+zM;la2vD39>EEpwcDI+QM%vLYE+K2MUJ%zj``kZ+_=y#m|GJm~-AFg%^J{j^E*Oh#N3=>_(wsyafoAoRGpKw5M&GNs=ZoxUdL4|$h Rf`U0ldjxS-v=OFy`3K-~o2UQ) diff --git a/sd-card/mp3/0243.mp3 b/sd-card/mp3/0243.mp3 index c1d5482b2e125f5edafa2113e8755b919429c009..e1eb269ecde7de3dff614ffd97144a35fee97a16 100644 GIT binary patch delta 584 zcmZ{fPe@cz7{%{5X=Bc0nrT^+j#_Orx#MF7p%%UBAu`VRkQtcV=)yK_Bxu!UBHGj< z8%_jzKf~+~On+u!80NyDBHAP(LTz&4!d5}JY16{*K3oNUaDQCxx%b?2<_7;{9wceK zlrnU$w8r!d6U6i#(Gb%YltYHzz}v_44x0gsRZLrmRU2BtVl~r8qcGTlc06jKh{!q1pIjiT#&Ik(>{tcI1U zD5a`o$;<{6$Z;?k-|2S;vZI-SY{o69M8fG#^d%$zsrl^XA=Ra|(;Xli-PMOZf6%=W s803cCOX*R&RcCe2P_L8JTF1vP*6elF_uRD7J&sQ4-qap(Z|y!-J1lO#cmMzZ delta 376 zcmW-cO(+9!7>E0f8QZX#&DVSm7bge1xVWqo%EiTr;^M-=Nof}s7qfm+{9g`JY6r_l z7>Ct}M5LXhlsG7*w2O;__wVvt-g@5O^F5LpUhTtcGHS4%EC}{F3&A_R4ncuwhrtAkU&Q$ArsZk3Hpr6W%BfI9(xXaJUVwx$PF*vfm@f z^2uZLI`0+C(O)T;Vn+GNp(=wbepHG2m#VF~-DjEe8QnAP7xnxo^IWSDoU=DzJ@*0o zWu{h;rYmT$#hswwlmj8bE?<X!~HnibN+LG?$6A-?1LoM zW>Ok=W~zb(PC~HGXh86Z<$y++&JMvF)_fN01U1Hj8lSlo6fASBUhtZ~4J*7K(s<0C zu%N_M!zwTD6wETvpmB@y4PJdRBADVv#GAYt6%^RKOYnmg!{^L4YQxP&!6I+(7Ob$d zN$`}*P1@|g&@6b%KZYgF?h!m;e~ZQ=erXYW2EohsFz5I|Q#d zdD#1tXxz(Fj%$GU$!d=`%g?lRV;;c}@Bih9)$A z(kPM&O+-iywTMI_H3-6B>0_c73|PtQ%EvpGwMOR zV1ti#qciRhOtZ00@Xd_&jFEb!eXAD~xX>VYV9+TzV&19r{)tAxH7nXI*O~-}>~|^b zvgi^NIn!+9x40EHx$QQlhCG4{pS3B@w6MJ@WO50m-hU=COiwx~sI+_-8p6&55x z4aXpRmze%7Mrs11kQNFC)g}=UilB{)TD57@rbX|4xC*@Remw5I=bU>hy^^_?#7Zfp zF;Q9;JmV+?Uli7p?x=-PbH y%V*rPzQNF-don#@zs>>GJfpn)lqaqq-9+rFv8AoO^9e?EC}&NxUEc delta 378 zcmW;GJtzcl7zXg3<9yxuILC1uj>BRyuvjctib3g0N?9xx3yXyTWx(l?=zZzrqZAj( zb*L+pL~b|+DJ6=@VzC(f|CZnI_CD|PKK9IZN89ka)GkB04#6qI zS&B=({X3bh_-4QN*`9k%2tQoJ#yIh?OZ(orB-p0 z^5hb;Kjv1f&{Jr;;>I&}7YP#lEK*!^y4d#Cm#CSfN(2WS@F>>#W8CI)sp5pKUNPBw zug#8>DQ=ltE{JlgTrtm1pCZN=<0dC61b6gR+H1$g6np(P_2n1rbFNA;#pY^7Kd+1l z4h6(~`x~&ot<~7B_FBOMGqpB5TBnH99kiQ!K{0p2^@?-88Fx9^pjcoaBp7Ehr08U% RQJr1cU44T@(+p#cu0MzMnTP-Y diff --git a/sd-card/mp3/0246.mp3 b/sd-card/mp3/0246.mp3 index 89acfc0fe41f1a7d36b2b09ee0b1fb7604cf1481..6965b41bf12c306240d947a866d7ff6455eeeac9 100644 GIT binary patch delta 584 zcmZ{ePe@cz6vpqF)G=o=&9t=1#>_VU;f})$MJ;;PLu8!sAr6dgY>{AX+_VZ}TC`CF zmCudbQ`BUO2^v8uq=k#3h$0P%a$}2G)G7!!ZCZ5h!&Tsc`{QuV`M&!V27jmL6ZB;v zX=r9)n`s$S#Pki}0Mk1Z1BRZ%J;d}9WuN&9rXPq@8rsBqCDUtUtC(J3PgulU&+rx>een!z982P__A`iNMAk^WPIO>*lv(<*|EM$$fMwEtddVo~i1mvQHW zk(^B@?d6)V1h3iN|7te$0Mjit)%jC4$s1t~*CU2*;%|g$7E7m@z9ZRcowr-9YU&Kr zGlZiyb1`bf&23{^Ls__j{8>AXv>UpM^>(HSWX~~O#-4B1N-sr17=+twal+rzMoz%U_1LEG>7ps3XKE5RY delta 376 zcmW;GK_~-Y7zW_pF=HDxvoVIvaB*_5i;K%jp8yM+5@I-N;_CDxZ{;J&$$MZX|XG8a>FjTNC%O)j*v zoCzo_@;4w@<9e6igOP5NT<$hk@gBh;ok7z%7gRbe8WNncqCH`<*UST9g%pcn!2)A_ Mf-zRL!gN3X021q)q5uE@ diff --git a/sd-card/mp3/0247.mp3 b/sd-card/mp3/0247.mp3 index 9f08f1ac6e08861334106e505ae072a5c9067501..7ad979f25198a3886c38ccf42b4c82fdb8ac21bc 100644 GIT binary patch delta 596 zcmZ|LPe_zO7zXfpHn%l*GykMzZMJ54(d1i~-GVNC<%h|(>n6Kk5!k^Xx_I#*UA99+ zMWyl>6!Q}OrFO|ZB&8S>C`1<{B&tgtIusqcc1 zsg%aGsTIK^jzaL6k$~VWO971;dK&~U`Q5izCHTQ;P~$!4gMvjCHVR&F)wsZkkj8y> zRtp~UopFX|H(3KU8rONZ#^xUi3#R!ctWD-v#CFzi7UWqmE;3!KP0r6+!8cyoBIcI1 zI>8)Y*J&*ARJ~x5e~jatZV+=f5#<-;ahJ=`p~&9BB0Uf5>e z(7auYv&IGHS_IGeyG5hSt2^wvW2d0RCF3~HL^V$HZ&WbGJG*RJs#W6?S6T(HIJ(>R zM`D5sOEHZH^xFJiGS0EMM=;B1yT%jFw+k+_uvc)7tHxeV?9;(#4ab&$G2U^rST4Ic zFYoqwLmxs)Rg_XyW}0!&*I>5&k;x_c-2Uuvrazl;hg94-&=o(J4F6XxWP1lxr{g5L ze6o?X`kh)T4o|{%qcfv_IJ*jn`dvWcs F`Ujw$!fgNm delta 389 zcmW;IJuCx27{>8Dr9Rp!S}moj_0h%DK^Kdqi9|#fi^(Dp3xmm0Ll*-YNA$9q2H?T!kks`$jLgvH7nX%rs@?=IM^UKa852Y2s6it*gNeiQbXwx4gs!bagwTL!u+O+7t!Byaa`{QuWx!?EQm)UCrlfC#{ za5SzI76kV=2EkV*B7(OpMl>GLTO}xSvBG?n;0KdYjSrlQ3SKi62xX*^+P zwV=RvhF^GUjbM_I8jW$@su5h`-nf|lTU*cUKWoM4Tv#WV zVOyPGp7V7YuXwy(P~soMJDge{y4VfcomP!sTxboI z$F>TlnMeulvzXE*kJlDvTQq#k{I)PpvR&gj=h_7i8EhBa=91wVPVCT8yWD(eoOZ|c zd=R+4H{|wt!=GYGm6TFd=9rqU(7;?*D(k2F-2U81wm+A3hm~#b?y~nd@&Bel?%;sx zw5)Vj1-ZoXe9-fcxW_62Lxb*t%t+X(oky+io^-EeS*erzt5;g}JvXDQ?zH7tJaTpNlrAO%i%7b#h%opTF<2~?E*L~~F)&cgX@tC&NF+l1 zLCt;Nt@+Fi9l#8RaoR!sUXgt zGSTyEnZh2YoPue(%LOqWYiBs%Qh4OIOZ1(E3c(RuDh1EHt5isH)GfGXUVF@Vm6g)z zQMzZ^Be>^ewYBf4Q95y_Ms&katzerU+9S@^DO|9&UhvK{ZIZ)Y(OExU>zAbl!8wDC zf_0{}BOEsrLi{xZW87#G?6TXZkY~ncT@E)34(ap@PPy+_x=&w=)h(;NVWidSV+0gZ eOaug%G}{CR%xUKtZCCc*i~9aKb_Q2$ruhe9AfAx` diff --git a/sd-card/mp3/0249.mp3 b/sd-card/mp3/0249.mp3 index 09379d8733746a8b1249d3c56c14bdbe40a21108..37b81f9c39801f88a02fee9bf62a56ae54bb502e 100644 GIT binary patch delta 604 zcmZ{gKTOj>6vyvXN|9Cs`J-sXBHEY~$<<21xbRbdA*A%jFQiS-1qV%x3z|4NSO*t% zK=LIG?wdE~-|D5$ zQ)xptrYcO2F+ogU5D757Los0J8Qe`w3s|kPP{Z^C(V(FZSPU{PBVWrjk2PTtlOZEa z)G@uqd*MTz-E5tKdPCE=U(fUgN5f1H@HK2`2A3i%`KB#Q=TH(3Ak$!|f}agccW`Yh z(=0k0nOlzQGh%Z)tzlKTfWls;*NAo*;bND4CBM&hvL?KO$^F)xh;vZ494OD@Q~V|_ z#DfLLcXQ65JN7Z8R7ojSBTHhoN`a)Sk@Zu9&X6~r9rCiyn9_ae?qq*|_`lP8(sBXC!=&vs_$?ztv6`* I>Uy#I2jOYN>Hq)$ delta 396 zcmW;IJ1hfG6vpv8*Xy`_nx~Gc^+8o!don4upG+>_Bjs02i*=qhH;0% z3Bx&pE583blPk#5?=*N~$|<@rX$xZp*ZQHMQ(bAGFLTq?J-ofWoo zTVcy%m4XB2dyTr`6+E)nC)nnTdd8_LgLV3<1;;#64;iTuobs#2=%#bEb~adNn`w2L zBYuM+{`dtWT&cI)g&GWgnQ5>;Fy1JL)7>Q6j5isrg#)(mu3mAbS&*i`#i*$kd&^*} beMwfGXSB_hJA&fuscgSZ@;e7 z^cCqIQj8+r!oQ}OvY@7!1xd=Jlq^`;*w|Ri{r$3F@ZHUHh>q7xdw9@(!<0wS0?NHi z+xV?L#Me}&O}Nq&O=2mH=@K0ji( zW!k}xb{|%uJ@LL!(NByQv9|1;ojuYf@g<;W2~Pp0AuJTL+b??Dt1#Y=;kwu}Y*=Qy z(QI~}dW1*{AyV|xvFxbP$4yy#tG-z)#8e`q|R(z1b*2-{1N?Fp|;C-poGL4v!!LW?V%IFKv9eKVi{sE=> BqBH;i delta 312 zcmW;HJuHJ^6a`?Xd`er@s#Z%Y>0&V%SVS;LBo>RsBod3oVluFZFwvZ*$h|T68EOzJ zk?K%X;xEymlSm{D36rIZ#o&D{&vI_g$=x1)9NOwfH8mi}q$-M2Ej!7KBQqRgpA#SLr5Gj6&CN9=7<6!~B*bFNvDr^_SQ;hsnF$w9AO zdGU(5U-T(547S*<6JvoBeldRe6&GA>RpgiqD6%XDY{PU=k>-!_n&~!cUr6x8=a6Eb z^X-apy2FBH9)xYfNJMeRH)D=V9b&$SL={&&Hq_y5oRntePt(@QIb zl%@xTRi-&i5YrlBA*OFAg*27m?qga&C1Adase*V|(@!jgnU>-2XZnaO;XI}zMxU%U z`X9o1+&IAW2%#EHlPK0Oy~VjG(+t+5TKd~DrXuPOGW|wbcng_YEx8-DCgt8CfHXHtJ*7ECz$C*B$;{?+ud>1~!SX|S2Y{!{iWA>y`rdmwaY75glOq??F zSc2&bN(n6!x7BDW!Ug0{Gc6+Crm2jjHZ%9nFcq*Ryo2d>9o`I$t-pZXF_rgy$8(3B zes^RgqEuNaRV7DiE}%fhL1n$}erF&znjOexoe^c*sZP7EC;H#5pSwJ$l9tue86X$i zy&rPDE6%mRV0g&6lo>UxN?x_P(%qI+V*FzDUQ*9>GRo>Yn@;sv={?q++7ell zsgjB|5hM(q8VL~wlckHLgL^H%+?@P!&i&RVZzp0y_=pcHY{d(L6V5^KMW-bwFli}V zF;pSQ@$;XHm4XtzHicKFZGsnus|0r}X=WI$HvT}3@gFoZoT(KEExSUD3A^BeBM!kX ziw>pV^G-pEjdg-|rZks0R^Ik3BGBjxX`9>M0dO3nCF_Cobs3-{&Pj9b6mg!!Thx-J3 WENQMV+OKS7D^lF%U~t_EhyMVM383-- diff --git a/sd-card/mp3/0252.mp3 b/sd-card/mp3/0252.mp3 index 6cf6ed73562bcccdd06fc13524da7938be5c48db..e6d6ea72be440cbef5e715f259ad74b6db5bd751 100644 GIT binary patch delta 610 zcmZ{gPe@cz6o>DboMv(+8#66yvQcYXjL92^nSwUG>Y*~u%u5{52y9U;+DH&BS~M+M zm;`FSTF5;~^hZ)?7D{F8W*-Pjlx%{={6=Rncm}Ah-m`fLYl^LCCoI0>g`NtQ5Mc1Tcu^@&nl+R zxVeLA8jTUAVJt+<#xvDSAMjf^hr2t?_GFEgp5+>*C%CkW=_LZSn(p9HEz><5uVZ?F z@4`u3+ig1Q_Ar$(E1ZN|Z|481*J5~UuknoUGkha_igQs-3-}vldWiA;Or6LyXu61% z2IDYzfN2%sn3*;e)8gYans`k(jZ%~Gi8gC`h52Sae@=1G%nz&hAsm-5VQ^)juzc$M66E delta 402 zcmW;IO(+9k9LMo~W6U;eW*GB2w!=;ic5!jp$pK{-Cl@7`MRIW2#Q~RDKZ%}yE6HIF z%Ui->LS9mHSdo-y4obP~BIV$DTs}QL{rf-Po|}=Mp_M+or6LMzse)jaaR}b&v)eEjz=~YN`%PZLAK!f0tFWP<1H=4^~dlJD2rauEEl@PfoC@BgNsWxp~)XI&EBxupO4_AQ)?vKMg=YHoq_fP)Axm#Vd zI+fORb83a@0mg{w8^S)O_bB-^J%zJ_=_S@Y7Au&3BI?)l1q*(rw7SfOfJ$*S~ORhm>y&7 z5R0F1jOi0fF-;%gG&8-%ny`%GVW#JZwrF~Vg%%bTjxfz*!{cPDSw0bG8pTqa{YU#o zCYEuj?K&cD!J_Rt19p!yxExTbtdy#dBQfhyAlF`G-Bgd=n;Xvd=CbynN+yrDC%aFC z{;L*pXY(pyS*dmpx$suK-*Nlw^Pa#!zkMb%Y*Ka1>d1Gdx-83zjh+r}*XlZUMp+#x PD{XbAx3N3E^;rD_=bFf+ delta 403 zcmW;IJ1oOd6bA5of2y=a>rqNQOAVbQSS$u65)2kGSS%JKCX2;DBC7dBa8C>#iHaZu zB~iiPkr3&CNF-8&$~jQyPr58Yf>BH1 zlpPs@D<;!UW(rdD+Z5gyvk4yAmnFDlN_)hxU15`k zLcuYIi%bc7vBDkKiv@FR^O}|K+5=9MDE-D;YUV@jHhX-69e(?iUO!VN`qW^#Nx0U= zIOtdU3;z6qJuX)W2G~-mbY`MbaKe!)(S2Oif)_@s6`t8qBe-Ewd&bFHL7e`8!Zl+7 j(e~8|E|^Lit~bYrf`U=r2F2FYH8}gkv4&~78?yfakJqI^ diff --git a/sd-card/mp3/0254.mp3 b/sd-card/mp3/0254.mp3 index f6bd8f84a0c92bb59aa3e3547996a619c64765d8..f931f2b931ef5698c101020501a75f9e5cc8b9ed 100644 GIT binary patch delta 584 zcmZ{gPe@cz6vp2-X=Bc0nrT^+jah9JnQ3$kYSF76BJ;GuKMv=7_uTKEwe0WQ;t00O zX@dvlx?q(v5PV@YBzVtC$e==RpWrPU0ZUDS24l?zAGz5q*kWP7;0 z@PZ$db>2Q8SYoKv;67JcZGJH#c*w62tGpW(6xnf5@Pk$5Yi8Pv@@Jc1o%4qTHTJa& z*0|MfbpDMF!8iU<*0^+7u*_tqQGV(a%<;|<%W#)Lna{ffPk6ao@R;9~FFAYE;3>P0 z3Ff)3EYt51Eb>>6!7DyGE_latCqy+hWtKN%2KV?kW`D7KQt+ASUhARWYqv3TO0dCb z+w8n~m{;5mJ5JScoF+XQ zs{s{S4kG8L#@zAzbZ$JKbBj(gnI24D7>@kcE#$9FI0K2q@L+&^bhkh0`C0dRFkouZ oy_%V}sS1ZCQlklt_^m4~dr5uI%{Ymn^CP3FRC*7)bGw(Ff8!*~eRAl~}rn!6Fe$7n7xng+be#M(}P#5lJOP z2q{AoI#eVK?IIE)HAo~xx>yX}vwXRExw-G$`#rfENsiz(HL9?lDhW;)gW!!0i=f1; zMd6a+8o>=K|Hf+tRr;(7Ps~{b4~*6cu36RQnW$Gd;!uO&oVVHv=Nbk3wAd6<+_sr| z%r4mAquq2aIt24{HwnIYti9rdQ)%Cwf&!PD1!V?Zf>Y*QO3$Bm3qJUxEi>IB$a1(< zY42MFNiMV*>)RDF+-nycaNHx<;oZ$y_6S}$>Ngomeshgjub@arz+}q?%zHR!E>O`H8SgXseIcd)&4mQV OjP?swS=C0E7~mgq0h?t2 diff --git a/sd-card/mp3/0255.mp3 b/sd-card/mp3/0255.mp3 index 897e638950a78c1cb4c567dc027d16de89571367..edec5584020513c559314e04793cd9d42b59f9d2 100644 GIT binary patch delta 617 zcmZ{eJ7|+(6o&sdHYPT)7i+84w5CQ|A+`K%NDy57)t_3@q#-0wU0T5|E?t~l(m_Ov zP{Uiq`W=e(hVhc2K`jm)Qjji+L_rZ;I_RWB7Z(S=FLV-m;5;18`<}~kb|!Z_j?cw} z!fdf7c*JQ4zA)$!ykp6u@RIHh!E0{RS!fXaV92ZRfh%4?mHC~5SKQQm%sHRJ0;7Jx zGS@Y$oZ2OLK~JN?EiN_+zVT$hT=^PM+J7Y|m}Tp3bE~XbX0l1)8Gkkj-ttr+NRCvmp2h8T)gGOD`ta37>P~+c_sTaBgi%fJYjI!1(xX9^4f;$X` zMSDtNg->*Q%yjgS0!?+83iZf=A2=7{G-pZ%9dp{=+tq)P7U3zM=BE=c1F?@sgZQb8MkcP?jJl6 z5B#U*(`U!5sBOpk>qrN;^sJj1bMZ*r|xJJ4tcDxN(nD$$||wq$tjKV~nytRS~8mO)$@;G{py7((RUq zbTRo6mmZu5&I|vBkzbG0D3; zHPeB7#SxPW1j}42Pz?l^;@~c>I$jK7>v#-=nuZ&Ul zc?8e=_1Jo$OtH=8azPJc<%%H=S14BLs#KE+RtjF|ud;97j1i7i+j~4-!9BxX#TC12 l6z7Z^Bb=%goUp;C*yN3Imi=|=?E3C&PvJ^k*@3q{3x6cCr?UV6 diff --git a/sd-card/mp3/0262.mp3 b/sd-card/mp3/0262.mp3 index 0a191f101210e0bfbe2b23acdd718bd8e36f19e8..bff457e1e3339b1d53676370a925f02ebc0c88da 100644 GIT binary patch delta 562 zcmZoSx@x?kkV&GBm4ShSfr0TP| zLj^rCWzV1cSz65wud3gi!up326Y)&5C8yQ)@Tp3jt-l{w6Qrs9F_izb1lF<(snp&)q^S2VBzZHw%bpGfST= zw`Jf+W9f{0zJQToiYD6vM#TiRUPX}tF3HJ>;@2h@h;b|a|1IG1w);b|)Zs%6A~x)e z77UDu35*7sTnkuV2{16&GfX}p;XZl31gC@(12ps)7#M-*2au}(Q&8_1;2PrS?iz7{ zoq=H;P=*DFfnWic1QI}DFz^j9c6apji}&^P^L311Ff_8zF)}scMw1QobMa@;HZU;O z0TX^aQ28L&03XLlumK@Kj?UikASO_up1-NNseyrkLW&bGe!&1kX@IMvH-mwmiJ^(5 Nr6msMHb6Bq006D)tc3so delta 354 zcmca=+-|g?kV&Gxm4ShSfr0Jpp^ZY zhYEUP%Kkt1v$UEWUTwcQh4l}!L_mGg0j_oLr0m~>Fb1mbwSPCO-F!{}D0{YgS?-EB)t~DN z&d%gk*bCIVS+Q(-n(gL%A)u^JZSn$+s#THsYa$p`zA2mj+%3d%z$NYfW&sgxX6aMk zZ5cSySUUTjFJNStqRF#>Q87WSSMf>#m*nI`@oSR{#JCmz{}yn0+x?+f>ZlWghz)zA vg#lw?0;7SZzyj7+0t^iH2PPkoaGyM1f>Xk|0UCM?42(eZkAZ%rx0$C3p z5{#6`L-`J2REIsd^ask5hl1#mgeM8Qcqlq}@sfuQ&AWIC9GD-&%rnC?zq225OKI9! z?lJUed7EhxQ^YimsE_F`Nc7<9h3}E8HuMC)s+ra> zf0F4lT56b{W3$H4CT`X;t>BOFC06RJK3Q*sU+bCHai_tW3&xCSUc~HHZzEF~KZJ{T zaLQ0G8c#F5#XDgcUK3LZdrd|jTR3CiYddSFpM>kU6*uDHZ=7iqPtP%ZM^Ce%Eo?Wl zaO%AE7ERbYrG$|!x5ZxD6)vE7foTTuRzp{@(aM3>Dmy=m+ZP|g3x|r1=MFi2?#Q>G zQe~x-Us7VNLV>g^k@r%4PPQ3O+{5Ksg+!#>pTBkZHC`1qEgRa-_ uj8+Z|4La9zW407w=fI_8T5FvczZN=5>bXu%>CRNQ)|WF!*qu9ER{sENLAFZ( delta 373 zcmWm9PbdRm7zS|9n6b@l#u)QwCKo3MyD06lqB-oM!HT?CPiw6 zNHU8ZxJYr>Ny=r399(vha`1hZUtiz*r{}$jeNAkR;5{2w*vXa!DJCIUpwl8)V&0TGCC#~kHOB1?|C>OPrvLx| diff --git a/sd-card/mp3/0301_select_folder.mp3 b/sd-card/mp3/0301_select_folder.mp3 index 6151bd232e7c71b414348a015288002edb4ddbc9..4d054a47dd8715ec841d892613bb0aa00ee5d2a0 100644 GIT binary patch delta 1799 zcmai!ZEVw36vlfhScHm9WP(oJ1ShyL+D*z}2rW?N=w1r0IwraqH-#w{Wx|+Xd6`Ig z8}li+at{kKB+h6RCuAZlaf>=JMN^}hFEeo_E-_hR_Q54)A3UcaF(D!Tlm2ph?z#7# z`$c1 zsogfs$cgRK^b~h0(>V+X6KK5M(8oxf&Qy+0VGsN>^xWYYhCW014yK*Rypw4Vy~1Bn zon~ZqEKPl)cj<1fyVWysH`7G~?_v5G_H^C3Bi+y`cr%#tF(}-R(7i04X)_Hqp=+k@ z8JNZN6-I<-(R^Q0rA(F^^$Aa-_I@2(XB$zYZ8pj|nfp zmu;v6L)lEn(KMH-6&X294d~7>REx@a>OU%sBjV7p(`kqicdDLsxlEtKmS^ZZ+Vhxp z!Tq3WG9cW7#`#8ao$AtfI)!og7pR8A3yd(F&(w>|g-oBKR~SR}B8_=$5z`e!3p5hf zV$Ez~vEDlP5K|}YOEj|`OAI{$?^0D@PYYDp7sPqF#Ha?<3 zrIy?gF;>A;kLX&Zt5Q>ysATdZ_&8HK z?CW^^bZgPxe0bKS!(FwN+z3563rnjSt{JiXZ+^;LWBt$b|K?{Q{Iu?AtufSxwi>1y z6x1^Pjs99~)cR*w$|dDlmLiJ@8NT&~HeqNz(^@obV9`ILP9KZzIzwkr`J6WSsPHl( z&#Ru!jassG!V~as)(j4B))Eib zYl&pOtR>qk`~%fn4E>I=EleLEx>YmfYA|#Vi3WYPg4;B|_C~dLG#V*)?<=}$Pl#DGK7Bg7_h~6thLb7@D-hYQnm7;W-Qx#Tx4MH& z-@tY#*_MZx4#9mmsgm#v8k_YYPCcS=b_zS-Z_)8^i#}4}H?(0gp_X2uv}GjOmJy9P zdQ{ubb<9u{iDOJ*1mD!Qx3}t3)zNBXl{c!EL165wnIX4!=6x%#~xpT&`r Rw=mz~%DqmwJFhOb{0k;H+h_m) delta 1591 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 Os$w;Fk$d6JVdp<(8=gG? delta 908 zcmW-gUrfz$7{>ShbULadB@XF`q(sCK6@@}YMJF78nu~6_=_VK5XeP5}+E}$Gquy6D zVKTy+N#krZtx%g|Yt7bX-L$b>bkj|1-nX02etY-*zTfA0-uHL!9gV)Fwte)wJ%?$y zJuLJ9^)46(Adtw8Hwv?fT~2HSTGq1@>q2@GJBtw*N)n1gjn51%_=HB$xxw1!Bs0B0 zI9aF*^&5qH;n`#sdpDWD;uN7Pm{tbRv{~pMe5p)}7)Z6d!7W11F}K9lG}eSO(}mt+ zL^+JwtrnY;u@qH?edliz&3|$m(`Phnw@Q4b&|CB=yHUP_HJ+IrR&U;E^GeAQwI0f{ z0IGKhO<-ObM#pZU5%~SA+Q$4sbEw-R^cC)G)`9e73w=W2UaL$gC(yW0*8$!f(Rcfm zVFYr8eqc7&!fweE`hfKPwiQFlC#X5VbRP=`%w1=`P#?{%1#!ZHnmgAHZ-1L>VWsG{V)B>>j(seZecda)Q*;OqD((8 ibQMF&7Svo|)xU5-r~sW8#dYUeo;wXi)wl5bqWeGDG<*R7 diff --git a/sd-card/mp3/0312_mode_album.mp3 b/sd-card/mp3/0312_mode_album.mp3 index 7eb573bb444a7b5f8c17f58a2d7bb116e9a079e8..d8c4384abf16f1f2122d850e95174ece1526faff 100644 GIT binary patch delta 881 zcmZ{hO-NKx6vy9rY2(LenvG>mHfEXTM{gWw{5GFCs7#K7IACrx$c-CYxF|QeFv_S@ z{uVj+B!U!8kWfOIkbzQ2Ak=JfBZM0(kid;DX3@QatH2BIcOU2e@45eT&hI<){w|HX z>DPou(DX!v=>z(S=_9N<(_4gfL9gIF#Pl4SF%HEs4Z|KU=oS{@nI0o}n5AYX#xaYa z2nrLJmar=M2EB<)qAOi#b|wE}@+i|MI5Px2!deDX2l|dNIbgAcbXyd&Hlf@0P@gIE z_$W5yCWC{=Lwe#KGEXpNVqUTgfh?viY-b6Y!{|x%VBRTJ?uujxJ$4n3+L@*?b(*CG zPqvUvie#%2`_C|afz_e(VTX{Gy*a8So05|lI?FTzd#;e0g_~<&c3w~r zg?UWPSe1N@-V032(DMb|$8Un$cISX?UetV@lkzD&h8BqJDft9YhI z-JO>VAyCfr6x-!$IHMJ+mb^-)cUX~JL{F8fJzB-GS5wuhC!QKX4-u(hIh_7lrf0C$ zsZodPgrxT}(*QOl2QgI76oB0nREq_Z<9}++DGpq3xQDccgdsEF?J%3Xo!=~)Hm7M? zoc!dPiBUp^W2-gbYBF2=-K{PDRmeVS3?GF+8T=K*$y_b+Sz0G2N{LjV8( delta 673 zcmW-eKSE^_%H}7wkP~9`Z5-e>tDw z@SQE0rrA;cH)Y|0V*=2niw2lgMcg@wm0?t*fHtwEdb1_hHGJ|Su$lIygjc&j7;9Ai(u;3b0vPBpUy7QOkD;0L#rGaNYW(8iKN!A&kH-?ROUV1-_h z!y}Fs37)XJSa6%G#TLEmtks-ZV!Ni4Gi)q1C!3`<={;u~^U4HGoG)|eW6OEL8}2Aq zICR0@4~Ly9m&4|;*iAWh4(O+w)j@3G4!!^MjZYb}vCn~tb YV4cGa&eloduYK3kLTszQ&y0HCf9?1X@c;k- diff --git a/sd-card/mp3/0313_mode_party.mp3 b/sd-card/mp3/0313_mode_party.mp3 index e205cb316b68b40fff01db03debd28f7ea09eb55..dd5d2df966496d60cf4168f35290228ad6fa7373 100644 GIT binary patch delta 830 zcmZ{hO-R&17{>qarmm@(n`v2Qnq}Iq+y2%4NG;rI5!u`palyQ3pce_eNC%x7@r6g#33DXlS2v^a0 zn&~|(rAjza%JdvHWlWE;T&9FQXP9PSv+IDO!YG0cB@bS6Fug;6xz;IiGL0kVRP+d~ zXPM&I68^%7i|G?gQ^&Vx>iD`Uw7#`c(F06XYR`35T4zNVL$6y&Pp*gQD`thG2zzxh z>t5|`z^Bzp&gmQD!l!7jR`Si;)l35zt6}PZr&dujmTH-5(S4qie>&nR*I~Ob*cb7K z1I_;WK+6}aVMGnXNRlBnnV>+H6V({5toJvB+8P@|js6zHG<^=!=S}}_Hxjyh)v&u< z6^;Z#89Vz;f$$aowS+)(lmAk%O;3$-SJ-K~T~dV|7gKj@4F~)|!{w~1a(gR$yV$+6 HbJ+L?p<5&Q delta 623 zcmW-eF-#Ls6h*z&QUpXQQbh|=(F(Sr6)8y3Fv!Hh(uFwSz(O=#IuV71g~X)P+rjc* z8Wa<)i9|&S(a^z0Br!PDL@|cMbP!{LLkCzGe80=d&(F*K_nrT5V(#8d7k(wf20h7w zV1Y3Rp0UOuc*m^6C;I0(X^PnK~l)&QQHUk?ZyLJa$y@o~{Oi3C=eNn%M6V)Nr%R@nf@&oqhBiw^xhG zN)9y&;@oaDT5I}*ebRqY@Pv8g3P-#~YwdXjcbRSyJYcxl=uDy63W}X7YihA-vMmPp zIpDKv6qUQ2JYCk*YV|F*T9wf=qD4!}6(-Ia6&>&ko^wt4m!szdU+8Ev%1oQ!B_r*E zEH~PXGJan0mhONRw4hw#V9@BoTS37mPF=7$z7D}-<~j_Xa`>X)Gk29GrY;G-F%+`; z)NJ?;e5dW)f5ql(Dpxt)Wpu_97X09%a*jjY*35Rd?Ka(G)BIQMi+SY> nj`SM+=Dl7)lIe)xHp6`eH(BTt++ggQIR6Cm6}RZVorkB2;Uc9MPTPUff#J|VQi(~4kwl4x7h<$A zftYDyO=C-bY@~ZR!SoU_agyF^A5cqdP=l9wA|~|#6M3;OJU~o%aP3K-OuCcvoqf*Q zd#``}d)>Z-lUH!A4*!hS8(bUB2_`uR!7YYeL6(!Q!6*|?2tMGva-74f1b;G8VDK~V z6bKgBvs&;s7nQeoHe_&*(L%vx&L}@(|C55dbc+nWV5UfrW>c}?bIuhTe9ED)T~oS7 z@HVr`G?OI;87`DqCF!RGpRsbS;1f=*HTasnrGl$mR({E`b%HJQ)*DQ7cD*3Sfu{w( zGE}Ce@M4+ZLpGNSUgrbl2A+7vpoHZcjJsxq*Y#EQFN2NwuZJ5ixMeFm{j8vnRhtBV za9VkpeG#KotwgMv@#h31tlw;Kj=9aEY6iDh_2CM^JDjXAm|&vPRy(iEad@k7%a<70 zmbYq~;1qkFx8X&lTJ?g_1koyM>Wp%P{VxhWrn}vskD2X)@7c6N)Xupb248XLCBYSz zMg{koRsO(awZZRPs21F0dZ!>@WsPw&OPs1P_=~+S+hohiDUQYL-1Cfb*0UZC?6M5S z4Zh{YxZoEy*IKt9C?|NL&fq(i*IN&+E8k<#AX@js27_*%ens#js~U}aeu>kKHh(qQxmMeJLx=6jrES*Ztnx0C zM-0wz;fO6DofM3*vR$;vrrHfIu(!i%URIvxSW0k`UZ=sEob9xZ4g`Y340V};!gcpr z-sjb>Qw+L_hW#MX?YAb5{T6bZtm8PV^wBYqr$T?{M@JBE_1jX%JK9nm{xQef6|eRh z8;k!J?MWT#bfU3XygH9m_)+|5B53zt%^%Qx)IXR!Zl})9SWRa`ZCxxDt9au;;p3JD W2|ww?YT}+(+Yo<@+>u9{o&Nxn$$3uz delta 825 zcmW-fNk~<36vp{(nWkpG(pTAQFwL}Q)@#tp1{<_eONHD>3pYYY$R{0dKIbnRt);WqGLvsb6IWSi-%-STyPevzO6y8ZzTwsA;ag;;Gbe^0iNTGke zcnL9lVi$iD%NI;NjV-w5`D1u4MUljeg^FK%YwTooKrGc{z`41)NO7LIiv`NaVl_8) zOPu@orHT^_FBP=2Fx90UF%Gj~nRxR&2GXXgN>f~9Wx9JkZnUbFiwOiXT&exW^Q>8+ zxI`~gaD;7{iUHl8m&u->&jWNhcj4T6vK=eiD_7#mrh zr!fu}Qqk9^Yj6G~9P`qPchnRfV4z)$8-07BY-1v=6y9CD=+^yC@zcIp^LKn#^a*Az5 zuCx3-Ze)Xd-161$btn7wxsE%GeJm*!T;XW33(#1ixXIK~wI%B=72Idle&>0@c$=*U j6fF#u3FR1Fz;ajCdLwNlo^;NmV_yyv1)2!79%7K!V;6c>}4V8gXc6nnQ)Wumz(?D zd+z`I{})-hafyyK(yj53p*P0!Odn#9n2y8eVLFMl$50CHeM~1XFFcQA2~!UK6^6Po zvx4az#P4VN7)!$6Fy=LM8G)5dXK+>cIr<-9`Wl{9hE8L071Kv(E@hg;T&bbYFyv#J zK>34Ae<340jmT<4pJHJ()AtxzqxV*qY5i=Op|ePov*=kCoq%GoCvI#MU!eSX}>!r=oz-N0|sUk1>^C zR#=8Uzac*gex}=)cwB3THW>N^`3-v8;1f*8;HzS~fOM6iFW^?|`n+%!$tRgk!N1Xn zqca& zX3adb)6o0K?__!lgU@T`zFnF|dY5W;cdM#-;Rz(eEU&=tXks%C(>I9kVY-4P;W>=$ zRfT~Tb@i(7TlBm7EJGF``9s}Z$B`&EB=n`+AlGl(3}UKS3hw<8gz zFHzmDS!COdWSZz;x`}1s0#Z@!*X}g(vT~hFVGP8W{)P9Tp`S5zQ2hyaG5w8e!pj(b z!N^^e-FoA+@Cssark}7FH}oAwU(`J{FR{#Lg&Fkq80o0c!}K;LdYN8DC}D*8gw6*K zF})7oVWvK$4;$JK_hqIa=7s+tc_fu|Vs5w7>h}EMwXBR~StatxI8~%TmW!h!R^RHh zMUQs0MLV1x%eJ@I+S@{<|N9${wscy7U~p?~5mDdW|6OjZ-RUhB=bq&FW#`@4b*E zNbycYoueT|jL@J+i441qT|voCmTiblb|Tx0Rg&H`+4RCWZk~Mk@_pyL=Y7vV_rMH} zF2>tqQNjLWSw)JqP@HBkpg7NTK%k6$qBz5x@eUjE6wetRBB*5N5XE68hALXwZ+yY# ze8D4@3{zaV1pGc>T;6!4)P8)btD(&oebvagLF3Vt>2Gsr9Y-%r)eX_nWryidQTPdF9u} zd#s-zIL=U^n(~f93H+bO_(a7p_Dwt$2<5S9l43ZECo6*NG8VEbESSN;u;LwCrg-h> zR6&;6sot&jbHzyprztW_PZQi=tjOZ^0*v{PFGYgF+<@H-<`P`+m75msTTd;?9U-_GY62%j?8=td$j#wJKb3BvAx$a!i zJohp)Pt4uqe8qJR81FFkwW5WQh~Rg2M|_H!1)fR%Lcuk*EmRy}*`kj=8GmE_Vln-p zsNyen7*8|4#P_0aiI~Esr9N-*GG~{ulU2*b(jHu{_>(QAo_TbI;0UuT6bD(m(lZaP z@-)(`TyyLjSCuoKVMCeP7KB%OVx6lMcbQnDc*=g`bvCbcg(cs*s}GI8usY^@5m+ad zeCs+z6HC`C{$`!5?hf>i(2%Q{?#C_=xr2i}ea^_l_OLr;H~Q&)Am` z{K}>s{-$`R+PTYki&eYCCK}wOIK-A86nhy>iZPq?bM23cItD8hKQmn^h%>fZv6MOE J8#eru`T+e9jT-;} diff --git a/sd-card/mp3/0316_admin.mp3 b/sd-card/mp3/0316_admin.mp3 index 856d14bc804eeed77e9f7fa4b533bc62ac007e8f..91345d51e30f3c1f6643e80ffe6d16af8a9c3881 100644 GIT binary patch delta 490 zcmZ|JODKd<6bJBohB27&em@p$k?xqm$Si#LLSx34C~Rz4uwh|CX)F*croRQAgDzOfj?+GA-a$JBgVhr7bLGn#HMh8dgbs<7bJY z7A%*tJyvzK7=^oR84sn7uwj{fMu$1@;1J?a2$8Ihj#xsCE+-;rc{+@)&~UIT6f_2e zTY78UKCkmnw?EY0BdVqJ)FcSGe*3+q)olzW2K4nBZGquwp VrV$Xbwysh7WWzt-o%?+(egM(cld%8* delta 282 zcmWm7F)Tv?7zE(l<9W1It!immtV|scu~;Nc7z`|Si^YOLBn*f*@p|3eD=;*E|6rIZiYwmicZT zalKhlXP+xLWz|(wIoqO^f6X)QdV(ScTNM|)F|Rn^rl`>g2};}#DJmQbE1viq7Ua1U zQEV{Yu2^Bk%yBX*DDX3?_~2$o!~U4!jBySHP^V>EoQ1m;q%~q+(a)$`9;j~@neFv?pYi?yLv?Uk diff --git a/sd-card/mp3/0317_special_random.mp3 b/sd-card/mp3/0317_special_random.mp3 index 96c5a00d8d090b399270fb55c91da23bbbca941a..40d2e8e0c62a476856427dec70df2e133b6bda51 100644 GIT binary patch delta 1616 zcmZ{kZ%|Za6vp@5L`)ZxRLa^6vPfH`UD5RqW~{oRuE0uc;if_5mTuWeTh(J%ZMu`x_#8TGul&_ zsu3KkcZY;Oq9N7LML5SXokN#!0O9MIK7%#h&<|)C&vX{~6PSKQ?*v0oZPrV-+|FujiKTbb(7Dcpog zhmmtf988R++nC;mXS$ISd!{ob5WSs6zdch=Ok^6#-It~7e&Kn5jv0n((LRG| zC4zS{O~a6I3>xlgw$d)anQg3;5p>C4+7*QFW+GTK4W*%FrW%_+OB3v!WkgujJxp&S z-Kipz!qf2Q82SnWIci|dY^H8xr zKG~Yb^Z^Rys-=CxQ>dP2s0A4wJ*!Q40D<{TH#AXGs;v%oA3exLERq=8ajmdgG_Ug{gAeMr?4ND<%T*jQm)A~tz${d z6Vj^mgfv3*VWyv8U(fUl66=j5;M<_$`h_PDdqiDyROpHA6--|sSgENF2~VNnQ6uj< zA5%xWgclKhoasGS8;!JM%SLU={IH(V8#ZEY)e~xQ`jaeUnH2sE|0W%SflW-?P_tQw zBrBq)cSMXZ^pu`BEUZW4)7lyCEk+FO-lCR8p3(i(sG+~m8a39$%P82YH~NGpQN7Jj z9WtI}DRG-Hg1~mB&oQ{&&?ksjY3S_dw5dCVGI=|U2pZX;vYMXPIG&w4yFEL#O3@co zmc3fniE8Zv--}w?e&KP%cBvhX-D+R^ZcRJ*k~Y|o5NL=Q=^*DG?W`_g9l|d&?T5A3 z$d{mHFVi>3-^Vf*z56u1sv3=)zF)rqN#TC@Yc=vfEz?odyrLSi>XaRIMs$Yax<4$8 zqw#vt79*t_Aa4E?4HprQ@#kXu#+7+g!PMd2Y`f&o$)kx%#s0KmCr0-v9sr delta 1408 zcmW-hZ*ZJ*5XZZZ{HgSsCaNKv%Be={s(RX9X`)gjrB|AgqfTj%qdn@-G}>r|_Cz8v z5#M8)>^d|U@#m%$H7VmpWpsi$#&vW;CrraQ!^Df25o7YgZr^;K-~67t-S2*P_xW9% zUUptsPQP~hnGSSkgx*7xgg%GM5$Z+1!?YFEX9#_RG3BRd$q{-3?p&rF7|a!FMKI6a zPAGpyS3Xk~-swWeF|7O>;WLH4fK$LUibR1>1{E`ee#6KNrekPy2^~T4Ora!_$`c3_ zGQEauq0nx`W(jq|QzX=eR1uRPRBRC@l^-B}me5=9&1Q{|o-On}B4-PoME)G6KhZbG zs;DXv`U<1U<7hsIDTR`At%?EVQPh?S9mII4MQA@y=yjBzFSH*+%0|??S@TZ0h2BQb z1wtRfKbJLQX0Fg-L@yN8?<%v5{bj7RS9|O{raXa`i-bObdmht^7@Q|mhv3CR^D&_; zLf8CWr*Im)qgT7e@AS%o!TD)c^zy(TiLJc_^~ zrZlpP%)r=XLO;N>*qB<(^ff}43pHU%%p?Bf)v6`IrnbnaijQLCLmwiixK0?(p zvvgFMLi2K_cTnQDTm#CNQM*E@0plx}D$#x=J0;UlewC=uA?3fQU)k&AUce`u+)d-rOl1O`{LGwC3BtftZ9-Kjzum@tNck7)H!^*Tsf|`l&mE$L`9s!~OvoZc z?-V)(*CwG~(7%bbfa<$U+?etZTJAO%-J31r;AWvO5v;dTCzL64-NX7{@4e>euriDA zeL@3p8d%3M(O^Sa5wCE!9}r#3q;d>_Ew%>PEkb(`+iFYViCFejgq5KO zE#steKjIJBWcaqR8k*i_mPH=6`}`==Kj@3HGs{8MBlcocc?8XmGR0Bym}rX!lufAJ zE;Nks?M$Dcz0pFK@35gBQtIY4u?m`MGFd&3TO9vR+uh7g>r!-=$#ON@xxd+_p!x~x z?U*u&mfdEDdym;SxW~#4K4}9sp?noxEvysdeaa?lSQ$t7X`ya7ds)8(iM>MKp<uqFCqS%EwQhIbp_KM XLdy{8l-x3BNzEw~Ki`gnoq7KQTl{B4 diff --git a/sd-card/mp3/0318_special_album.mp3 b/sd-card/mp3/0318_special_album.mp3 index 47501ba1928d3bd344a8a37b9902cbfb08736fc9..1fab70d8a0f4ffb986b966b9b321d4511159d66e 100644 GIT binary patch delta 1490 zcmZ{je@xVM7{~AVCZTeemqAjONfv1!-GQ76rkr{r9^h$SA!$%?g;#c#7ulsk#ae77 z+M8VPd14gif)y<(ceQzyE+sblL0e-rYU3aNXtc&CYQxs^-2AJ}?{=^I?)&+EpXdEN z&-?lO1?#n!tm8#AdctGqwKSI+SrZ?eCF|-fy z6s8!0*Xrq%@CUS|8u}TzbD1t-P&k3mJf=@!%{TNty5=)oL`fRcPZ&)z^ckXdrVrpq zXL<+y!Xfw<7;4AF0;U6KxsItB*$bHtBeBrXanv|8!i?}V+OKEognN;Z2*Zn*zDD>4 zrf-p&Vdz)%WM~zonM?_c2`?dbqoFg%T&z_L2tPp}i|G_5vou2MO-yef=Vqo?aY5LC zI;WAmvrZ;P$1P0n!L!6j#*rmVy@=e(qTjw$GxjbuQoAo(@5hBhXu6H*LpYZidKvL$ zOj{AWohb`b!Z~QWqti;CMsAL=(*MDr{5dWobf>ZAO~YDls2E+#Rei|{)jGOD^+oSu zdK->hratrwPr;vO=oL)lF*TxPCDT{P&R1oLe66$QZk^MN@C4dj+F!TZNQ7ZGQ!By+ zOh=JgXef-HLe*EgifJ*%Rv9Z}8nL2Ii!LM6W2`^_#(;$HZ*JxuSyzMko0^sYBF0^bIm;0hcB0@fv60I#-XxA2PNl6(~KSs-p6dm2F z=5drWMbR&8gnyfn(}`^?iCg>{C%b~_BoY-yj8s#})PWh{muRnI`VsE!I?&vR#Ogx{d;Aw%yY_hD5vD2yZYh^n&clU=IUx=KQ7gwc?pAfk^N>-4{HJf?~Jh3DaK zFcN5@L1ne<)BVp5GmRq=*5Yd(*LP-w7t#KN7U|w^?z&q%%_=N#DH)JfrC1*$%966wMNz3InN|_PuPvRMvXk%s15CSR_Qrpq=1n_ z8aeVDi}UR<{i-)+#LvFxRqc3^O@~#X^N9K}eneXtJgWOVC9FVOlS%o)c4&Av+|9?D z`ue;9-!5;Null=Go2}nwv(1s8T-`|u21m%d2WD%d0BA)i$@gAkV$lo%X-e zVAbaBwp^F1FfWNJ`<3&ZzCeX{cQU}Po!(9UTD`Tc+~LYESY70Dxt7*%oO`ub13s_c U=E^TDELu}gbQQVHS3b7=2k4Vej{pDw delta 1283 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 U*+W9dgpM2)_JVNNJil!F57p9~KL7v# delta 1447 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 delta 875 zcmW-fNl4XU6va9Jve)pkY_RE7+F+JVufYZzUV~+7^%`E0v}hxg8#k^bu!TWFU~vqJ zdlAZtjCu`LSPLs9u|W%olEM}i2!$;Sa^s?V+x!;yyWH=bbMLtacypt#M(KyZoqBNWx_GoE7ANJSqbDS~!3rzpNKF-q~1gT_y+4GFrLGunGP zj9n}pqj<((te}PUV|{+!IK^f5j1!z@MObm1Y2y`PCXK@^N)=;&ssy7(vwDJJ3Nt4< zTP6y!S(2tG=8*9W>n15~F*;fBo}H5wtt^|OCMz^m@Rbcy{rgzDS9* zV}{@b^D`Bn*=M}Ns(EUik@4WDQn9@Im%6>`Ws0}VT<&ICmivg36^i>D`p>$R zE;kw#6tFYun<-nRc*4+X?`&AD#@HIgbM_jqvvRGS0@HI{R+F)v@pX#V99Z{1ru9BK zYlB}zn{kjQHVVFTc%!0?SMn6~%-tlo!R}3p2Q1(06T(|uf8!Rvn*6Q4wm#!=R>iy? z*(MfZ^ERKH*siGL;C2aW1J>>kOCo2dXVYQ)%+h=x7wi(eWBo2gD)V-$?Z0QY;2J9m z6mh2QQ5<8^c$Gzk9$$Z;wqc!|+ GB=ip^ByceR diff --git a/sd-card/mp3/0328_select_first_file.mp3 b/sd-card/mp3/0328_select_first_file.mp3 index f9ca9b6fd1679c820e1a06e848427b1a06a7f9e1..e736109bfb86aba34e0e41993343db6a2c4ca6f5 100644 GIT binary patch delta 1172 zcmZ{iUrgIo6vuz34vV91;2b)w6P)5^JN%VOfq}E#=sH#?9a3eLNj1Y5Uhn}ELt<(S zF(X-m42G>-5sOs}9QJdgA)rt5H*8M=bS zGNvDpxRvP_YzbE|;WYFOJmng*EL_6iZA@7>b{qN(GrKi^YX#FKtW_9#2_r71II3=E zvXB-2gXkV3+}L9rWjh$#%T$iqN@cFnkQ>P=rXaS3Bbd5_iQ(I4D38^BObZyglcg-@ zenZzVyI-FVR_o5X@ES(%GL%F0-7K6J&LMt4MQ$E2^byAIVR{dB_cFbWyl@36x1m?D z<7Rpp)AupGh`>QZ&!cdV=_-crXL=v58Ww{xHHQ8`xK?i!g$qbOpzpd58Jfo8A+;d! zAWNNF!mly$kk;&}Q{$F}KV$G=rr+T>Z0KXm9M%e3k1+j&wIfDUj6A~hDXKhbcUE`> zQLmvdvEgO0d+aFFJE*N^YD2DGH6|Zr`UKm;>zJ~citzaiUB;@9=?x4u7{|)LaQd}Z zvwlq%d`uIq3v(E4)cxuJ)AyJc<`HjVdJCIPIt$~?Odp`GMXk>Zr;s{ks2@AWRQ7bM zMh4o9oEF+NX83W<={n9diOg{$0>V$Q*j*GZAsu9q;SL$fU@^q>6%r?yE?`Ue7bZ>` zF~!raH7pBX$6#13cAPR|$IPjcO%dfgcp4yImYg>#6WF%rLVM(rDmGQEJ> zPR*R_G-7?SOYd$AmoOD$D!|vR6|QzOeTShq)3Sy{J znSMjRB79y|7lj`p{j6$t4;X64;sBR@Zk>+&hUZcjQJE@FhvMPhP$b;!%fP4?Z##6KNNJl<^TWy delta 964 zcmW-fUrZHr6vzGELI`dK@u$oyiW)%}bwjTvikMwNp}bbN=tg;aU@IQNIi8psweb)y zl6`U0og>pRUAG5jV2PGYVL8%)+iekx(b`>$Riic@;vpV7$CuB;?{|LZ_x*m)xjKPA zZsF7I`0Z3gFn%hn7-k0)(+ma_UoaIAoM!X^MGrH^57_;nBEwLY;5J9I6hAY*TJa~B zj5F-Z7Nl8~<294UX|_G2xXwVXV2lH~-oG|aafY*bf)nfvDq30aup-DI;|iPBh;dWfu$PI9Tl8*O&Oo zym5|Qj|uLu5~K>n0c13a+vD3B?f0*C{S>!Z^cZNYKlbkm6&WU9ae5q*U-B z)1`_jc5F~wW^kigK&g#_B}U8qRmM2Z?oIx0XtUrVM>l%}@hxgPmyCDW_oQpCD)->8idV2!t{-{y_xjd$3!-Oo!R zik~=QyvNuM#RV?z@LlNLskp-O7d-k2<9Q}`3Et<*F1LNQ)+-~s#ctEPy{2Q2_YBr4 z&M{RdW+1v(E$)nQn%(tkHbVOZUvYGw;(NwlR2<`y@n81s7Yn88CD$-%Jju4GM;tgH z7RSJW`!O{*XB&JUI~&zZ793P0Ib0?g+m@+Pm|&}%bLCOShHC4iI@HDvT>UI zuPAOSI;{AS?J>m<%swL4XYh!kh4rs0c5>c0&aT&dqf1_Qzaz#ojKvlGT#Spc z_YLn*{-zIp!g!I%7O|ySX}O=$Tdp&b@U=@Pd{iB8`;daIivO5u75v5MJMKDT{F>eG by7$mgL5!nEHS6Z8#)e<{Uh)&>Cv*M>^?sbw diff --git a/sd-card/mp3/0329_select_last_file.mp3 b/sd-card/mp3/0329_select_last_file.mp3 index ade870452eed73808ec6f5474e6fd5bf534e66dc..f36d841ca98b10b79ae268976904901c1605485f 100644 GIT binary patch delta 965 zcmZ{iO-NKx6vyA0tTCOeskE=jv=6h!>`l|mSYzfZC$rIU2nB1C3*89e#x^b*DHj!m z$$yHbdl)K78mXa_#9B;*5JFfX6jIp2jnKztHbJ!LTxk_};r)1=bMHC-^YJ`EmlfUg z(T_pDqPoEurt|0~rdx2>n4TbIQ}h|3WlW=(7rsO+N7J=j(Hl%GXG)`W1ycrDVHyK= zMGxT4)e)wI33RU1c3YmJVI=dIt{|At)Q8!8MHkTHVCsN#6;mhD!W#$|C`w|XfawzY zSF>a)xi7I7(RhkSfvL2$gF4yl@<`?M#z!?NE|sVh7V8T6Z!%LRNSO1G|*aUB=Xg zDdAmo?$)%GYkRVs={kaYSUNeoN6{zr?A85ux|ybt7G@Bx&^0bpXr2B0m{KUM)R8ll zO1x+H>yG^rzD3+*YJtb2=mn-dOpnoZKwgR_zju{ z;({COfZz$84#5;h90orb+$4xGp`2s)W=nUHL7dSf!8Dtb1q)nLPO~q?V3gi1w!*w} zm~C6F?npHl<6x>F%s`spE|<~_ZnD!UxJK7D!F7%*BMhY*yx>Z@;2wLni+1;92);3v zVepYHF2M}fl~)?A;^y$AZ0rcbrvDvDIzxk{fPOM)uiW{#=7~#&d18j{Vk&Gfy;! zkvyA5&|~$4a)#Xp1fS^6H`*ndFL=b}gMv3)Q$AzgA*1vb2(EBm`J8QqmX0E;4;Bd? zFi&njz9}~3F z=QH@gMV}zb_T$!PN|}8*RA&8GpRoF}@-n;1jrPj+3%+t(xyYsp!ELTqJapViX75SC z3JXqI6SK-0wpQBVH!20c83_mq>8~>SbG%CMiXEqI#LhE<29BIDxX<8OTRNc}W_Puq zpY9r?XA!Ltwb*>lu6#{7#=i4L&&XSA$D3Ei*cKGLrlZd2k_PMSf&=w-)TMg6n9c^l dFS;&>9>l0}lA%VUXSC92J@kY`qx4)1{|CCDK5PI0 diff --git a/sd-card/mp3/0330.mp3 b/sd-card/mp3/0330.mp3 index 4357e882de4cffeaed5a67118b1af7048c44cd4a..6304596282129faca8dfb44dc5368b8f726ad5cd 100644 GIT binary patch delta 1990 zcmbW2Z)}rQ6vlfhFk}^($P}Hp2~Kcxc8qnvKw68O3byRRuSDh0qNX1csAr##B zw~bS9>U!GIWo2w|yeYsi`)y_raq&!4u&x4=V zdB|Z<*vE4aMeOZJ9%s&Y8ookw3aQ4 z6qR%TB2DwzyI7C^+t@Q<9baCY&gRGJXc0FpRl<7fEM8*6?|)zE|M~o{Wr*alYieNQ zaz%e|csbM*7HNv|K#`J)`JT{J%_%FOEGoRhj+IKfY~emMQv~`^vQRORZDW2~Jw2#P#-7niN}C<6VQoF#Dgx+@zMn?@r-$cDgVh zBQDHG<5Q?f=F^%sa*yyZS5+uV=CKM*e{yII;#k~>(C@}x?=z^sX)W$Y)IWKP|vt9#!b&bW7hNNh~5M}FKAI`tj7ItHS`vG&?y5R^t2nt~h^u4hM$qD?$pqiGKZUPhfWYLQ8-R?%ndeg%Gx3PT)Rk7&6Yl=Nxe224Qh ztBGFoqIcT8n$ECwqo!|oPo8T`t#H`n2P1w=s{Kt(P973&<@#-ileQi8>e`MxT|1y6 zEm-01sZoL`k2GJaQ+qU)^vymcB6v60Mw=I(R7TX!VY%qRdPBG z@744P2i`^=^Xsd z17jQN5^hVlbP`n?IH~9t_PvWrrL?0pMraV(|B)22lU1|l<0MzLAQ?z zuX6A#;^ao)YF`BJsy&D4PCBndSNnNX-})Xh92C~D|AL|_PL1NaQ|M#oMNJhva#4|q z8!sg$Ma~@S<&E_=r@h=(WUu%($zX^Y42E&?l}}rO0=b>|lsNN>Y%5ADOIDPY*eVR> z1@p7#nhTQu^VC(ktjv&QHs@t0P-?pIe6`(KY+I8EP`=u>)KQ5`S(sBccR{|{Y)-F! ceB#ZPI_)-x!JLyfFTcPt?be2g<)h1q=vJ2F2s}wAXk6 zRo6TFR&EX>w;A6>;09*m8Sm^mHeSu!f(g{#F~ONLp&JsM^DkqVb=i~?yd1{JEZ(-!LDU47*g?@T(We%Ojc7&%o zt8Zq$npaK6-{6}@MLpA;MXS7xx#ir>2(89_D4FhL2l}V8rnLns%aK3BNePlORAwPK zQwQ{WS_dw{$(ds?`HnJ~i>kYwvD`ZjcizR{|Gm=x`TWmWj1(wTiNZ76$=`_0rka8| zDre9!$63Wfb5&L&;~pw&G@eD#JZD|jJ&!6QMLeH9pZ8nlJNX*^d#SR|*o)|WYTcK& zz{wv-EKuv0QlH9Eq%Cyjdi_F`L-5|uZ*>}vAp8L5Ci6l1-DFI{SIqh7Ddv1sKE#^j zJgl+~t;QHi7C9M#{zWSPp!N~Q$@eot($9B;kFx&hi+Ml3m|6;#s4PO4(foUivy!#c zS0YzzQZqD^$^IJdLVy?tt74 z?147p1q3#do=r|ZMQjuIU%_UTJ?PlX3Wg$7m$60VAR3LQP_)(AcI@7&(uT;ZtYdZs zBezs=*8P>NV4txa(QPT0wmWkmu|2i7c5oAoQ*REarPZh?i8}MAKgwNL`-aN5$lt}Skle+5f^YI<`flbB-_4C! zSj+sojEeF-RFvhMM9}QG+lp0X8+wh^sM_nSI=M0C+-6K5@D~3KJaw!~tS;r!K31(` zpOYSh_OntM_3Y9{GdCCxAz&(A$pi`^5S$g5$Cr` zk8rxvjyls-f0Wht9%F``#t_2CovcJ=Bd?o`8{j*kvJ5>ZoQy=}+o?&ho#Q>Z`d#ZJCy}Y-X9ZDZe>f17G}ALuGCbabQu{Lwgaz9(;;o1x5*l zt#5&L4@0O$gBbn6kSHGt!UDwys}yT zh8h$-!*YW)*4QDY`*3=#LGxa#bEJ{!1=fUfNFTOd_cSSbh506?Pe>kN`hrbi4pT>! zXnf5qAGRdCjnpxwN$4$#9wOVqbQ4{z7FSvoUB&ourWf3<5`LDRG{SrW4)3M5^MSR&YC_QgkHeesw%tQjXd{DE-L z@n1C=KRuxN3?t|-A@1C&4@MIG=9yB1p+WOhbi_J}uzesH?lcU;d+}uXcG5(|jA}-E Q&&_u#$Dd*{4gmliJ0oy2xw$Z)Pu z6|Qp&m>oTsjuT`#?O-6%m^`W?Gi4t zr$w0IT8p_CvcgmTbC)^4TX?_|t>%%xwhGU9Wsgu|LtEH+)D{AsY8M`J!!7Z~Uh}Z+ z9l~$EbZb1@DSYFfPV>o`eW7~ae&HHFxXT=m4bQn93uP`I2$MryhF7e1g<6vbg(B14 zq3BX~xOt>USY^$9$LT}i^|;rt#&WMv<=A222e;f3XOEcA%Ly$!5-27*=-8%`K`@&b7imKCj(K1AAA}0m8aVU zueheUz=?fIU)a81Fv$;^GxRzHkGS5U^i@*_1Z5sSD0ss!niXD%DSYK-Oi<P znc|g2W1caa;+}rHn9h}5kz=%6al;>Dh8q=vWA-`}JA5=Qa<)U`HkOhjUd0*TjTtUAiTTjmtVriLjO@ diff --git a/sd-card/mp3/0400_ok.mp3 b/sd-card/mp3/0400_ok.mp3 index 8a8ed5bf8eaef3455987028ce433d3360456ef20..a6cd8f51f5e70503415773001373a4ac38a3fab4 100644 GIT binary patch delta 866 zcmbV}Ur5tY6vuyOX)|4sl$Nzj(@fi3zin=AYUQdSV$LB7){6`Bp(ypihmdFmz6h4e z*B7}bsqEh(MkIt32qlRKMeHFh@#yF(*B z`Z4NP^ma7G^a4G^GzXi7=@AkZMRyV2#&i=a!b^x}FnxzTQ_&2jGnrCo-_EpxHQ{Fr zTNN$9k)>&fI!SouIY(;M{k*)P>bC^c3l%uE*eKw{ja(6QQL{eA}_Z}rI-d&3B zT{kxE{x^SbuU4|=DeA>!9+Qdse5O}e7JfwkJ|!;l_cJ}kRGQHOrX*GilsFnZpuLnG zWbr#ITtLjOh_P;G8p8M?ruXm{DteAoA=4G~99EX@Kd=>PiA0g2hX@xl-N%aX7UD;k zKEPh0pPVkya_yy>Yr?M>KB_1IM;Qy}g-LWBV;Y5}T+ww*l3Z_LYRVaFizT-?W zp3pXa3uiHAGQES_tvmeWW*Wd%57QW| zUL^yY^y;_jt5~cqr`hjQG=+RW3#WvOh*s;-u2w60jKP5Jp)AOvHY*%LtVU4)>oxj( zyjIg+r)UDHI;LyrImy%k+bJeH5~u!+Ja)P_9*TzBLJi@LFIK}y8itV}OX5MA0y$1c zbJW`qYK(L?H%6L69fle7I6Y<{XH&L4a<0X2n5M^>M#Q#}Zw*J!hc2WA+FCzK@ ls5D(IRX$Uq=<=DYtxBWeP?KT0yuM&nFtA0tw>K^ue*r1PCW-(6 delta 658 zcmbW!Pe_w-7zc2^D_!Y|q+D6cT&|_ftYuDfX1fSM7cWA1+C``itzdyjFaimL$mF}A z@jM7&e^}J03>gI;8iW;k*dZ)XVFW?s#Y-0tJwKsK$1WdU-uK7%_k7>y=`DQC-0Q^m z$(Z2%WLfc=DJT}{^C+G%?-AT*{}#mLB&VL8U-&|Zd6=n>X3L+)$}#FhYR;Rp96qYZ(bFQh!KoH^Cf=%8HW=|?gj7Hsr3sJ=#UOA;udOO4xHr?UwN}N`! zy1eOVr{Ep^F*Rq*RStBy)vk33o^!n0%@97LmR2&eO!f#8+~{%db8%;^SMY@8UPYRz Tvx+2reToqCeScS;Ja_F63#$W8 diff --git a/sd-card/mp3/0401_error.mp3 b/sd-card/mp3/0401_error.mp3 index 1f1da31c6962e0f470762412ebefa4636c4e8d49..97b055bc78f589a262fc70f5d4e574ec40a393b8 100644 GIT binary patch delta 808 zcmZ{hPe@cz6vp3~v@vHCCA6%`X3{2Yrs<8ZnNe)vs~#rfj1O_Z+Sno?B65*YxY3}6 zw$bUcMcmUMSdmdKGDU@?3}OiS!z{{;EevYqA~$VXbnhSpk{8~O^S<+)k9*!*yf%D& zEJ7>ks30qyW12!AF};G@!8Cy>hoF0~b}&7_n&b-%7ASfO1%1MNA=6i+b~4RjBhOK% zkaeI)DOt%y^zKrAN3ozs$P_aTq1nZB3o9-`m(cHKYDakqQyr#DxNul|ZKv_8#EZVt z8*~!BvRYg%y}6}g`5xXawy6J{|L-eR=C?K-lL={x-U)lP3E=WxLz=o|icm}YVR0Ml7Ss|CG7u9|5HeFxR7+spJA zQ(hsbEFaS{)+8Td@Q}*!)F}UajY><^GCjwJLY zjpll#uha{AivFWacTgT+nKmu?1o5Dtar_K2-No%=OfTUJF@3>ONKg*vk1OYI$sERY zwQo4Al0Sr*p5bzX`b$njecwrgDTtO6Ol4S=^kN_)D22)>(+|u@F2jy7Ww0I-bOj@g z%HwYuA2e;N%WSo}KRY#TTGO-w`N%Vor-YRACt(|{W?Ql+(Uwe@-J0Ii7;MmEt}WeE z@=S*o&~+o2N7B97@3ic8^ISfltJ6Fk?@^^RhB^!*qU*Z%;;EwTthQywH9cg6jYyNe K&2@)1hqb>>1`E&t delta 600 zcmZvZK}geK7{+^6%hFAfaAp0oEp5%VtffxFix(m2vLGQm738HuJ2X+ecjmi&UR9NH}?v-X$Mbx!N}9#fW- z?>Vy9{JwgJXDrkU?y$c>aF?qM4q0Xcg3D|Q3c5KP6n}olLOREv!7ww8V>r*qzINVd z9N$)no|F6CHuazL|M#X^UcA|oXPO0{I1m!VxUO8|!~utQ3>_3Kb52=ixX6_Eqlay$x5_fFcn;tB+Y>DE!4bhFrdk~;th5R~GIP{=17X2)7Q;@v1|xzMR+UdV zdCYRWHuIO-EN!S=@R~K{BBzf#2;)(6E-62A|4)}m&Oq6GY0!<2%q zwM^;A&CvI$3?*##FuCzZ_#J)gm>$4duVmWU^?H)C8}y6GnMx)c%4C{ASr*eM7KCHy z*r+IqtW8W$F)qA`NH)_X7PA#SLHA~sP4l;Ch4;d7oc1bujAbv=a|~?NI~M0C(VWZC zU2WT#zQOHdn#HhBZ|CG{XJW!JbZ%#2c=Hrp#bjRGr5!AVQ{zZE!o5L4D)*wji7xmi&q}M-Zv^7Kuthz{WHMShpv50uaH}yyQT^h zb)$Jd(|P<6UO=D8bOV;9=sspGU2`_5m6HpVbPW|U#ZXqn^cf4nC^|xlT9H-EbPeOe z5kw9!eZt}aC7yR5WcrBwLvfdc6F7ZX>ntDE{tT38o#G=(zWCe`9ceqNcXyZS`G-q& zrE^Tr91~tgXBpEecx^>@F=^|m8q2kNOTv5TsZjD)_$!$rn5pE%qO40v33%lA(Cu7I ztTp7obG!>LowQCn;xyP*PSclU!-z^EL1zu)VVnX9$4_0Pu*$9ux71aK>+B}O3I+o~ z%S!tv+Ze7qVeB(aD-cK6y^^nYBDMC(xIja_T~X7buZG_&IAIlqOw;t8DNp%dX~eN> T46`6qWCjaE{~7o4O0)47=vYNA delta 712 zcmW-fO-Phs6ooyL4hef<^>A20Ww_nh;6e+MwykD)638x9$) z4lfDbu^WOHbU6gWj5!P*GrU94&o$*0Mp6Zz=ye(-IPVm^XIq-!3zOR%Ot6hy9Al^=i2u~ zu2HtT1sU8_{^7`O!5D*iM%^yvStnh4?2YO9MjcM(3uf8i5zKH!8E5ZagK2v92_`tF ze8koQ!CP(=7(8R&e$k}G2du(Ze$H zx3*O9oXe#KQ|vh|+Lb$C^Tw5y;*UJP$jGhp@p}*X2CCUvRu>GXq1~-*AIZ`2b z$Y9W5go{Bt({;)!r&k)?nyeJ8vf;GgCs&ly?5#4mMo&m^pL5D7wpI&%aHHC2=Y3}c z-&lNhyCvl;J8G=XR*mgvtk&v;>WqHzr8;}kea>cg)m#6udOI0DZ#}Omql`2NuF~6R zFu?gn>#FU7Z7->O!oiD1|B679;4&AQ#986FotDapu#bJ=Th8VbedxXv19+ zYdWQ=b9$BO6~>5Z0hW)cjFL~&Ex4PQDp(UPqgccA7~xt?FEL-sbPt6(rq`$lpJB?c z=_z6XlP?Ny1I$j9N@LHL^<7cauc_(+VIE?OQdXKllC7cdxdWSz@qj`6aQA@QMZ>w!a zZEP>gW~_*ru@uqt0B$?ebF2v&#eHUn;SMeH&Ucu<3Z2GtMR*5O`!!ugEXtI|VpP|C z{(J1q;5f#+t|8PFC_0`y?DV@MD}JTQN~sz-60=nb`tsdR{sF)9mpyG delta 403 zcmWlVJtzcl7{+_fH;40a=j(iP1`C5*ECv=728&6`Vv#aQStv@Ohr;_NAB!t4B|=ds zq=*|XB@2Vea*LGY{V%`%_5S<4&-32){q!uhpm-@PXj+OXZa50XJ-r^q86zIS20M}z ze|$H-aw=JIz+j5tk|!yO2>VkN4@?-(xsWC}Wn;Q~-x;?!oZ&p4OhKF*naR9q&Bn4O(=^j&oZdLij5?cn)cW@PW;eK4sIrqQ+dFRdc{e3j* zr_Hf|p!Z|zOmEObOiQp^m?kh|5j2FzF{VCjOWwnPm1zUc3_%~Ukiqm7@#9QCu_yT% zBQ`-RaAm5RRmmlEonT7Ak|k&k6In_>l+84Z&1^vr&}(Nhk#~|wLrU^DqB$=tjz8GR zQK7+8iY2*BnOMvfbO#-I>Y07XMI=wFvo~MR7p�NcS0*wrmb%ZrZ{03*iD)vn9EL z{s13-^D@n%yIOg(`P8VUed@b~ZzzQ=$#L}ig&a>ofaw+HC6h3N zOs}yU6!Iq+s$uFyc`eJ~ElY-xs1sC%gF4QrwDm5cURMS1aYJVW;JHqah!^_$~rZgEq delta 601 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 XNgwDuUY*Ats&1w delta 424 zcmWmAJ1oOd6bA5o^`@SsUPUSOnhd&F3``b_M@$xrNRV_g5DAG+)qGv#9uh&~FCiWw zl}HmTB|*}`E3t?$Su7TVbC=)noqNwY_dbrS46kSvF5(3HCUYElBW3O){EewAz^~xMfPc;Y6O`ldgP&b8h5|+EXBy z<#U0-1;cLJt+Y_E%L7$7P-Jx0SCPQsQn9TzmI%(6EHOCdSgGKUzv?C9Wp+xJ$9BH+ z2;!V97mPBe!su386@nf1dTq}))!|&F(LKFYcIOlIii6dHSAJI;JTX!ucwn&B&bn6T zIquUh;h#_N!nHcVHbeCW`%Kr{yVDK!q}y+G{6^p2-)Qr9^^^-ucDgTM^F+YrL(R6s pkGjlgi@_8-gQBL?8BVkc7U^m;h;XA#5N1!ippVb(1}TO+;{QF7tr-9S diff --git a/sd-card/mp3/0902_max_volume.mp3 b/sd-card/mp3/0902_max_volume.mp3 index ded3a6b5246994438df301649d3b6cd01c908146..6932c835d8e1fcf53b16b72f6612c8046f70fe22 100644 GIT binary patch delta 694 zcmZ{hUr5tY6vuyOIkjz>%e1UzTb5~?^EaJZ_|mWXC9<4D94rZ}$d_J(*i$d|;7fnN z>|2rC!w{>76%Hy24H{8oMien$jHqBQKK0^DJ@nAI!>7Ol_rvAfd%ov;?!A8Q>-n3V zSgGzWVV(*%mb9~duV`T(C((KpOHnU;|(XZnaWVIGqe zie?e4)H+MTS&VLGD#GDX^c)!%3wzv5i&$|hdV;ivMc=!H=?-#2MyyKFG*+v$+Qe3- z2MAO%Jw(1*(F+WFncm>Ha1OV(F}*~jM$uh-s$u$xi`%uIidseQale-7F^<-0o$ta0 zT;8GR73y}f^nF%hqFyWis#lc8)m==t(6pN=hquC~IO|j5^RJJk@AMwct_JO)(4hN{ z?bS{^e$A|3-?Rfv?@$yjV|*Wzg0E50CCoQ6JwkH7o@Gt=8IuQ;CQ^J4YSmj1n6SfsI S%y7pMGa89(VE4*;vGEUJ5$XK^ delta 486 zcmW;JJxCN_7zc3vSEqGe=H*9e=f`E~+4(u-qJxVrE-peisp!&ji<1a0E`ET@-$kFN zqfjCxg%U*rk3KLY(8wSxX;JV%XmP2bq33b=4KMHefBx_L{^+0Vn|XwS*=E7x*+az( zCZTx7a6s{r%K^a#+pj35cw(M$^s3@3qd~zbw}Ogec4sNR@xuJX>1@FY6W2Uv&s^c) zb;UUYA;At)AvIfa6bC%Y5o~iPtd<|Sp?JzQbBUd~g6GWSdfM1c#Vf}06st_<3DWF| zC_eL#xxu+xiVtkc7o@nKugLI8f%nNS6nx=gp<<-+{V=?7v7i7G$4XC82{TJf5JxY#?S;%YC@QscjHR3p}W x=%KGf5`OD7^CvrN1#`^QDtb9qrx;+Y-c8pFTG`W}!FZ^#<1f)T!FLT={{h?d#|Qub diff --git a/sd-card/mp3/0903_min_volume.mp3 b/sd-card/mp3/0903_min_volume.mp3 index 137d0be56bb20185ecdaf33bab4e0feb66727509..f083cae9b198f264574524811b8eb0bd2f09de6a 100644 GIT binary patch delta 645 zcmZ{gPe{{Y7{`Cl(q?X{%e1UzGt0E!lHY1g!OMQ-50SO`LpE4m?2wl(UKT``Nq7l@ zCf{A`eH#5CVIvn6gmo}*6kdW@5MI1^>C{DnE**N_;VJNe_ruHke4jtxcOzSvDMUTa z@1>Zc_)?weF6N1;2)~Ev2g)8rkKr6(TEvd9jC>Q*M+BP{Rj}5~R6ypS*6#{e@yM&_ zCCnD4_t+FZ!Q3IHIy|jPvZYq04UGAiRGwSK0D$ed+*j$NVr(m5qcb3nV^6s}cY!I4fXNg`)hy<1*GbzGPWGGEFsg=OG|7>8+Mqz58G+2mv@X=e><)CxyNBfkH#nbi1{VOmx++(62|m!Edr>-LRCgY>k0EitRF#zkv* gDrV^j-MZScpVW2igkcR|8i~e6uI#h!!d|iQ4}IIzi~s-t delta 437 zcmWlVJ1oOt6vcD?-c+^TkE+(2P7<*&SXe9_NTid2#nQ!Ov5+t+r;B_yB4H`f1kuoV z1c@L)QXv*gB}|qs7K8gOzu~**yXTx6i(F5}`km4IwE=_hT1K$UCKt13avh;Jm+eW!7)R{f?M7x_c>D{$kHh_I^8T4WH?wR zNb$YQ=&w1KoqEd!PfRM$Ip#LF=eJuB<6?#2lR=N*nHL_Tnuu3$$zNrf@k+rJ`>G7i z_*iAL%~p%L7uDDd+cknk4%Z6S`Jueve4Rm(we>dtk@A}12FsrY+hwKEYC4+2CT5T6!n{_;C69^~T z1&7Qj^-3K^m7z}C`c9eROqZzA={87kvsj&7)=d2l=u@vOi`WiAY8P}DlMbd78h5CTdCAuZ*9ppDy^iTJ zrt6trz}>*~3B?BGaK4f09sWo@!2O*}8Tgw7J;!nr(_38H#q<-lWlP&80 zH^~xGyM?S<_Ar(4QgRKkR`sG^twQd-wU_BSI`%Pr#(T*;E;!Z3zfPti=Jqo^L#Rzi zz0{^&F>!$D6&x;=Fy~Uvrd#DKOD-aPkm&}T?Lz+NLOatO5{H-`VnecknZrU(c{-Hy zl4JqNBTSEB=~Q~QQ~gF?7tSKP72Q6jF%*5m z>U5;eUd3;p8|j`a))SgMApQ1iPj%XenQ^1v99gkxT3+f^s;O!76-r3Caw4%nzcCOU zjSNI1#)#(khrGx1Ui*LDMD)y%=F#GmWsO`vSpG@A2Rkai=ziwSVA4>WTmW delta 493 zcmWmAKS&f%90%~eSEs3$dYPJfnWtrEr>73NxU?97ON$Uq7Ptsvf{?h}B7vpZ`E5wv zw_D2L!VV$|2|2i;ki>`^LQNZ71VbSgmxg}t4IduA_ulW%+r0Pq?$c}7PW3p9r3ylt zqY&OQ6c7%W2{?RZ-$^0QU2~JkQ^GzYL5CEVgTieN6bTJHGXHR{*kO(F)50`Y%_R<% z2tODo^?A~zGJDH}0yoMWo^w1TJY@Kcub4Hzu)o~lJ@?9m2~JfAIYujmpUhSI40po9 zcOILs`0T9kg56aPD_pM<);WAmIA(FR!!N$976y5}#^2vDe{-_d*}mqyaL9M&K5y0e zi4N+V)t#;vX4%>xZ1J=Ck%JLm@n1yv#)S*Q2lg~Nix(RG6r&e~PYgBr4l_+Yb6?c= zxofU5*(^L^q{Z2PF1HAa9JnN8cx2`|ciGvic&pF6YUVj~MaVJ`^YL`d|3+_{@P-?0 z&f?=&{b@Mvd&`=y+28Ik!@YLlKBqdo(S$I(P8? F(SJP&$j1Nx diff --git a/sd-card/mp3/0905_eq.mp3 b/sd-card/mp3/0905_eq.mp3 index a113bbcb09485b14665b758efe124d6e595135b8..b0e652469061dbfafc533f916447f81701217121 100644 GIT binary patch delta 532 zcmZ|KO-NKx6bJD8ADzaW$^1ymnx@>e@q;@Kevu1b^)eY}e8>nEfi0#@2qpwciwt$s zLUi~~pxlEbQDo3t6n)UbKsRj)ixv{tqD`wdZrrr!&cjvUf&1fd&pqefJKOiwS?s{~ zLZ^XW_$FB41O$&53kjZbEoAVMZkfR6FY9$qmkaJQ9ya*G_hG?1jvNwfa@YEi3lW1i zY_2HvezN9y^RQr^p-O`du2hQkd{k`jcGO^vcVeX*YmNwt^sPSARR+)ayGrnZb4LZQ z*iLHW^jIlXt-5VU_jG8j&*m>w9CXxkowU+z R?cJ$lPxk@OeX!rG{s9cpr78db delta 324 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 diff --git a/sd-card/mp3/0907_shortcut.mp3 b/sd-card/mp3/0907_shortcut.mp3 index 54282ac897db3f3a1b0a8a35ea44e4c95feeaca4..71165371f422ca64451ac5b4ec5ee309cf44616b 100644 GIT binary patch delta 2296 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 AoB#j- diff --git a/sd-card/mp3/0908_standbytimer.mp3 b/sd-card/mp3/0908_standbytimer.mp3 index 7eded526692067afc88a7e34a1a30a1a9572466e..2cec891d9ea4cc6c2062dfadf96925341ead6aa8 100644 GIT binary patch delta 575 zcmZ{fO-Phs5XYa{Tvy%AkNhZev(<$zF7mFn3yYWD>dkE3^+i@t3G9#;35@Vip{xfl z(T8q-h3uI^^Rz}Dlp;C^+NDE?ceH|HZjq_vGN2I8N$1c2ZfLU5R+C>*6uz&K{Z7KR7+Lxc~qF delta 367 zcmWm9F-XEu5C-th`fI6~W?Gh+CbTp)Btc7y6h+|D(&Q3EiwzAClnn++LCM#U-X%#j z2Q_Wt#*W?I%{PMT>m+WeEa@hbQQ8a+iCZ-Em7LFs4#WV|hwxX{{Wix$1d^6JntY$cD zRWt=>4pRr_g+I}=h3PRYxr#nwDwk;v4S8CBF;B@^{WhkDDBQ~Q0!iT{!ug6~SjlH< z!q7HNM*-6q<_Z*zptF$0&zg`izMbhAJVlCLW1)yCiQXMr$6BoDKBkMAUZSytDTO8B zEY9y#;-_Sno_Qj?gIK9n`&Fvw5-#u7x2xKtb>0Yv&}~=LkH2=Nx0u+=^c}%6CEaux z({1$a(|56z>+VdslD#1Z(`hUV{YdO*x(j=Sl2cL@OxF>w)H$pQ-(&cI5)aNQo$$Qy z3wjQ+O1`3_pTvC`u*rg>_Tv~LfmgyZFb^7t< z>U6A~hjl<}!U>GKnXbX((GR)cVLFFiFVi5bK1HpV_Awnnqo1h~OMcG&as1WAZVU#l zB;xV784IMddhLI(o)XfY9@;g(Jn6KGviFQR=q7k#h@OZs0PrWw6t9`}1S9M!Rb;qsR1R-b%rg=Y{Nh|d@rC|&#d8+7I35%{Vr+*Z%|&C8 zeLEFz=#>e+a;8l2iJj$6f3;j}*I-ETlHpy7_skjZv%5lYksB3?^UUmaj8-b9nXeSw zVsBW@&!+JKr>YcrCaMKXT&`BUVtL7QYf{{$f7se22whEqd@R;Hu!!c(rXZ*qLxRcn3JJHN>#b-v_-1G8nF4o=?E}%{0Jx-le++iZ& q9=V)QTx0(!#VCXAf(x8&SEQIqD&kyAYT)m=g&S#JKRs6J`~H7ow%pVJ diff --git a/sd-card/mp3/0910_switch_volume.mp3 b/sd-card/mp3/0910_switch_volume.mp3 index 4dd40ad486d3cfa0028c7c406099549db84d84f9..5c46483c4b92905b57c6f9115c887b112e938b82 100644 GIT binary patch delta 809 zcmZ{hPe@cz6vp3~v@xAbGc9YfnJm+0oZf4k@z1pIRfEas_z(qkql<)+KmrAGqYD=q zmda-fxd&0Kg@F@kgww((p)4}OO(F>4Mkw645z0mP4z2<(+#i?ko_qd$cedy6gOLEO zj|K&ej;=CIp_75*)S* z`VCJC(1?LYsLWv+z=q@# z^qv&5tDI9zKQJ%(0ah;4J8b6)`iQ~PN+~#_GAv2P(dHCV*>f_z!uVO$c`#4VeXQoG zn9g&G4j0Q>GcG~B2)l(X`UjhCrf=vwujtGdw2Fm%Wkllzrq|e!+`{lhAq#s7m>ywS zvJ&l=n3`cL6msB+LYCHqikOD5RwShLU6<7unI5LQh)Ko~@v6*QUe(+{vHCW*k7*E# zK0)`;a)l{|UCA#P(-jRv*|%aSz2mB)-7lnN(|)E|RFokw9`@IQ79sBq7VXMY$ vP9$ikH*Z8*)lnmm&`WkO$U`3bo|oU@@qO=m@BjU~Ih`75!rExF;L&Ja zG0k2m{?g}Byylcg@SN?t6dA5Ml>-HeAqKsIB4NtoY7><7Tj0@Pl*J7DVR>#anJV|8V%ExQ8P(iu+u0USZ;tB0*2BxP`}S)kPDl zQ#{~uow(?er)>>?MA63?XOB5Pab!p5** zk#CJFyt?1-7AVvB#`|T0Ri29oM!6Z$SmsDn@Q#%S{CM8@lIe1dd;C={=G_|y1&>)@ zA=uz*g~mhnR|=l;pYa7}4+)+#QKd1<^(w(FUOOxpVW?W0sUB1drrB2G*L*cDa`cEc zzo~E;^Ts4SKqr_Vl2CRHv-wE<2U9 dEGvHHe0Z-?&vnwuYPRjOw$-x7xD&gV)eiFFu$%w@ delta 342 zcmWm9PbkB27zXg3F=m_D%*L46E-p@#U0jq_%7ufAlZ%qeE>2P$wC2Qmrtp0eA$D2G zpEhYVvZU;=gGS9^iGxJDC^`84E}z5G+xtH6QRHc4eGspSutGdh61*`AL%T_E&Xh^v zn2|ccC9B$BF4YT)^qUo4m@y0XIN2b$Voh7*TBAaNeHKBMciIAHnv88GtHK9&tb%(E z*#t3`Z3?%Xw+kNWYBut;_L^f3g#*4jMBiO$5u7pP6g)HMR5;<3OOWA@HqT_MAjf{U z!Y&`&f=$k~36|ODQM%Q>M<5*b8Z#AbmJ97lf8p&ARCubba@;5Q;-^oc#??+in!R0u y3*KlCIqg?S^3N~W=2o|1IH0h>Qb4fCXpdl)_Ml*tsh~2K2lBBjCqi+42QB|}7>&FD diff --git a/sd-card/mp3/0912_admin_lock.mp3 b/sd-card/mp3/0912_admin_lock.mp3 index 0b5aa14b221631fc9a8a1bbec046ee8488c6fc23..23a619591e2529aa407e9f7194e09a617c3383cc 100644 GIT binary patch delta 584 zcmZ|LPe@cz6bA73P1=|S z>F`xh_Y_hDxk$<=#KlMnS&Jk@DuOm{Tm|9AO^ePOTm@dZKMwcLxgYmw`cG!E6RU-! z!F*vw@PVTc%rY7hyySGq;1#_cf+E+{pUjsDo-v z=;0@IjF)x^9xzmC@Saa91+RHBB6z_c5u<&sMFn?Rvs>_yGwL!^RR(XlUM2X(@jZf@ zY_1kuXR+E~oabr;1#YU7yth|ypNU$d^_N<~ZC>6NqT{*7h(pB{2LQ2@nK`|Gsz~St~3R=Mh^=f zGa3*2Psa_e(rXTKuBnfhZwa!+S`Dsnu2qm`?ug(lH`G;59A)11y+OCv8~PfytQpI) z%5>_ODyislX=Hq-*X_%WWcsohcgRX4y4n-n$0Psk=CWt{tv1_s+Dl}kTl)i^pLT~! t4F(6?)2Wf5)Po)UPIsqm+wqI1Dz-c7dv40IJDd|v(&^eJ?v1U>);|kuy(|C# delta 376 zcmWmAK`6s<6bJCWW5zaY*cgAa87@u^c5!jp$&>>nCl@7`T^uCkB5~T**8#srp=2c` z4pXd9OA~5RTB$X~Ny){rz5EBdmYSUa diff --git a/sd-card/mp3/0913_pause_on_card_removed.mp3 b/sd-card/mp3/0913_pause_on_card_removed.mp3 index a089242dec70de912735b4ea7715bc9f757506e7..313074b3d96aed130cd05178be8aa230a66ccddf 100644 GIT binary patch delta 692 zcmZ|MUr1AN6bJBomNs+CT&86$+p=lt*4<#HtkC#A+2& z3h5n8qwrTVjiXSl=mrKnOb@UwE+Dg0cNnfw^b8+rnBL*iE}eJQD*A?dwM?&YvX1FK zzKfS}b+@9QsN17!o{Eb|)-yfFuX;t-ac!@jv1uPu5iiAAocAi3_pevqGQD3LZBSG| zv4O>r1Nt1dPn+}U9&Ep!Vnv+8*um0QYSdHDH!@A4?-0`v*2D!&99HxQfhMLbmc-i_ zKEjf5G%I?IY%|kGbhPM+mRpq6r;f7x1y6t}hrBq7SWr^XPQ%C>hEXOJ>zy!=zvWnPw^otX`}xU>j?ON)y%L~)A&)`pCz>q0a zg=?IEaG!~o@P?(Bp~Rsb!UEUbIZo{qo-kEoo-EY}Z#l9{_{vQ;&Y4=n4`z1@-?-|2 zX0A?{XRO{3u~;wM;b2@Sa4l|FVLl<0ncO2h<)RyLxWRCn8x6uZr}qkZrW=JTtTY;K zaWp9ux#gA_?F$>8ZZbUQmnPvI$C^W4+hVBlaf`6bv#o;o)6Mbfe#0-e9tblp+}Awc zCcNbDHp6w^JQzB59TGnAy*tl~Df7PDsc_5O;ov~K;T5axawm?2a}piFQb*WfC>>g? zy9=B;`u~+WL+hnZ;RZ*J2^YBOmN|3W@QB$i;T~7ryUcaVlUT-3VKF0oOH+qHdoIV-$N%sj4Sm`rNa`cpNj$7^rM*Y!0+}Xpl diff --git a/sd-card/mp3/0919_continue_admin.mp3 b/sd-card/mp3/0919_continue_admin.mp3 index 8bbf23a945d4472cefbcd3fb673f5e4d5afe124a..7972c245d0dbe6012f6bd179e36a2e14035a5991 100644 GIT binary patch delta 613 zcmZ{fKWI}?6vp3)d8Rh8w$@gwu_o4JF_!mY41!Ca`Y1Kc>!UOfUFx8VOP7L>B@POT zV69&V>pg~|R)|Bve-KM8R6#_j)J!7k(8bB6i;IKjJ~|0K!;g>iefOMmzkAR8ySdOg zV>+*>Io)KM!vrzSAQob}iF!!UL%7?RRx{pw+qMxX>GJV6TsGjpRs^}3WV@&ht*vYhl1>qtJZHivwR~u6U z*LN{}MzWphEtcE$#tR)<{|Pv=o9PMcPDQV<+Nt%GJuGh{T}t%hE~ZHgbTbX$hwu(6 zd-cZdeLDG4_#A!@(^LHJQFITt_Ulag0E>PvoX5quq9y!|Grhs=L7mL^D!HfGtCJIl zSbiEyFx@~sp`_y`nI2$WSVQ$N%dU8z61UOER6*$oQyCjVA5$r1W^$GA5>`@4`01DyRKnN`Pk$DBd8ycRKx1u??fHH^7{0%-?T^p6iZL*96C$SXQ!!_J<}WNkb8 zpIh?Iju^)*%gzMwVw?V`>km8Ug8^fs&Y8lvmJ;kAvGXS^%Sv22-Lh5Gcb$S^_2+Hd N%4N4W_uA%T;~xi?#f$&| delta 405 zcmW-cODM!)6vdt4Zy4jvc#X$kCS|228x}T-NV8yLWn*K*LX?fvY)~E@6yI&~$busB zXb6Q8im;Hx#>UFV#>T?^+{Lfcch9}&yPJ`>j+wBl^>?=3;C}W=Anb==hF+H-$_tG?kTqzDH5#mtH@xA*(JX(J(Qdp- mFvPBEK_|bJ?VJu8rzSj-bi(ssfRWI!(~_XY$Iv7ng3cdwHJ~Q| diff --git a/sd-card/mp3/0920_eq_intro.mp3 b/sd-card/mp3/0920_eq_intro.mp3 index cca99052f6ba716c2356bec34e4972ae3676a504..a91990b2a15e83d0bf012de36bfee81ebedf2ee0 100644 GIT binary patch delta 1498 zcmZ{jU2KzO6vuliSY#EM$P}%(iB7O%wyXPqO=w|dj_#w(RYyfPbyb*RRVHF(c9KCD zAsO>O7v^(p#3^y=8evf<>WrJ<)J#n;%wn>b^uiI7X|kK$?80*zu1t87{?eTHdC$lH z{Gap3tewZLr;6!vZ;7GG-W1b$M2YDf>{g~>3|S2wh5s6+e~=cABff;`BRG~CI)l-r zOlJ|gmg!r}3sdN^8M*-HGNv(13V%Z6I;J0BU2f=eB$jKuJWKOTXBny0ZfA*SU(a+3 zN#XkltT2*ib_G)sT{kejjl7jiy%<|*=mUhanSRHDupfOlGW`wrDnox^Y8BHZwB5vX z5w_JvIv-rEC%rjL-Ix*ni`dP2GUpbiUoj%whu|9RXKsy=e!6ef4EeXI0u#csXmRK) ziw;#|;C7v^WUcm=TFbBRWjEnaa>sq!F7(^&IptCE&fE z={RPDM-eMFvWuJ&-OGrO5iDi;26LrGwCXO?%=w#`e#C_EBw9A>W*0ZBdIRO!XGw)2 zAXTATjy}NBr+tg+Jha8oXYg-j`Vwj3FydaO!*KYF^f2nv^Pz2;XI^*$J==}+>8#W} zP722n@oP5g4(%kdLnGx?y7B2MBVV;Y$n-O^tJOJ4VF-bphGLlAsmgQ()VFyxI`3GG zq3;l`&8Q?yqpwcA>aN%QPSxw|Z9#RHZI_YigS#}dw?X4G!tW7#$cQpIjZB>w5hf4} z>D#%Gp$>FE%v6E=N3`1s;YGAGX~o4Rrav*TJEKxqcby8Ww$Vp*7W*EZeQ1x7PW+Fl zyVAnbh(E6Tbu{aYqs>|+)S{dhj-%%ZLtnx9q`Guccma{8)PdGkb#f%A2V&E0^Ovxd2SLzT;#ptV=!5+)pxR{Z9=YLHzq=iYu4{Jq7hmmZf9U2L}uG-EE zhtU%^qNKA^{WdAALF5glN?4B=x{Sn;zPK;wZ}e69n|`xdEJ=&SvP6FJyqlpw!kMQw z=&ABm*EQEx*VX!(EbgMB0(V(y*8kpy>MH9kPM6D5kU^dO%KHX?u*SDLlc2G|w>{9T qe=Qqbh4saoid`;OZtJ#XS34T?`vMkMp~qcTj6&Xqqhf+W~rKr_eQHoNTe>x&dbP=m&Q)<*jGavfk9?X1bh)Z-2-m{m_!|k{Co_p^3?LXw{ zJ?J^ST+VkiI*D|YRR)j}l{4^qRQl25anga%1uFldVjM>1LY2?po95&ciqlkj5xq#| zdsK~opuNV)S@72*k@#hvstZPwR-KPDvE;fBZOx< z>oYb}*`eVYIxuMLMcl_-CVh0H^IC4# zIFH)O^HfeCb)Cu=@Xl8$p=Z969)#*u3aA)AMP`A@G58ibc^}1vDxV{Iz1o|q@f6x` zaFU0=LFEIKjK`6C z^-Jm1z*0_(-JF+X>E+ID zQP;@3^chbevO?usjIVI!RrU^cZn#tBXAByTA-T5PWN#X(+DY`{+IFQ#Bq%`@PO|`q%P~CF3v>A$Ieu zqmukOR)Xt!@sah;{?)oceH}F5jl$MNVk$9R3^lWFU^V_*yFu}}^B%CFu^%<2PQ2Q)% zSTJ@Wyn}Zc+rdiPbNmwPlWM6ONOJes^K@pycm}x_I5)79?}v7(97gg*l_RK0O?`1G zzHEAl95t4aewp>US9p;=V?QFhn7#2`PX0u;l}Z|RQ|+L!3-LX4b#f24=zNu#Y23?n zmG`PmOufbq-t^QLmv;8ugN!-b%(6 RB;Hbqz;nRKdE^h|{s&9oJ(vIh diff --git a/sd-card/mp3/0921_normal.mp3 b/sd-card/mp3/0921_normal.mp3 index bcbb661c3b76c443d58dec5ed3729292c49e891d..cd7071f25f9ed7c0c09029cbb27afb46abd3f5c3 100644 GIT binary patch delta 362 zcmca7yGDM3DI>!~v)PO>6EDqSoH;q4aSr2@$vjMR7Vc#a~RJ~4rQCecxv(|AbD_dKl^OPU6Zvr<}e7D=Qcn!GXMY>PGD02 delta 155 zcmWN}tqQ_W9ENe;4y>fBF-~5BHD?f{^pqgyPH7z3FA28%8dEMTLCmJDap1`oJQm)dG|t>OVrtJ7WPH2Yd?HUUWy2Q?nfYQDYCZ@!sxMU- F{sC+9J4FBh diff --git a/sd-card/mp3/0922_pop.mp3 b/sd-card/mp3/0922_pop.mp3 index 502af5c7e83a98533e70a9dc76c6469f774473e9..65e3c3abb39bc40caea99f1f9ed79e508b68ed78 100644 GIT binary patch delta 302 zcmca3v`S=xDI>!~v)PO$6EDqSbeNpaIES%zG7r-n##NJ7GR80Z;W Pm|9pE<8W>RR5Jqr+gwOQ delta 94 zcmZ1_az|)_DI?27v)POm6EDqSbeWvbIES%uG7r-n#&wfdGRfEWlCfJq<$6b1v|0AqJYPrrCyPd{JB2nItVOC3XV zQ*JcbP(K%c25kcaLme>T#{-oQat-itj077H66EOY9S>px73%pLo0}RK7$~GTao|!K X;OgkjV4!DYX=!O@g2TBDP|XYgl&4JU delta 106 zcmaDXHdS>3v)PQL6EDqSoG>|`aSr3!$vjMR7&lE`$ut{Cx-!pUJUICgkX$;s zo&`k8u+CMw!V> GT$2HEekZB` diff --git a/sd-card/mp3/0924_jazz.mp3 b/sd-card/mp3/0924_jazz.mp3 index b8043c8b7e54f8d191d54d8173014ab57cd60247..280d5cba02d6b1572c33b52ef822a66a5107e463 100644 GIT binary patch delta 345 zcmZ1~{aI#$DWkwdv)PO$6EDqSTsAqMaW+%l|H(W|a~K~_Udc3@asOmj<~fY3CO-m_ zTPN4E%wgO&S%!5slhdqSb!J^7Jx}00Tc!U-vDEGM^C?aUr#?@#|Q>PBLf{nOG9on*-$?he+F#>10x+U z;l~4&4{{Cgaf}2T5EA6*>>UqcGUyuU`5PM<0D(e^69+D(0j`eT3k^Fell6JNb!fNO}OyKBS+b_Rw%1_lOZAO?a3U=m0Gg~7l#z}Vf<(=XoF)6ds2 zg2BMjOvli|f(I%aqMXdCF685;nBLW&ayc9r_x@d2)m-V6qM RCPoI9=EgXj(*V`Y008@&B2)kX delta 7 Ocmew$dq`%36CVH$Ap-0G diff --git a/sd-card/mp3/0926_bass.mp3 b/sd-card/mp3/0926_bass.mp3 index e8801eb567c743d03994101339c1bb2349fe5c90..b694e36cacb7018595cba1356dd3aa38bcd89036 100644 GIT binary patch delta 183 zcmbO#dRT13RIYl*0M`&lch`su>?-xW;{#kBy%`Mj RjLpr>Oe}FYrva*)0RZD`A~yg4 delta 7 OcmX>sHdS=PR4xDvVFJ?t diff --git a/sd-card/mp3/0930_max_volume_intro.mp3 b/sd-card/mp3/0930_max_volume_intro.mp3 index c8c9f17813af8c64b4ca941043ccb07ccc1d4237..77eb5e4e65a7956828dad7201884d8ce0815f32c 100644 GIT binary patch delta 1075 zcmZ{iQB0Fp6vw|)!7}I^=tSK*2UAg3#IJS~5Ow|33fNLAwnj;eJj@regctTgmSGki zWLbvf-z{VJNL19w5^I!c&}cOpm1r=gMuQq+#*2vtA9&%##GTXlw8Ss@e!02#-2eHX zbMDbJy*@`rs;OWgXec_6W%>)9#FT=^#l#qK85%)&9@8OA3!kAUkLfnN`G#I#ET8E) zlJk|b!t3aF8#)eO0n_go7e2$mkC;xvRcPouh6$drT~1N}%IYrdMz;Hgq4u ziCZt!p{^!#|Cxi4j73U4rn(*-)JAE zgs0G5t)5GR>ULCk9Pt{heWpge_0_UeT2W_Qt?zKFPFvi*(a5#88=3krSg#`wZqgBE zH!+<+=Vndj*`i|`*<$D)guhi=(?WSnAwBgvhMr)|VS0n)R^6Eu{)zr=MuzI!uEmTC zrA=X`i*W5QG>xGhOm|V=pk+@s7%8BuQI$)YR3Rv?s5}iH8$TLwN5)w`HX*%gU2qzGFEGWIOjY#{&&cQ|v%< zQ>@w9YuUltN`IZd=zpil*!C97XWN0w9AcjL=dIy*)Y+F4NVGcJA_sJ7t+T6IY`faF f?XvH-7JMi)9(E#@T@?t_)dV(t!0w^ozlcsnR1FL^ttC@%*UD$2{T)>StyG+Q@aSU1hqg{(>Y9yWfD zhA5hn2tB5F_|VU#!C@dtwD63{EwUE8npt&fzYzrwOjI zPx*_-rVBdhiFbI;ws^rC79?1Q!32j#tV<8vmH)yV78!|HFE@i7?FHH|V71Tbtbaa1gclcChup;uw;5e57-Q`cbCH-P zh_WNi;X8}cEr(%cgbhmt9rR_G$DRzq4F)q!^SJUfTb9|x>@2}g_9;7gY`NeEJu4g@ zux*7vSg=x1$-$Klg{)g8IKq@{!5M}pS-RTc6-QQE=f*XHQ%qkgxWLF-hxe>nXE{$O zzcIAl6#D&6rw{lAWvtm?3gb39t!dwAX#_S|4nxW=*5{aJZ?38BR^DYro_Qb3Gi^A!SLZwZ?O(p&JTDg5%5%5c3Zq*EVb*T5VTs#qZ5`VkUa)9~$r@Jb zF$L_aZ>PgBdv*%`GPuj$j4Q9RWw+C%W*3^7KBdM~BzQ?rvBPh+6$?JGV2{}z+~d?h z-CpaQvd=n%mHjL&nT)B#4yCbFaGdF7g0GB}IjyPMZ}ujXml!%=hvF}H8t6c|;4Nz^ zY;N2^JA(Fu_8d55s)m#y)*p7dNbeEB33e;1SrHUuaxCa#eervv&)9mjnSrA*{{b6! BZl?eM diff --git a/sd-card/mp3/0931_min_volume_into.mp3 b/sd-card/mp3/0931_min_volume_into.mp3 index efbc90b11323c181f9fe3f9a5d3ff9537d1209c3..b64aa20f1c318fb395784732e36278bda8c88a4c 100644 GIT binary patch delta 1028 zcmZ{iT}YH^6vy8)Wn((EnP%HIl~(IkV>Z+1nAw(Z&70ZiI7&hZOR}u6$bvUs?8>t4 zf*b3_uEpPrf;wmDYM~e2ABXdtujl-q zyVvN~8+5+f=e@ZQV~Ssx75WjqBy6%U%7o5iyo~7=c;#jRo8zW)6ZzRHc)0v1V)w5qzJAX8{rKm+VR0}=Cd^P*}-=V*T zwM}VE=o%)JainU6mhh^URg9qnmRwP1rKfW2I>HnZk-w%82I z%I`6fw5K0wWnKDAt56quQlcXH+c>YJFfDR6vuf>Tj?^Ff67|svi#G`TG}jUrq#5Z<%GO=^5P+hyzEeR@UkR>F!`>7 z{T>oz#iCYbf2`QTtT16gJA@T>*dZbCVh154de6&;$NRnC`}ciU?$OIJx>0RuN*neV z3JfQiuA-Ni#$dBB{X)cI=mC6dn8HX2U!gyZX%>!jL$5KJ&U70cYc+mJ7{id&&~v!f zF@400@DaM#Go@h3F!DN@!IXqIlj#NKGL8HS+Vs`Ff$1}%!WjHnhNiHP#qO> zL;AVou%@0~Wmm1C_gJZAD#eIb<@D4Ux{qWX(;xI6VfqVOy$TtrH*!Y4qna}%97BJD z%Is(~^c$0nTBYL{(_1VFe`4sk5!qZPRJ<8s4Bb8z!_s8rAV!;XcyBXP6m!jnJ|K9K zMPU0W6*Vdxfxku1ccF#pE<%15+u5oa;;lyhcAnNAmW3gN&*39|08Hh1;3`_}qk1i7W6@FLfAu=x01;pPF`V%?jBzhMJ-AAB+=^Km{ z2sNSoO`#>2Q$9eykLfZ(3x&SMgz^MB771O4yV!Jw7mKpCQ0OM63z@z{w_m6SC2v{9 ztnxe(MNApY772ZU^b(;z5G)q@2jj)2aG*r!D(01g7+flpMr0Z5smWzRH_*A8_l!d>{@|iNG+lU8+P9mo~jouH1jw7&|=_`z_Hdos} z5;}@GywxozN}Rt{3XV^m?Xa=>Ax=Zb`_T&nk}~ zQNdbewu0UIr$}$G_JWmpn<`n`JMf99x%0{t2E%50B*JRMWW?gmjWz&Z)G{Mcq2E!r zNk}lGyosJF*2YR>>=r%8xtOI=)j~NuuC|T78lh7t-)xh=rp)2+7MskAEkdIhsx`UT zR#r}JWjFU6U7rfM@NW|}Gqa89N5r>VBRS>2=&iE?0gu&$F;8eW+ILtVbIJ_*cbXxg zT|)n1LU|D#ano_@Ss&zZy{PTA4L0KG2Bs_M-feM7qtGwNDzixJVb!1AV{tlR`CyX` zV!SEuQ?t;|m{;m^N*W_AOhcG#5&9OLDbexy_A+&2WUoExx>j>^Mp=WNeOyrd+m&Nq zBG}f4zxVliy;Qu-Yl!c^<8z#><2VcS({&qgu>xyMIK52%l+1Ps>%B-FVNQN?Mxi9(Af~KY>95H3WvjGpYK@sx?fXq WFX4nMqnoO0V%4v??$MWro&NzNJco?{ delta 845 zcmW-fPe_zu5QmwsbW6?LOv~KN%`$Dx+RU|Fv$b3;wbWDy>EJ~OFA^A(7dwa~1ZqEn zsT#npQLYf+NIG2ZlqK&Y&ukDISZ$-)OTLdJESY zrdCV}`%t@<={>A4tRS+o*T2#TfdIo*1Tk@WpC{8R0WDZqhGf zH#5D!pl||ZaZIljG>Un(4`4dIH}8ru&!?2GN*lWUyF|vDSxT(4(oc zET#p_XX!!fL8gaDKBSu;7cQVCTQ{?u&GZ;uUM=UzF~VSuv3`W2{xDM{B63+|267Gc zz<)#snHBy)lTRBs@{C9r&SR=X)lr>eQ8WZfe4NalpxW@JqOb^g696@oBk@|B*8aEef-dUoH7%f@xbdo86C80d0Q%ZNKp*xr= qW$Hv-8O!=?<%TY!r(B=3@3b175$2<@!h|^b##%1HS=ox`6}JCv++~RX diff --git a/sd-card/mp3/0933_switch_volume_intro.mp3 b/sd-card/mp3/0933_switch_volume_intro.mp3 index b67f5f0c9000b7ce37a177a8a089b6074f38bde0..d68b644fc28b38441a024327756cb0ed6b85997e 100644 GIT binary patch delta 1842 zcma)+T~L%|6vy{$+WN7XWKtFxq>;8r3n-$P?y4&)3ahb&q=My^Qo5#OxM}MLD&z;^ zA5nac8%i~;lT1Z9n~h{rx?^oMG#c691s8F}4L96y(K(w}&N$OMv%h)wd7k$mIuISs^eKD~ zGTp&|@K@B^^t@vP(+$Lgf5JbK>3dj5$@K`2Vp@g#(M(O~9&P9M9HD14u9QheR-%*CPv2ygxCVqR zsDIW_2^{H6{~#v(1ODff)+vTgB0NPs%%7_4o~k}qJT3`5(|m!Y+& znWmnkWil~3GL2-W>;=7fP#8g@Q@zc~G7?cdOXI4{X1W4fj-f8J=4ggp)0vKN`I^1lIfl-lYYtNt z0xvQJVV|pZBXbRX0na?GU9a#wLas!X+}blyH`CAX&DUBF2ydf)fsuE|LX9gXJO;mq z=`gH|4AmmMNWIN3VDh57z|dG!EoRC?(h?>Y(w6;4;Lg$!iT{(ad1>`MZ~ym9Ry!`p=nuTOQ+?^C_hy{yrtzoPea z3U8sJ)X-K8m8vJrt27(AWk%}NRi;@Ayvows_SG7DWVMkF_pD*6L$9z2p|uGmmTR`7 zsb*dM~YicnjyaE5~+ELc^s;}^R?Z$k+s;S#=B=c2os1}mmWZHx_ z;c*mh&~ESBz@m?S?$^aMl#SE z&`PLhDz0643EpiiHSOP~Q=+a)SJK~Q+J#Qxaa3$KbPhw?H5<+E zC6rigL`PlKs;0mWwP+9OZX{^vb9mlo`U$H6-TRE>u&PE^lJ;vXZNi%BGImomD zX|+thprckjDXUZe2Za~W_@U}C>yXZb_#stCk+2E;X11K_Xs~huwJv5 ze2nR9vyE3R>ClfYvWJ!;3{RO~r-5LXtD47t6Y5_f^8^mm)Z(q^$(hRIK!mIMW|oOqUd za|+x`iprNSDO&C>wK%8G%E)%+jlO5sSG2I$;&3{%GZHAW-`!v7@h)?(N?cH~(!HRt pTwg8IoSDVhPJL3>&mVEWqh61@(BjO@$;+ORn{yv`ci;Wk@-L)2?b84N delta 1634 zcma)+QEZcC6ozxEI2aYF$ON6Z2~MzxR$w4QEJ$H86NXZqb4pQ)FdYRMI4V#W12);{ z=3D2s?@%^|WF@-DCZrl|kOip3si_VZO=ql$OHAg)Zg#T^A_dGqA&_y7IQIqx~| zxq01Ed(G0gm=-nV8}c+In9jjROjlsFFnxoF#ZU~cTbO!~5`Krkt@<{5f}v}OPGIVW z`!=ROF)F-?V1|BgpU9LzT=*NjlbEin^fiX3F!iD44rT6CjT@V4ByOoq?;I0;fzX{yU2se@^fCIUY3Aj3G1Vetx}ojp zoX)fq&McNFN#XbK-)-nGWX;f%USSW4XR>5FGSkpS)ZL@C;0ag###>Z|ER~=W8!& z9??v47ciYgY=IGFN*~pm$AlLUdQ7u*IE+-(@6fu+A7_dqW1*p6(78|*b}nLSMpC#D z{>6s8$jaBMdxae+Uc%IXktJ!BmTL68CzyuNC;S1PWkwVmU&f+LYk{hlzueGANGxaC z58sna)v&J6xRDixqHsN>y-Nvu5OAhdTB$P=UCHz#+^e+Lqrwz|PaAo*uhzQa!WMX4 zOd(j-7-~d#jb>Z0mZ=1TYYojnWg(Ljwr7|Mk^Ahw1cug4O#hyP9qX$8^YnkeajZA; z&;IpH-=e&j>33voFjRrg4XT6lIqgeQcnSXJjZ~erQ761tcoxNO^~H!={ZjXWR+smp z-qR->f~UlgALAvON$X3hMt-T0ekDp(CEv>|vu!QY+9PE~CfxN3Qxj6cFaocpow!NW zj&5SAhug!n3!}oz2yRxt*k99#abW`9*L9*SThw3SEjo<_UUk!;*NF0!Th$A;H<mj0$522DBFY zAIW-=PoK= ziC*DF6gTO%8EG=&&ARtB&phbP>=UkrCuGDm<017?>j%04@|(3IiDvzm??a}ou(s%~ zjg{PK YcIdtx>@X7!W$vrb!QnHvK38&rDWkwdv)PP36EDqSteTw9IEQiVWFDqDj2kAeWSY&mak4A(93c4!NN$>3 z&oYN`+hiHm*^KKZ?_iz7xN>qR+Z@KJlRp8;iIe-;XEUCftj#fpv2yYWAXzXum2(bb z$Ydt2*^D-m=W)$pJrR>|BzCeRcVGRgRgM9!A&%~@5f|7Q7}hZ`Ft7kI5G(+bKmsTX z2EGBt?v9>*@xGpZzK#(LhK43OhUOOBXtJSxF8&PK1_nktV8V|FDj(z;;NutxHXtO( t(b+p5#AMJ7G|)3OGXMgG6ekW`N&{RSy%`Mjj7^PiDWk+hv)POx6EDqSY?_?UIEQiTWFDqDj5{W;WSY&mbFwS*93c4!NbZ_k z&oYN`-((rq*^JvJ?_iz7xN&kQ+Z@KZlRp8;nUnk3XER=!tj#fpv2pSVAXzavm2(bb d%w#66*^Dle=W)$py%3XdBzCeRcOTQLRRG*)Fh~FZ diff --git a/sd-card/mp3/0935_yes.mp3 b/sd-card/mp3/0935_yes.mp3 index 24814246c35b6db228ee9893ce7266aba403304f..1bd8fe4841c0262de88ec7907c16e6b195f05f13 100644 GIT binary patch delta 310 zcmca3v`S=xDdUZaX0sXFCSIDuxN34f;~d6^lX;ltFdm$|l4&;Mxyi1~a~Stcegq^h zO|EB|!?<>`4C`#h1(SEM&S9J|Ih1VoDh@-n}#07Q+hII@K3@ktl1Pj0KtLhIi369? Y09QwE1_M1~V>3fzQyk81fNEv{0KG&_AOHXW delta 102 zcmV-s0Ga=)6xR3<8`1$&;x9n*r97MFX4x#*^X$ zoB`UCe*~NXwUZnLn*pekxCNX6n3GiooB?r@@&=p%Sd)(jn*lhJD+rtgXtWpxu#?dU Ii<5u|1!}q|DF6Tf diff --git a/sd-card/mp3/0936_batch_cards_intro.mp3 b/sd-card/mp3/0936_batch_cards_intro.mp3 index 9ce8e37bd0068aec7e334f18881b75eb6d156cd9..0e791b3189cf234c63228155cc572570ec14b916 100644 GIT binary patch delta 2893 zcma);e|Vc!6~}Y3cG-`JL|7D!xJs>H$4s{_Ogd)^3uC0)^2|j`9j@T25LY7-VbqPl z2y+ESS5LaM+`G`NW0CVa#ScL^4ZbZROj9?WidcLpt&mMRl^E~~$ z?|a|-o_p^1{J1^EMLq3BTj$ZqHkYFQwv47d93V}H*=p9bljCMZTi88G(|XPcujf#a zrkB`JtZ0B!#hNngo2)6v1>p#{S`;1S@+p`zEIiDOQ#JLld77eLj!lF8s_B}x^5}F$ zK@M6qtz_HBG!=0|_%?gaQNsK=7zuwIEIC)x6dpKNk(KLhnkso*nBmALV87x#McX-j zo~8%bf4-(eY?%Syx@TxQ$kldD4|CRF^Cy*fV*iw;eVi2bu=fJ^Gj@TZRowDvjF){z z(~CSPe1sbuio!hM(8A8oVvnv16?JgtLM?j?d`{DoY@G=U@tI0?Nmhp~%cq3RM%_w(o_iZlnm zq$$p}axM1>;X3xrQnZ2dv!G=7Qcb_+lG(5}FdHk@e;GcH3t#7m6XO*XN_?%z!bJb$l8C{H-!K)EF+Y(JfoDgnf z&ozpgIDd^Ut~~#S$)$XJc?VTSIer|DT9^C^0eTfPCW%D#zA7!(e1Lmi?pQHMF5*CGn8dPOPD)EhbW zE$qm;3TnhxDcZ#D>tHh{e2PQg*2L^st>__6t%glsgQmS)5Dszc^-5TN1N0vj#<}r3 zpm~j=r#ZF;%2zcahesQgd>j0(rY^R9&&V<19`@Xbxa4m{l*2cn1C;nR-OdAkM7;j{ z$d+;8PL8a_oQfv&igc5v2>XA49JT~tp*sNIs(*+o$O`+p`DP{iwf_heniMA3dyA$R zkKKaoZ3$v#*^lAjpzts^+={xGxD_Lvw}Gy8@Hw;2$g$foVr@pv#+!{C`w6NqC*01V z_3*^8K}p7@Ho$-19jL5=Fu|=MMbB~ho$zE>*vE}`;al@Y*oke#NY&kjN_Q){4~8{u zX4^f6O2Q-T*@TXl--LLDe~P*&*{tav9@wm8mHHMIby6xva33n_HEH!M+t6ND7}|ZyQ`6Yg5$5Ek844VifA2u!|ca zit2bG0@I!C2#jl+l0?aDgFt~O>{vUDhSi~D8}3d`yE!L3!l4Htr9&wZBBhW(K8++R z2yf!n?Mm8ic?{nU3uD~)5NPf~TZwgP5wU6qL>%3rXpnT1oX|2vSxk4eN0f zvOS^YfJqAD?A;4x#`YqFEl*%(**+WtgTk%c(4)lWL=X1fnZ#PI{fc&RW6VAdbe8LpTlx4j~cipF!&x7jEOoVU%LUkdh{n9@2E0 z{ln1Hazx4e?jtxJs*h?)b5_{I&Ce>yI{R<1qolBdy=m-yERFWsGJ^J0_8ej~DD2~g zV@lMUIEE;99@mnSu8fiuGZ~ae;I}wDtj`;*|9KqJ?iVmKC)~}U7ct*)LQxB+PGH10 ziWLjOes2ApaX`L=`NKkKUN0k~%qNu;dF&*-s>;Ii(X65uIQR<6&-Qz)m=F%I=T-c} zk$)8#8-5M7P?FQMkq2@(-s=B=oF5nVa^w_pui~^4AJV6>mj88(Sn`U@+?|KE)qm7< zoU_7%-25kGto;ow?KmkMVegxo@;vq?np(@Bwd8-<7*-q*!;SyM>gIQq9PqJs@$XvIdr)BXJ@^ouKx4A~3r#Q~tYy#p$b|g+XocZ_O9O2$ zA+ggwK#HD;){{I0qvN&|t z-nolwmMmMnbjh-%HFYMZYu>Cm&iT{Ndh1)ZXt}A}>6|mmz%uIx?^n3JwKdlo23D@9 vxze*5x28*-vzO1Sbjok$nk%M!)Kss##$$5MuB@2joa_7uckle*W7B^C$X@WO delta 2685 zcma);Z+M$k8OEg{roxaNN?2vRINEp4)in`8Slwj89_*_Ou^}}vIz-?kzTaSvJydFc_})ptmNUT zHnJV5YAgHUpKawJO3J-vLA7P~r(?=Ltg|O4g zQn)^1#fc$h8LdYeW#LGUL_SK^&bKlj>G?($pv`4vA!d~O5j~3b8;>@!9+{)9tVM8v zl_ybi41F6s#>y^u-B#{LuELIw8TG{daVuMpRBnauSo%{u*2pq+eS+ilpS1D*cfX>fYc^sY-jM_|{ zU?l;6ot06Plw;`rtcs0>dagLBJc#zsSsihlNDt#D(n9k|R-%|b$;di{KW}9it_G|3 zL&|H>da{ulQ8<}OM!sNW61At$TKW{O*!D&GJfl2>=$AO&*l5&;Oe4PyewkX;oJt=C zPvyGauUJ(qryN7aX-0O!-DKqJKahbil-a#q3f$wS^qUFcVa@h9)UBs_wpGP zm71wQ(?X+r&n~3p(AP=lnN~IMi@cFGgjiTSk-RSxry{i8{Ghsrx8-WWMh4O`*)BgiTp{dQtHe}l>Irc;D$hnki zB$gVv9W6hi&AjqabpF_if#)J4_aJoNH*Zf6cpw;TO7{4*;X;re+c$CM*zy@YWoT*4?vE@cO(4O+Pl=^!KC_6ugqjB*pA zzvP_8W)XJz$c` zVfY@PGQ|fN!mbB7vwoPzz=X07ff1uN%Ol)-f0Ao8Z8mZrvYV-J=pnWh=cttzkr<_V zEf3RjUg}Mik1-M39%t*BQN|J7&QfgLVYG>4c33GQI7vNgb{d^O zxRb|&cbAoCkyEZf#}h`g&iy2JlvEDDm*L)v8MfE1DYmEj-Hg$Matwi|jH*|Dic#+0 zV>Kt6vPM_TW?3Ggr+IidpQ*I|XLv}rJjal}8(6-&x-bU#-) zAop|rq*9yLpP5mP=ZzM5{CRrSoTKN{IU_lQUtsyUUgU~H%I#=TdTv{w=nW>|0bO^fxQ* zaF#fpC{fRrzq6L}%B#@%4=YE(^EUNQy-jcZhv`E}nML;u55k6jTJ>{Mc^K{g;_8lf zj2`guclf*3{4N!kewRLk%WO=pf3pb=DVL!2J!V4TJ+{Kgf3$)2){5HcO^cCCtWES` zRcwZr#j&VEFPmbGv-I6?EHg`=vaw*5UWQ{e)jF7n4OZ(@I_90Nmo>2*FOgVBjlNqE SbI;MIvtvnKw#9sNCHh|@71!DT diff --git a/sd-card/mp3/0940_shortcut_into.mp3 b/sd-card/mp3/0940_shortcut_into.mp3 index 6722876f514869f0328c63c1c9e78a55c931b8a9..31134ef419ab71c99e4d42b3490a1597968ed141 100644 GIT binary patch delta 1149 zcmZ{iO-x)>6vy8gi$lxwV?@yESix3o9g6QUGDxZIyp~s_1H*$dO2?A2kPRC)tcVGX zp@~Edt$#GaJ(5zS&ZH}ozU2LKbI!fz zyKSQq|=k>Gfs_g_Q{_3FIvSM+RU$|A7M&`+4yrU~kc)Us4$=qma{EP=w? znYu77yp7}zL*rQ8!Sn%!b~3$-@?A_HA-l`a0y1Hyd)N^EfYC>kPO+hBEEa3Iy^k_| ziC~G5QYK5(nMeo6X~jK ze%MIf++nS=?`4*}p(FWqI%4PoyrZh+im)Gp2|Y(#LqnK#b=v7;I;(ZzRg4@r(p|Jc zYnT^aM~|n91x^@x2NNfB73&+d$E8LiQ|*66E#aoTO2TVMo-|U`>Pe07TOp%r7250G{lVX z7W}jpx|TLHf#FwmX)9i18pE70i|$TCUtqJ7X&qyybP(9!N5PoCAIb;tc@#f=mMQ= zF2si|@qNcis)D1Rw%Sgaf(M8QpJQ}k^ToMF5`QN?LvK3iXNMmApxgcosevm(S~p#<`laIR1hWXBdk zF-wXRcR6C5V8iQPDNyXw^c1_XrCSx3Ik{DElg)1^rWq>nnfi^_S-DN{4QIBwqix&$ zwqS?a%k&Ph@6=AkFz1ct)0>Jbj79}NaxAJCV&hwi- zn{Z6g#R20rCibd1G`rWewZE;#qIW#2A>#z=_j$Gp``pX!QpJ77%EbC+%3NpDyJ~%d z`(Lcnet|Ono_jfM{F1HZ{vA0WIM2ZYo_6w}XEkTM!H)OErYkw*8b*w1HpJbrz+u7H z>^bbKSX$vTPF9Ge+Wdi^gg$)XlJOQRkBF(7IilFewo2c}f-0|^t`gKRb=0SrH}ri^85IqogPpLmG@<9#NQE_602xXSiV zeQAqMD8A>Aag_Bnf?v5%qj=8l&wR~dDZwAiq!fR$>7?QTgSBeQ-&ZU6g7G@dTA$82 Nw21%KoxV_)`#*N+iLd|w diff --git a/sd-card/mp3/0941_pause.mp3 b/sd-card/mp3/0941_pause.mp3 index 4621ccd803d56c87e8cc86f2336d9443e2d49583..9f607018f3fa736b214f98a9e7d15e8977e54047 100644 GIT binary patch delta 430 zcmZ{eKS)Ax5XXOK{!7g?ZF8ulMU&_ADM(ANdWo3NhbUN!ODz(#wA&y-Q^_^;yTqU& zhnxzW#Dt(Fh=yoxsWDnw`@M%#;1}-0@x8k*cM<)HouwesN^?+JuLNy80D=zsYy|h% zwQ;ybZHnLuKLoF_VJGNf#K8so4uVIlI0=^UN6>|*E)F*sbrT%pli(fhO%n{z<}q{H z9y4>rYx3P|I)^@jJ_ct9hS(DvVBXJRgr9zbH*C(5=obT~KL~Jmz@4CZ`)l!hj-ZK& z5Qlwy4iT(kJxq>oHrl+z;QR>=!_J0kXk|64RbO0;^%!GzQ9>Py0>ZXbFp^m{r`HNO zy`WZELRMn3l<@wWR`iV`i%OClv!MIN^O9z4soPdSxumY=Yi7%qrFc=vNJ0y{Ywk&_ VhNkA36i+HCIh~pC?$P*|{Q>W`d>8-# delta 222 zcmW;EEoeef90l;5H=k~Pzo~6SuqlFJD_Rx7Y_VG`7W>MIFgE3wcKSn^A| zU@54u=5J82=W|GOj)zS@3oEQxam@Db-_D)jg_(##gPVw;$XZnF^VLtGODERkJnFsx D+z400 diff --git a/sd-card/mp3/0942_up.mp3 b/sd-card/mp3/0942_up.mp3 index 1b35b679f31d0af30cf47464a35f53a473353d0d..36e7cb3ce7d959c91ee74d6c5f7fec601e6a0cd3 100644 GIT binary patch delta 664 zcmZ{gPe{{Y7{}jdsWZ21%e1UzGtIOu`I&7QcIa3AQdyfr9GC zub1&YNPnRPjVMx?WRVaW^lw2(e=fTOL5I9_s6)?hcnW;r{qXXA{=DD!`7^w9Ivt{~ z6Jbr6i2~C!#)#=H+zzH3vJOoTV69_f6ouE3Dr0&GU%3`#ww&n$5*18ItOyercWRnP zppxkw7KJ}Bx}NC^995bYFjd7ggKihoEiAh=8cm<^rp9)DelycEIBT_Z-Kk~KkJK?e$2Z|4 zT-c)J-MXzzlXxPWM!cS7=FfWD^VMzkzgo64Ww0Q;f-^o%hw;~EZzHpVDT{D}78V-p z%CVhH7vc6Z6_NF8`UI_bv+a z7~R9NXGg21yO?UVH{ac6E6Z(~9wWJz=?1(3rnATin-K}>a*yggaSy+O*N_fYq|CTA zX!ck`@108Jlu~7KByN@{5V;i5cxR8<7aNK8#iHhrGQwTKj;^rlzic9Qyk7+jV}G!O zn0qxpV8u_Gr%D3`2h3xU5&Nq48SVWCLxzO^+|kOlO5>IpQAT^`fl%mBcn!OkR-4s7 DOTypb delta 456 zcmWmAKS&gC6bEp>SEr@tpUX6@%e*Yp?v{pJTpCgW7Z(>vXmQZR#VG=p8fx+6TT#3( zf>gsrNPkd{pc78AODXUMJBu2wAP5|~#i0h@Z}|-G_ul)x_dZ_#H=enSUkd|#%=HLGj-3myRd(Q@H75R-(x0SdjP+~ysrQN diff --git a/sd-card/mp3/0943_down.mp3 b/sd-card/mp3/0943_down.mp3 index c69357eca5b22d094ee9c52b241c77be109c2cfd..e56063660f6cc084c9878e1e8cb45d27b24f1a4d 100644 GIT binary patch delta 701 zcmZ{gUr3W-6vn@^wx-)MmuXqcT$atY=C_vG@TRXCB5U(Q9HB@9-+{!29Fyo^u|aa~_{wIWyiu z)`?a@zKJ5!Ym5-nXSggX&sqdMLUaSuI2KDxS(zTfV-u9etc~dVOcVRF}tAK z@Ru?5VL|c|hRd1W!Qv2f0~rU?S+qNuOnh|;`h`&!(;v7uG5tVJavre?rMy(Z^Z@D2 zOgVTfnWm7h6w+0{o9Qi9CBNY27N&0qRSEip_f^Wnxvfl-uvZIug2`%@I!9}m3ivMh z92d3;%A#gF(+f;X7GTy2%as-Ut`#(g>pPg9qH(8MV_xzu20enN@z4yip%Gw?JB8p3RYvXb1(^b^aHS&Z!ya@OCd zmRXRT!SH^jH?a8BQ8GSti1wz^nVJL@F?v8LxczE2Cpm>!Kq)K*l-u+{rd#j^nJyw9 z6m$yx%}hF0o7t9joG-k>ef=s1_41TqMth8oXzxe6rsXtEv&tvqUWpR&yA*LV++lPk z2I8HGxY4WWk+wkSNW}SHHJLckrTKL|94H~-TB~TN88u>>9t?*LMfAh#*uA_qto;MFiSDZa delta 493 zcmW-dF-X*57{+=2b(!a7cbS%TnU`hSnHrRf4J}SCEiTd^$qlvGa6`1X2ygK&kP8Voh&6T&~H_6a{(bbjD)V@Td=6pEbMFFa=^DJ-*;G`AW}32(XW z{LX~~!VjKnGJNCvrr>b>pzw%w&4y>Z-z?9$)FM>)#rcxA4jES1a#$$wsk6e%trks3 z{M~BU3QNrN3b$G6HC*FppKy`eeTvP+Z*Ht{sehVd{q_F= D$vw;O diff --git a/sd-card/mp3/0944_startup.mp3 b/sd-card/mp3/0944_startup.mp3 index 7f3808b562c2b284a3191f21189543077771709a..7ec1cfb3c70a7558ced9f42821e58ec0a260153a 100644 GIT binary patch delta 665 zcmZ{gUr3W-6vn@^bThZiWm?v(GmC}G7e%7P-i>8gt)=%Ow<@9-+{!29Fnyw5rBInSS=#oyOHT0R)iU(_mo^}()sBotxxV{dVy784io$IB!On#b3r(V z(H8xqqgA_|Yt^1~w=rGCa+{(QQu~?C!y8~ag@Q1NXz-SE=~u_uOIiF3UO^`4Oj`+i z!0NFF-@6Q>U>HW39EsZ{3M5^MSR&G6^~OhHz44eeXoSLMFl>h0|Gg&TNBfL`X-0x2 z#64^8{dVG*b-dJIpx-(Y9nn`2w)b@$=rm2!fAVm}deVe#MGdn(Vjhf`;dRCxUn@5L E0hQw6O8@`> delta 457 zcmW-dKP((@9L2rEsXxWlziO{awTio?p%zOQlckGA3=9&nSgwOEIy4f}hRd_m??wDu z7;Y#Uq?I5MG`-X%LNx7gf~0AOS~}>^@6YlXzIosGec!j&b=5ik6u;sv4kz)HaKr%! zDHa5T6)prE7TK05Y%*=!=TMgLiQ#OA9j;~zTkHr5)4Vj!axBMThLIaWg1g2Pdvk?( z2J#%%IF%#=(LpOy!CX8!rFLb84C=~Yix=8rQnquJ%H;NsW*%=bP z@~^Rp6SstKd{W|&=9dzgTYssLWKNl%%#{g0`K(+x=Xc{7UsgCvUVdA6%O&FvKCcuu z_`A~C7bAE4FLif?b#5DX*d6u>|AoEg-96txOO?X`Q&qw#2ku|DR4u&VLbbE4wi@9* z(?)UV!F5Zue!g1k^&Jm|&%87)a_o^$5~=f^yT&AYANz}edf)a`z3-&8L73!mgTpJv f8if&tB0`J_V-MS-lfh(0^Wbotf1;xtj|Tq(*)G0g diff --git a/sd-card/mp3/0960_timer_intro.mp3 b/sd-card/mp3/0960_timer_intro.mp3 index 6a6b1fb3e90d9ee5ef499e9d1b6bb5ae6af98a5c..6cba7a87e029ff038a2b7165b324e8c7e54cca01 100644 GIT binary patch delta 994 zcmZ{iOGs2<6vyweOw*|}%U)AymRX~Ar^(T>hgTh6qvH?>B}6WA6M+QMLINw?2!)To zMfRQSp+yD`l~5LBlu@D>F`Fc4V+$d0S7u|>KdkphzO?7KQ!j+GFTCJlRYmm=xYY z^IoPOh|4h)#Xt^AXNvb}XJ+>q>O<>(tvc1CRYZhS2<93Z#e6Qy6gu*l9w9Sd?~Lag zsi^UQPH;&$f<9aGdcE518Luk5a8M&8_zZoVj07<2Tbp_J(w7KP2|Dl_y2o^tKRr0_AC1F>DIP$dHuY8O{( zjkA?Tl(bfmvrJpFHghf4N~_g!tvuvKI(YGr7YjUu5JE^KRBFG6 zWTq`Mqm5cLEi`I_tgylk8>j?!Fvybx9`fR$8808hoA=)Q^UvJvqmK`$tC*bKex{4v zaiIvBNvI1>htMD*4yNY_t`d5MIprYQRttTAJAtVWqX|O2s9PiS5{t^;=uBjK1Mga) z&zMlg(6~(k1&(W^aL$Vp=WTd7kYuHavGr&rf--}5gJ1K2B86X zQiX0Imdg51y-P^2w8EZ^mi48vW+&5xo}y`!y-G}Hz0;p=C*>JJBbZg*N9$&$OUT$F zREuHd4b*0u{)J4|>K$8!?jd)ZXuspiPiSy6{l&7|^!IKTnuI@#=?mgn7B}xOubkOd zF_LZF1wEqKIpql2cG^Gg9H#3S%`qS9c8NN(s2o7&Zl*i%<_e8rLiretdxZYLk;n84 z;XF}i0{Q05Og_^?wCuI6U0&-Vs+>ZofayKv3q+^TzE3C$Podq36|#0zzuzXfq#Q%f z0jultncI^-Q`mISen>20ile{CdMQ67T4h!_h1O!GpUCjr1c#N=s4WpywNS!3_m0C> zG53hgYh3vb4W+EgmrLz!Z$RiD{AH|hyiDjjnvaTBaUK(Ti^wt7EeaktwR1{ATe;9U z+!d@_Fp*+dYq|ZRnl_Ww7JgM t9HYuGLe;h#^VLE((H^ob^3(`jMXZMD8tTsqUBHsE3_a(BF2i>o{{ed&NreCa diff --git a/sd-card/mp3/0961_timer_5.mp3 b/sd-card/mp3/0961_timer_5.mp3 index 609c6063e2c3743b60a4e524d9be3cb848dc8369..64a3599b8b2ee336e74cb6befd825dd4003dc784 100644 GIT binary patch delta 453 zcmZ{eKS+XM5XRp#O;adan?I zAR?ilXfoO&2&5^9mX`LG*4|H?0w1^^$K5>-w=I46_9mcF339loyb)aCDiA!PRY!1( zmpTqLR5}Q%_$7FUMLod|4j4EbBwRJY62@&DBKU117{+oB!4VGGwY0jO!#XZH2u|=< zu!0A@+J`=;mh0BD&}YgM9@+5>U9QB08~!+- zSRECbob?SI-uXh0!I<#IKj$-#t z2j(W_s%vM+wX|V_ziDBSwTy7Vxoou7%^FU)nG-HKbhg--iXN8o(avi=YImlE8si1S c7S9U8Dwm5wnO{Zu;Yjabo%=I~u^;ta1{914ev;6YlslvUKtUYrHgE=e%EV%4k62m>&azLk{N)KJb^}E?);Vp0F+? zc)`zx!l$pQc+a&0!8SX?f-gJ^Yiw{jBG_X|p*PPOE;3Q1vBIB4f^#n3_T1WH zL54@g8ZS9oB6!Z(xtitNrt~>kQew zPWpRLsjO1UZ_4Ohjs|9&Q_^kfwfj_!YZ5EC2ui diff --git a/sd-card/mp3/0963_timer_30.mp3 b/sd-card/mp3/0963_timer_30.mp3 index 4a8caa98ad6ec334fe81693ad54af0017c6b4059..0bc5ffa576134731e6c5e0cc42c6ccccb4278001 100644 GIT binary patch delta 484 zcmZ|JJxE(o6bJD8k4cl7)cVoH4;<>O$V)V7pi5rOLnJ0XL_rJU(k?Ex;3SF;E{f8I zf46%qrJWoMexR)mQAET=lom=Uba8PgU0htfFE|xEaDQCR7iTr~_u2a-E{Z9QWbwP8 z%t;8=8Sx3$xaQMFr%JHTOJkXHe!&h~0~(wB77%Q3_<`UDuZ@>{7t}asdq{A=Q{ygQ zKNNhWuUg|XKUNFg@OfD9o4>*uKRFc<9I@_^V3{T3JaaV~v%IPitZ?D6mv5^TEV5jy zvB#Hnf;4Z8S$?P&j56M!vC6XsL6H+r1WOD?HBR^?>fM})dHequw>aIX@s_bB!5FuV z8M@5^i~pK+K!pyEXIN}mW~RkIXS>dj-Rq281eGc&rTpfZ_*Btg(v`@&UA=Z+VLabg z$lD_-9#3_|)7|0!s>6lsfNHm_?v4tD$X$KVar^Dj%7URmyC*m9eNAKMK$qpk(^oG- Y_qDo?ol{n4BAH016Zf1ue>bdd0V|G?7ytkO delta 276 zcmWm8KP&@r0LAgV)6;6JuBxSt#cVRD!E#FziKPRJ-9RE1lSq&liC&(C->XDS1_l-e zQsRH2k`7`KLPA0;U95h$e1`Av@_vS@#mXSwf}%kwXb5%ALO5m85l*=07;C9TxaYgI z&c%pu#a`FooX4(E<9Ms^!KSsz^{Bxs`(wf*Z>`syY!hzi#0?I)9T%23oDjbGoG|$0 zTvBK--7bg+)(S^c25bCE301CiguTxbHhJb5+;Ji;jPTDo#*I#4iUS#geLiG_9Zq)% xTa0E6p17M0Kl{0G|HXR2`EG+{=6Zw~9$CvQ=LMg?dEK*h=`B~nh(J}5s5@n zLkAWiBv>p$FfdpwCI$wp`x=wPm)u{zoOAA%?<;W8xLQY#QNN_i=snW`Mv3VaZX44r zVm3*KFp{kKtKuc5lbKH7Ns)@jDNGyaNoAVFr{W=2?20o-mwc zC(|h!T}(T8a!K08xSMGU+38F%#1)?r%8;~P2w@Q-xMSK@n3FM~ag$i{wYM(>?nh`10USv{rQ1Qpacp|Dxos<%f5!tIf;-YYbJ zpvv1&@A|XtiL`WzN=@@sB@l7{?z;@LL+?ujba&~^p#jUP!|G0NKvOCkYI6K{Y8rY- SXw@}7tD<;c=f|x88i^XKoprA?T4H>*+AfjxB z6+zT6a28Qk{$vqB1u(;S4@Xvj%?27jC%@p~HwLyt3{Yv^dx0 z`Zuf3t#09wnSilg2ZVDj_6SGpSX(>@8vJppS9s@x^@^)~LX%#|?Nmd;6Z2uA!RN5S z1ver>o$-F*h7Ic%=c5K^{EP}^?hFXq9FMu&j2Vc_aiPXvYn$akp~&fk!4lgEm)C~e z&tTG^%G0E9%tFe&da+ixIqVivBf<$Ut$Qq{gsgglWC@9O*Updf)SaShH0RUuX>n_^L)q*SQ}f|CISg0Du}75 zAc{=>wTOENCHfdCi9&IcU{bUxYBok+3pce1Vm57Bbl>1A@WB0Xxc8iM&sTTKyOlCe z{T@#%43C!u7dZmK4TepDuw*Js&{-pR&gH7b8o?t*0t#i$1q4r6SSxtUiuN_ff(oA* ztM%*OYD+x6PMKDPW}U(--m4Qly#Q{5*X2 zs>6@gbw0BKMcZ@oc8@doA!r!WhGEp`qhq3~MqgJW=k4vWd)=X2ubZ<6jYKlpnaE^9 z|GgI6L;Xf99#3^val3YyZX~{Kcd42YF8`u96M{oyONoG O=|pmsbuX+m8~*@P8pSUF delta 398 zcmW;GJt)L+7zc3QKj-C~cjxuI9%r~oNy=hyrIc>5$Y5ZxC|O8ZB$CegGU$1_MFx`Y z5{@tsStutmV7TRq7%U71zrW?v)6?^Po@YDw+_&6jji-g%1ryr$ZO>mDk2sPeR*OZ;DM&ElR2;A~ zR}te=u9$klrPyRqp5l(X#w2_5#e`G&ibu{CC@$GhsOE{YLctsdiWIN>HpaMGtk`F3 ziJ0t7iQ=83rHT}@-1deIxBXUEnc|Z##vM+T3kF$Mp@?wLm}0QfzW7loxaUHZ;+Vc_ z#T74%M;!8q+5hWNoN~QJvBLIRL6C`BMK{Ol6ial~D{i?}FPLVaL5)e{JZBmeH}rS~ jYm9sCPJfePmS4tUhMEN(^!wED(=$02;GJ)gQJ>=v)~=zz diff --git a/sd-card/mp3/0970_modifier_Intro.mp3 b/sd-card/mp3/0970_modifier_Intro.mp3 index f90a945132c41a46d670585eaaa476d9d217049a..1402f5584c0bf6f9b346344bd78e3e8671a40fc7 100644 GIT binary patch delta 1135 zcmZ{iUrbYH7{<@rfmP|8Ol8w`4%9KV$WB*ihdAv~D#JoKGOAcfWXVVtZb%j`7P1XZ z&;;kg+yjaDy(2n|irKm-(ST!JHj<4kVOg3O%@(}i25<0!8*WJSeH*Wq_$B9;lkd;_ z?|CNg(%DJ+x{@B94;!jEpJlp*PGY(VpNHuOjCu^6M(hQq$CwlThMqj88}R2Fx{UFB zrf-p4!So{*h5w@8d(qSJ1c8-$by|2I?W>qF@T@lUH-=Yhg6aaMG#(TfdEDh=I)%a) znLbBa*o*iYLxY%K!}J$=Ut;h!!blg%{BMilJc?y~^|*#)P-vZe+TRg^fn;^lf6gi_+JazQL66C$#wu zUB$9r8y}NMKeSyw5bl+F394$69fmn%VpA!zEXA4s+{96ruhViXT14wRT z@?cT;2m0SMl08t$bP3bKLA1Z6Re81>i8j1lyRP2BbR7?N7-82=eN`A>>PA}l3*u!) zCOluJ8GCo>tV+t2nQ|j_rry>GF9|PTz-Iaw!Jwi4FcVZoI^I!rydfikjf6CNw1VkZ z%nB#aU1^9>6lOY!G2th0BP#7eMCaW1uEs2_Ql+MZ=g_v>&^;{gRx1ouGyRP49<43A zM_+Zmmow8|Rbh0mk*Z?*nC37ioI+2O=^Fe_&P(cBa68^T%C;fu#vnYhqdcy6Vs=d6<$X72Szk4YE}o12{Uk$ zs>MRm(C_FwqWzX0RgX;xAEK?rNY~3P+WXKkRWqD2bP?H<=IuPr(nJqG}I*;oEFQjh?E7ZLIwXj zO(tp@t$=NZ%5q5fo}D+v+T70XOFF?p46$` delta 927 zcmW-fOGp%O5XPBJv(!v0Eo(FFVcMFxS=(c+wuko6YOS_x^Wa4YBP8(TAukfy1G7&e zn?VvKGD<2d?6r_)nXv^Mg#-o}Bm{PlKnRIuy!;OT$IN{5eHUKPi)VCYJ^g9&F!`Gz zLLpR>&`ZQSgr1_)!PJDpX+nQ7qWplmD4{2CMl;<*Pqa`c0@H<_V_f+YjWIVIr{dt6 zVNVB?KTtVSXb_HArmtv?wFcfep=TJ1WBptcFLW76vxFMZrM!aT1g18OCJ22&{cNEY zWF!h*Lnx8yDuPKuy_irwM)Mq_doC-(bA@_PHP42QNwx~@$wK$xOEC^BJ5W2H=?+pB z2z8-b8G=7m=oQ9NS>H4)6ncZqMMACUQ@%xolj$KQohG zjh*RC{V2?^?jy=J)GZY{3Fk7V^XOS7bQ6K)LJ1gGenR64*6yxMp}QDRwxM#RP36d9 zt=5`ls=e7lk1&+Y%9>U7E6F8v4qeLkD9&Ns@Mw;8tY2-r%E&c_a#`mLuCWbIC_B)c zCsYHsn`snbw>eU^*4&B7XEm%n-@5zO357AN>__c-rrSvI2wg(A@+|xt%-XRHw&#Y8 zRx@*xIn}4Uj*87p-!Zw_qR`?MdIwK|%@!%JU)5Wt!nD;~=-kRWRpBNqYtsI0uU|x&`m?CI6YI-w|S;qR5zfe)es(P}_ lw6`2LH$6e7TZja$Z}kbGEX0>f^nfET?8Jlei}066{RfntihTe8 diff --git a/sd-card/mp3/0971_modifier_SleepTimer.mp3 b/sd-card/mp3/0971_modifier_SleepTimer.mp3 index 6147ec016ca2c8ca81a124b83062e899bdc32c4e..69cf0f022fe77c7c67d5cc978468be2a51840054 100644 GIT binary patch delta 435 zcmZ{eKS+XM5XRs8(lj;G%%+Cg0<}K>R{XlsAGjaK-95+i?AaUM48mX`#9_7YM6ivEKyZT=iJ*oR ziNgu1Z3H{`E_j4_9lyxk4q4oHa_RdQCwhx{C84I2u$p-^Fji%Z=|l;Y8x#=Z zhGGeSScz!4SVW5{8Rqqc+}_cU`QJ9JO~;u_mVNF9G)r@zR1-5ww(%g9RHmZ2ddocW gP&_y$3-!-U8e5$vR3*yfA-_Km7?E4VU2dA$AKnUpA^-pY delta 227 zcmWm8JxGFa7zFU{P1DrOk92BjmZ%0@T3no*UD_I2T3Xs$noGGCm*oJ}!RwIWg}JaW;=8lWllA4+ix_-1B0`6UQQ?NOsA0(SnA|8FG*9;6L(PSV0NPq&0RLm9&{ N?=t57uCSQ({{et_TGs#o diff --git a/sd-card/mp3/0972_modifier_FreezeDance.mp3 b/sd-card/mp3/0972_modifier_FreezeDance.mp3 index 6986e6a9fbb5ad455a9934e1144585fa55940159..023b25d2ba56de01d567b224cd39e04b89ea2f18 100644 GIT binary patch delta 1487 zcmZ{ke@xVM7{~AVCV_I9WKh;+kVV)L-9;QKWSnvW9zPD^3dsVCD<$sUAL{=~j!I5C-ON=Bi zy@lZQOb4+bY(!_Gp+Y#5nEuANa2BDJED@|#hAyLT71K{BNM;(vbh43TEq10}q};&t zHlo5a@UJ#vnOn`oXupx^H)O2QN=DZh8bf^w)7MxOeu3_r)Ze|zUp~_)ew)*weMB!E_^K`ZAb4!mRL3MDAib z3&#c{o+BHWK1A?t&ATA%Mdv+6T6AVIy^nF>&j{Vi^c}1$Sq1vCm`YKwQ3o)+(MY>3 z_vvYhlPQX*@FM)#hWapgg%a_?|)|(ufr@T|oF@&1K)pRD{8;hUVaVgy{p!3NIm2r0aEfRUji? ztuwfd<$gi)a0G-7wavK+3M#vWz)D9za=-Z(c7L=%ExZYNU+?S7_B$S7yZ!f3gXXX`68`=anybXRI$?kXc1n5<%&LUTZ;npkZlW`DJA zxu`~cW`t3+KB^JYYSl6<96%tb_W7VnxZ^RkWInE&8WWD8u}&qtRHqg8>{5E_ji@D7 z&vYK)Cv?L0-Av^e+^vy)PpW-3&d5`$Ku3dCGSZ;W1{<07VL`Zn&ONF<=hNEnxNrcW zXX09FGBk|7Cf!RxNR>GqGBWd)XLXV(&uPr4a0>ptMzlS*m+3It_vr>Rnwh@EXtR+? z)<3TwfJNaHy7#MM-3JW4hRFk}>gI#`vL=RAxBX#bow$OcL)!L?uota|wdk}LH2ttJ zhQJYB&-@WXJ?Lm*I)uy@wV*NK1R9UV#dK8tdtQo*>6j6T$ByZ189uJb?5#|vG1zKk zK)#ptJ1{Feg~%&R$Khx*)Pj*VrXYeRSiTPn!Yp(~Ou`_Sem?{E$up5)&>Qg8dP{tD zza-jhQJc-SLVj{xh*Kcu_*D_eE%BCBHdK^VR(R`dZkH$9z1fxgztdo4ah1*Ka^+^n zQE9(=UgHatdw0bH)Yf>n`y2Gu=B#$*O&ndh`JVi1lzZ~( H!?ynbsvu3f delta 1280 zcmW-hZ)}rw5XZd-wlb(lg;mgsOmu=nv@(V=grO8Bn^KhOs8fbg#mcP6z*(8aIA83A z%;mcf$DNHhHP-0jn3@i>MCPBxI7zB5%S@45Tl-S_#u z{ibv74d%YLHs(V#qcf`%3wfw3c)2p?_qKYQwKV(7wSU!4MOi@ zNI8fmAJdPR@rknMMxoD9wUqT~cB#;Zh}|UA5BD+?m0HFsGf*b@Xowu^KSWzzY1%{P>B66G1uW(lADv(?uvG>XEIt`B3wJzbk@ClFQ$xxNXlgJE&or3Ap54Z(Fsoa#VWE!^ zd%`B{ZWL-js?jnBp0xNxfr+QgfxbPaB)!Mp4K)cJ#+333I`^9QDxS7}hn4+^JX7$} zKBjX>?z6pAN6eXH5!RWvJZqEmJZCxkl;fz|&+6^`exWwBKW`f>iwgaMOq6w!;TP-z zm{yLX`+zyt-^_FzBhBXO=!^ETx?<+r-WWTHV${53ZReCJ#1EQi@5@$xKzR|tL$;pD zLrfo`qebW_$`6~MA>}BVjugyv#LoA;QZQ31tHrae_F2YWwPNnLP!CdZ)&T`xvwL7d qc>;-}LT|y>#&itnHle);y)Jq`OerhTnc$+Yr1tw9{Kt+vq0s+!^D8X? diff --git a/sd-card/mp3/0973_modifier_Locked.mp3 b/sd-card/mp3/0973_modifier_Locked.mp3 index 307cc668a82ce32aee47b4c74d5eb3d5a87286b6..d2923ab0b1fb5ddc24ab315feb7275d57cd4e390 100644 GIT binary patch delta 520 zcmZ|JOGuPa6bJD8ADu>>@tv<6!&!JR_LrA9aS$A{Ou2QY3iI z9rYteiUk`Cl~_!3p+wZHQNbh+q88uzI3`$OWvSp3*VJWZ%B;@+T^9T@SuXg&3l)NG zey*?>3ewyH>EvpXxft>MT}STQ3lP zP+!t-5Io^wgT(}=&k64FQlsELzo;X;6Sqk4Z(PvK`6dZ(UL1WoO85K}kD4OmuI~-F zJ>Jk>#F#Z>Orai)#k`6>=OgQb%#tc=_Ha@QqljigSi`hrp0kmi9EU3 zNxt9nZ@Rbg0|xrt>zU!;s)en6sSZb@`R=u%(@K5M%^0UO-Ih+bx1F-?#7VRH2UU!r Ae*gdg delta 312 zcmWm8PbkB27zXg3@iVrW&1{U{6bC6Mg^PrPlZ%UslZ%TI%4IG}OIs<{^DF6ntwd^K zB~#|$?}dZZN=Yfr!AZ%*#liP?`COjf=Xtk=ABHyj@tz$JB(r73E9apoFyK)fGVc*M z9II07uwpE8rP|fD1P45~6eUj9D0cZ{bhzOa9J8lZk>|bfiZgYJ3wnHlEOS0J4*C`A zeDMn^TnZ?P4A(1eSumb(JSe9BGw80Q8WbPw2q~U;84|2=D(p7=8jExq-K*Y+;D(P8 zHO@A<|9hi?Bko1rbR?#D=9{s^@csi)S$(Sj diff --git a/sd-card/mp3/0974_modifier_Toddler.mp3 b/sd-card/mp3/0974_modifier_Toddler.mp3 index 406a96147d8d8c5a598422f543d7e7d85af29020..4e0084a1f986cebb32de66c1ca5f1683463278dc 100644 GIT binary patch delta 1805 zcma)+Z%|Za6vp?g68_mtGD&MQNh1}cU6G}Pj8#|C<&|!F%>mw4j+Zti)QnR*!;g2)eeb#F zKF{;sQ=i*^zi2;_M^{@sh7Pv$F`Y%2n9jjrS0?O+PQy2j={yF7=MWjs6oNCwPzkzH zm`)*h6H^C9grA~0)yST#3A*kTcA#b=(-GJw8QB@1#Kb6=%ybF;lMTI(ItNn{X*V;y zgQW07_@~Gq7@DGYHQb{0jHyf)&^^^q4=U4`u3%L73!=9&{RH#xhmv zbVE+GPiIO&QM$2D{}Ti9Z+Hy#w;Qq{eFoEbbP5X*n90v#bY%an$E&J>A3NL#OZ<0{KkuVL0E&3{4L(oj~S;EZus9#}HbgoyV3iwIEiY z6;GiNt@;Yp|L{XvaV%BO6HATk@ja~dL17e;BK6tnRS&zo>UMCM)<=X5XkM=GlC^?q zA9{sx)cCY+UumRgd?nKtC@5BC`iqTBSoer_NL$5p7)jw7_*WZIduTP&2WaqXMlwp6 z`p{isM61eDb$nEK9?>#Y+g+}mFPEz(wE=Z4b&a8GXkVilC|b+(2?m6xQ2(eAf792g z&YczmK`Tqa6ZYD_(exP8ATl4f!X%6$RAFTFu?l@mY`s3fQ)y(}zDl(u`~*`S92<0< z*r2cNdr}1*6t*Gqloh5bBT?(BV)_BWkiPbaa0{BBR{64?QB!+`7f|!8j$*IYP{ynE z*cQ|gTA}l|*FoV|i0o9=ox8L{*Dh5kxLd6p5w@W@Vq|C5 z9(`i3#hSh9iG81u*u?iSjYGkHrr*%N-_Q{18uU=49nkP3g{|;6TA!{)T{pb06J{LL zh;|<|^cgCf^ogUw4n*J3;JFW3NAi#!&)PRNBdLeAL;GQ-y(oH%=>P_VS5V(<mg7I%V(2KEj|gdGS)HJoEn?HoJC^e#NdH4A;mHGSc?nU2HJ zs{IqKMh=%R#3)F|-L?hG_gh?)llaaKsz%t@9T9 zDz2v5Y)PBVHeUYZchaIj%GR$mkX!6sRaRBHs;tyoVRPlVvlqGYC;#heuxv%SEz9M~ z&9+eHxPE=DFHquLZ#7uA*1OzarAymFS5CRhmFIG~W>+tpaHFjOpVx15<>W5P%l9n4 L!MJ;_-)#E__h#1V delta 1597 zcma)+Z%~v~6vjJSgyN=bCS`3VNgIk;o4J~pVzo+QxmH`WQBtv`YqgeWMBxe|u_^*S zo3Y*lqhd|#)Q(0qVe&+OxO$^hxbyZGe`@25V?%$c{no+m7+U?DGC4O zOeu^CKSOh-kv#c`+DLm#5j&U6BfE12F#Quql< zCd(igo~-|>pQ82LY^JkFWgGe$0SD80j0t~7>`JEJ;GU|T`=>JPLg*@%sWPV-%17rk zCI^agj4kI949U-h38=o>kR3VKFlD1lSPb8ErgIpXZlqK6TBZTGuG1-cg(pz%G*TIN zGW~`4^-QPXnPKQ{49sAvK==lxzhR%rltyBvkv`sBrmv6|o5e*(Aa3}Y5!x9Pv!ZbJ$5yO|mhx}E7eWX?Bq1fBDlK0?tQ zEbAfRQB*I`dX9(1hc4kE_!csKfRTkpW{56gdIzpMS-SNK521Xqb{=2M)Q)(eRy<3L zXf?1z{SV)z75h^4Jh9Zs9`7=(r-cU*DN>)E%hki~$Vj}dUmW}`VxgJRhhw+MkcH*)((#QnBqtZPoiX%5w(X`F@22s63s~NYNnr& zT5UwD!2Rm@nD7i@52)JiHQKp?But|EAtV0gJgho* zS@ij>Ecp-EHfACE2-5|)9<{TBYU=?gpoBjXMP)ROSyOxs~E({-Xu zKim6+3Yr!s5P8xH(>fzj>t4t7BmCw1*`va3XnsoND|lK>?Gv6w@EIM&R-vKnsL*3u z7-ae$gF%gB<+B*0vnkYVN6&I_Y1(q z7bjXPV=)iyFID}=lmEBphnKaFZ!^;%Mm8I|h-jr+>Dr>ls@G!qR=tqMx2mD>SM{vDF!gO-CKm6cp}Y8pGfYLw};OUJr$1r-mmfJc5!2>*{LIb^RMUVQ!;FG}UP6 za|ELL#xY?Ev0WNG_ipP*?$+ZO+M^lCY|;*$O-xNF+RN06A>nycHyb%7IW2kxbqQ1O zwXz)lkyb+;i0)ePz*y}}gAV;au!n0Ai8$#e{!{hEb={hGe;TTF*xZ`1yXHY112 p8)w>yw6GSD157n=8be#qZJ6@lXU=fW>1o)2UpBS<>-n+v@IO81x|#q0 diff --git a/sd-card/mp3/0975_modifier_KinderGarden.mp3 b/sd-card/mp3/0975_modifier_KinderGarden.mp3 index 8d5fbbee74dd8d7236d1f1f3522ef21555788ba1..51c1b156603e8d41beac74b95f22080ecbea31b1 100644 GIT binary patch delta 2531 zcmZ{le{j?F8OQqsSyZaCb98gd9G$MyrDF+|f|j{diGYO?8QCynqi$q#*|bw5!%Qt| zWK_jc-;vRNK4j1UGa%7ft%Z%Ctw5;@KLSepQFPffyWRDl-tEfm`p2Gk&OdhE=v`lz z`+Ps&=lOiz&-;14r%z7lJvC+1Y$gzM5)foam2r6Wcji)jmj1Jww1k@P##0V!z^B} zQRckN5}C!F&U&MQr|PY&gzq{lK8z}R5u44@R5!3peaig^%wY+}<`{jqV=g1qUeEYL z%4gAfgV91v-oQQGjaJLoFwe+pZ7iGmdK&OFOWN8H25S zYh1Yxop%_e`zJK+uu{$RQ)Y3jHqwFQYAazhhN(ayY&2T*XY8M2%HwFhlXfiK zNp0i5V5w>%%#w{5)g<&wmV83lkHoK79q(Q2#@t=hIdZqvEY3Egnxxv8-S=zWVpRDt zV)q!`UwyAt6Za{fN8mncHFh5-LPwMlYVW7fhm>d0y2fah$u*oB-M`@p8`c`FZ+`X(Wc~P1L_nc^H8% zW*_UK{vFTI=d~!Sq&$Sygpre&Oq5&hS+ZfXQ4i-gGke?bt?WYO7Tzemg=hA6Q-Pu~ ziS`~=)@6*Y44Co+w~R2Z>__KTHnTfPe-0~mA^Zo{$g#~x9La4~?m**qEAODNot+i! zr7~41IwYgqhvpsBv$TT>$9M9MHM@B7>@E(n&>tCdLb(%(=QszvX`>3~()3kiH{WvZ zF>*Ijd#ucZ?|F{VQDqNed)Zml8TzwN8Aad)?rD9=lzBDwzPR>Nzks@zFZ#cG&i~|p z<9PZRG2L(EW%ysELPh0nv=8v?u6;(jFtE?+S2DPtbskrap!3fhw(h@Bm0@KD;R9BV z!jUyPx|3NtrSTvuSvY8v(bwqrs@M6|%P1c}^Pth+r9ozl4_SE+HE+-t**6#=bcmmT z31uFM!{xbkgpSA^;rNXVv#QRctbFRIm8anw;h-KJF^<~*AoeE5MfG19x=(okfgHn) z<;rF|#+BOR3_YaGqV#)y3(=4_l0rVu_GvrC_OCom3#Cu<^Y4F)>qX^O zw7+fTak$Q~RR_*+)C3EZV_eyb&Ua`D_q)8nu<|Iv?^$^bj`xl3Ouo-tjiXj2E{xLN z(X*7G>H|ugQEE$m$Zt~VL)t$6H^!+ca%^UcMg|wO=eX-WdAZ`hH-=j& delta 2323 zcmZ{lUzA%_6~@<23*pazL?l{`NCXEk6^Y283}QgSzo`L60Ol1kcc%}Qc9>D zpr*qYYNzKA+ol~!p#eHr(gG=+X-hhgLeq8{TT(Dk;=(mP#lRp z-TRzxe|z5z2WK5RFl*K2^4?INkXfjci8kY%9wU z|Cp8eaGqntg~B<;;hv3v+sYYex{c$**+_kStz&XF+@G+zVN^K>p*dFSn3`kssDq!h z@+^8kMUE?1BI+@+A2S{+&tv%0RwmGQuF(V3&$aSTBtFBHp7X5iM)5o&yAbqprKNlu z>Ccj$^Ns98`FyK#g)gwO10CfAvY%sQU$2$ps4Dj(b|Kdt7cox$BCF!`U(CFXi@B2g zJf(B_C}~OgG6v=vc?s>gR*oU_1uJ=YFJbvAml%~R@yhT&lLFyi( z``y2?YT{948KHZr)zrP52!lyR=>0W~KCb)#(ff>MnYoWsWB4~bVc%k-^{p?ahKb)= zIe?xNcNSATbMSXmrlmZN^zW&OXNl3qD=)D+7sB^jxeguW24vGlv-_4(hpKWCu?Kis z$1_Mk<`y#5;ONc=E~!2U+AVj5)1*4!I{e2mD2&3fGGCReUqQaz14wg~C%- zZbaZ|j?t!a1gS0TEO&|i991qw=o#*bZZ%zAfGy81{;wxs?(?Vq&prSD&40$ZbQ>cU zw;6c_!564dOSui{GSBYWZsZA+w_E*7hIg>e9pxmlFLK!WUZN^hK7@fWqtERzX3UIRX~VmlzNqYGgvcKL0;ZM6k=xsy zTd&d)wO2WQ<5gDGxsR1E?6a~Kfe8-k=7e!vcrsG2aa_1xXXsJoK7?uvH&yGJZ9i9f z-(cu*PDVHz0USY9Ax|V9HNDahxq#s9_D&WSwwo$$`Cw9 z*sA3t95vwv<>)9&$o`F%@Ezp^s>;KN9kcQ}9B&!jnSYD9`kPiIZZv7{|2_BCE&AR=ojEks zqN4MdNMve8<0_^bh)Pm)8vRMyQj*NnfUiOe z=T<9Ph2%BLOq$2A{9gY-V68Ia{^Hl#+ve|iw66>M($_N?cqzP!<4#4NFz?it(X&Ad z@@`brjmeE#debItair*dFhz+tUn&!0TG)=(%}fC})0EU1Nz>O*yM^fJ(zGaeMjSNrgw<)=pOn#Om|U|&2$Z4g+n-( zqr{u+)ngnM4j_=LV}9i-8C`px)|;Ns^c*jRA91`uiMsg${WN+CSx)g5=^iGFm~NtJ z56kc!#VnR!v993Ts|PhL>_lsc*66gAM2^@@XHi?KAHu9Kh>m@V9>P_oAHukB5Dh*S zi@9G(Zts4k78I5?yLI{Wf&pDFe2oW)fc8f%;8lT@RZvXRmUnTsw(X|!^+Ffbmw_v|MS*Xl~o%q z%kpG~QRP_hANBkLaJMGLi%VyuA+CD`#$D<-g)PlnT-%lPSRK%{TlZ& z)sClx{-B?PzQbk-y+_1inn7TXDA$!s7|IX|!fa+J-OBC|C1XZw=UlCSczuG=`{h1i^gPTbubpD;VA{nysur z=r`g8CUciv=q+}X3z#?{6oRLa=`oUpLif>kQ0N)5ikR+Uy2wQQ4xw9EQ#NAo5K}1} zhuLyuV^%-Q8R#fxD?bNY#Z%UHHb##8KXV-wvSCpfLXVT_Cw85t8KGmwkhg^CDN-fI zdjD|~v6Y(tNGYqiK$*}htSg^j=!8%=oaL-}V&$d{ohOB&NGlgGdWzMe+a>fF3FQa$ zo;I&o6->(rR~V(QQfLyZm8@0<&Y0oY-9kHvDpLqnvFbOggd!L|D|8cO)kYJqX8MY* zbEdW(sU`6UNgp7_>Tb8etVYNu`ii&F)R1ZvdXE0fqQkc}iAEw#CPUzg z8PvK`FyuELoz1MuShLVWbY3-!kXFuN^cvGV+}F(_B$P4q21FxP3+vr*i_kE9twMLP o+REDVfi|HBu-_26ji|B>!FHx9Y_>~gw{_>sUyKJI!5_@|2Wzf7I{*Lx diff --git a/sd-card/mp3/0980_admin_lock_intro.mp3 b/sd-card/mp3/0980_admin_lock_intro.mp3 index a44acf11a2580d90f2576c8b9c28f46770f8a5df..07d310e3843df97ba76257bdbe4698dd077da1ef 100644 GIT binary patch delta 964 zcmZ{iOGuPq6o&sZX;x0VE6z|a zJgbZ|DW`(EGzEdma-esgFW4o9hqj5LWbu0**(Y41&WjyID zwVD*ZK+9ge@5nIp0sR?FjG}$YnSH9zwqM=4Jep%f_!6N^BeCW(nJ%FtOP!}>Gu_8{ zwxK&Xd4Q=Kf1~X6GKJyuX)aSf)jX4<{^R^cYCh=KdX^m2KK>St;9RZ|{p5gpdn&wv zaGrK>K2Im1GoR@z(hIcL6T%3Z3ypMPv5;jR`ihv|AaKY?m!}W0^tbh}x^)&a4PmI* zNc7+lRh|_NqrHTsBW~MB*T!tkrLj~eWI^}^U1dhpJms48q;LQ&K~-~97*Xr5(C9^# zsxwom*|ix1EH`$SZr sq24YFHR-RF<;||k&GpJkJyp7DxvSxz9kRUH{=EEvKd_A5i%XBKf7wD_I{*Lx delta 756 zcmW-fJxtR95XEy<1fe1oLD7m-6u}}|k)jl_s0E}BEF^|F;6OrHh)E|F1{)Iw1Nbnp zxl3b24K>;*1gs|F2Yyr&OvH$&!2t&*78Vv_ymR>t|BrX?-n*+zlQ9~qA?L84>C*6= z&<*sG&^)Imp4oPJi;at|Y%eg|o5#1{E1)e;i zpP0^LdW8;`&@Zeh|6pXB&>i^lnLc4Q-(2-<7aB%p0aFyC1wxk*EEF2TqB4fQ9ZVll zxYINzl(*4VB=i6)MXZ_wyM)G3wp-{aW|S|{>1KVh?iQLvY>()4e=*Zr%oPhwqj#@4 zcb1sXM2RVcJVN)eq`ZRueU_NJlr@u7sn9dD?-#m>Rb@8@53nZVEfdWut$dBHgZ90n zoM{&Ea-q8jR2Ua3Od)c}+`7EBV^aAB;Y!w7%auX{h#ofQo+_b7n66?lx(~I_p?lSg#Ykhgd-91N`-@Ezi}9_S<{P+&UYCo*>b{Iz4pSl$Vs_=nslE z;%;PZE!Akdw4bpHSyj$s@GPsE_nd8=R*s=7WNMBkR<(GOO%F7i&O)IMNJV!Dt%O z=Lp`&^e*OvU!%`v=pE#zGkuD2;Z=lhV)`A{rG|dS&{C#TD9>Ozi^&W_@1w)688UBX z+J}U&5&mU{!kAgcltj-hT0`z~rk{{pt~Z7OrZZ(rh{;;Fyz6+3S*u92a(&D zEU;yrvSuw{IE#tk$=1-6Fddz@8`_ENJM{h$;dcnE)J(H0jnvzFr&gMG7t@~@6J9`@ z!$@@t4yNxha5vMBaIZ3S8mU!GEr{O3Q~`UAb`Z}oGzecV(|ec}UO?<#rVrp)t>;Hq zGrf-BecH{O@GAQ5U(_j&VSU6hm8?Dp6jgnoL$1QNClFX3nfuJrlx@;ooj(6f@h^vpxPr zm1;DetkFtBwW{j8uowMxOs~RKZzTUjy=oE(=yYs5jN}~Np_x4m>Xs?t8FW5s#LL-@ zI@u9nI|4zyWj1J}i{8g{f_aat2gZcw(blBuEi~zwfo7%@+#w?!r9!$l(I>PEdy9H6 z-eTxC_@2~ln-+>nPigOtR(0cOt7Z4wb-e?{L;Lw_LuY0Wt<+>P)vOl7dP8}ZLj zJBwE3VJ608*w6$zp4FV0&#CeW;YIj&8S%-?E~byrvs?X{8&Um}5kr3=^t@IwFFc3- z7t|rHJ=){M9(8+Uuhw9T8ri1dsP2?!pSo*G7)R%R-K^{vb+RMEBM2N|GMGJJ=neFC z=$X8O$}wRQZHII(77j6;z`#pP$KgI~Kr}7^d{_`IJ{e;@Ht+Ow9;(ahg5y)i;Bn*ekf)l@ap>e2v~JU(;oq#gedCEKB4k&*?=9B)s_4 z28ye^)pf14)pfPrCX1`6q`*~Hn(@E4!Me(NOTN=tRIrFT`?dECzCew)c`-p_gLjL+ pRsUMnIScDuMNWBg+Bc_P?`Xj1^;?{U#br*nv-CRUc3=D0@*l&2M5O=# delta 1264 zcmW-gZ*0?b5XN(rIbao6fht;&iB@okR-_=ESd;?NDMhVfolFac^-*5LjS-qm+4Oo%@z6(fd--D7;j+u3~8rTa9t+Ug@UpT;dx9Q zn4Bkc7TL?Kho(lM-%)C`7h^7=e=(!{5(8HVb;CEGX$#8p*>Rx`$pu2QQNQqr!#xYR zg+g->bX#ab*@(=QOnczI%HAJQ{)osTt2DKUwcp;WtyAwcLem&kmJs)_c31NV{fxnD zg?@v7G1JGWEEZ}<>N=r~a4xY4@=KVGA=D&PKvh{n&-Fr|!?V=#i%W&xLG%WjW?Fe3 zeK%gz$t(KrnDTujZW8K;V;So!L(5F>z;dCZ7+=o%ZTe;*fvZ_4j)L+4!Yi1DF}cFJ z%HCoYm{1-= zX0`R~_6xm%5oH9CHMWhZHLPFtuC;A=@3j0;snMZ2tL^nJbuuOjo(*P46(ScT#S%Zc7&E|^wU(RVNFcA7VeI%Q0G4vCOZ z8IDa%SqyCwYDHkPX)?Zy3favu^;3kBh5XZ6oeyQo%ygwRJAPcZ$8 z^fOk|^{gphP@YD3H>*!3cMBDf-DCc2N}B$qB-0FH&sirk$`csaYYy>s*o@^4b9?f6 z>!CiydQG{M-Bj=ebJv7&7@2)`S?(8Yvm?sa5ZN!3!_KW#crWph#r)> R1<6xqj)Hn#$4>_v{s$sWQtbY1Si;mD}(Vd#1$w5I<|sT#{`*56{loGE8qk>VIqaW z7*Cmud#HoKuzVEQ_!v{FvS2B?tRZa z_qoqKZTibvQ88%Wbh6ZB8!D>lWZH-_V%iLwmFXKqt%f?`9>jDKJ;I|14rYqOKE%-1 zXc@xP0pC4L?dTK!g6brVJBBia(JovK|Gi8rU>#X{TZHdtIs)ftL*?ig&2$T;4=9ty z7`lXpF)YkYVfqf;!d8?&XoM*b=`Naud*L0+o^twYOro#UIp z)Pp`@9Mw;#8jf_Pe6$O_@IT4a2uFEWwo9C9WpyCxYrkHAw*HAr?a9Y#!e1D*^+ zKchE8b*r4L`O~H_)gnH{&{h;b&D4Z`VI#tsOb6g}>WUpsJ=xN!Ec;E$GEzxH7SmPa zPSg3_!fPm>Ze*2|Y}Kb(xC`DHOi|pPq4PsC^*wzSQw(jwdX&sIQqRC_bwXVZ(@td1 zF~ZI{OdC-4j8+JS0c9`hindj% ze{_|hO1M|6Vm-nt1Yc6k?Q0S$tx@;-N|;WePk0E`FB`fH$16-P-uuo!1V+3)^xx?J9n0M?0m*?)@?9vP>ZQM{3bYBEP_j|aVql}j z>oz5PwOQ-z+^m|Hy{DDf%Jod6*HK z^V<5`ghx@*q!t-y(rZ)KoRH~)kt5l?a~FM|WU<67 z7RzAy$y1-8K*EVPA*66Eca^{r6Y1x_bq^_Me^lnqVZkNZBIVEeBGkfZ^yOdjT=VQy? DbS`d8 delta 1891 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 diff --git a/sd-card/mp3/0984_admin_lock_calc.mp3 b/sd-card/mp3/0984_admin_lock_calc.mp3 index 3e739ea37c1a32623b146474f4efc1a490601168..e17b8a7f042313bd176d786a588c7bf7bd69060a 100644 GIT binary patch delta 1390 zcmZ{jUrd{26vlfhSR8c@=g?`L;1t=)um#50z}apq$jYB@NR{D~s#Fod%2xlRQDF%t z!$bieG{So%Q`A^98--|?f11o1!b~vAg%TrPaKjbRa6@9a@SJ8>Ci{~9^5yjX&ig#i zd)|@1>92oiWIz2mT4|_nbdl+OM2YEhIPFZ|AYnH&hrl&VKVU`p2l}&^eupdD&`+4n zW?|@BrU|SI=P>LrbP?_xrVE%Cjw5m%(`DGV82S>ETbRB=buQB=mU0b!hnSP;d*ofu z^fi*gcM;rb=u@n2Wm?4G4cfDC8&e9YZALV-=P~_;4dDfh-N^JByxR?($HI19*?kjJ z5{@0JZE6S8$MEGdO=DU30pd3sdKvk*Fj<%p*1+1yl!djOh6*rrD^oFwZqrIyXhYa# zCG#P{ot`IVaajBpOt0X?0y13C#qRqDOypn6FQFCl!$NWzt&PO3q7 zY;V-WHiQ>3)})zun~lu;LUU%8ET&K3IAQ1wOr2m!)z_jumxX#Vttyf2wzQt0+ z$c2nO#S})~(@YIW3g1Go%h20c?b7Qy_zY7I3cGa_Qr(#=_pEl?5T-HKqxANw#D!k% z-2EKW7#vX}12h%YuYAvIWm)(p;(bOW=ATxfGs3g5&M>`-wKIld7>Y6VqUZ&^kZEBB z!Y`U^>WS9|KrapVhx}Hc)n6BA`^90iC2clami**7pP@j?nWxbzuk+V8bu`vDHTv6Z zUhlz@GEZ6V|Nad%9c#9^J)W|X44Rx*{%;9b4gQmv2CXgrqrnb+we9m*rDYW!c?vp@ bR=^0Dnd%AOf; delta 1183 zcmW-gUu=_Q6vlI^v;3*Jid4~xRCErDNI?qHiACv9ic*y7tW%~^opo4rMMGmUV=AGE zrW?;Nj&p33sj)^IacJrin_!g;aZ6~TnI^-eG1=9k+0AZt;W@kc<;%(Yo%ea3bKcAU zQUAZxznT6S2r?ZVs0)3DGzonNmqX|$lpIVGh+HA`D;mlM`mPlE8{Q_SIx0<~j9(>m z8nem?6r4=w;9n$k7Gug!kX$Tu363R9KcKio=tqQ>3Jqa$Dbvr$xP*Rz`)Z*o%E}_5 z%b32w^fIBpkh{iu`kIB#quR`>A>kIfj5+053|=dA41wiLUtoN>olIRP^gWy_Oxy4Z zp|2742#sP&Swi-Drnlj_L8t>G%AJU<6mnr^B~vT%HwtY)%S~3PDHoy3%hZf{uh5qm zy4jotSFtLouM+i`zC~ywT&pdO(rVUZB0iyhG?d?>?^dBR@UCI{1C=%Q9luSe2eZnp zDBRBUKKw00d5kGPM)D5J+|kPPJBqEMjzVjNKEmW$)=V;Y+PB+pXUfV;h^}LGH@!|M zhumF4$KhLV3ajgx-b3PUp#jV(Kg8e$p~DCSSZ^N>SU#zZqO)*rGB3lMtY`QhJ2|E7 zM|QK_=m}cpBgzTHw%BxLw%8`*x0?5sd(BHtc>!J9SQDP#CiE4CLY8^3&0N>p?BDc# zM%Q-RpVD^L?TXxQ?i$K7=nGrjyMwiVWrxs3#2*m)6tl`v6dttB{+&XfV{C!Rh~?mD zXPthrUFbK2cG)IO?qba(^N{IuKWs|M$_t3@X5F{x-R311HK)EkHl6AoyD{;IB{ru# zhrvfJlfYiqogd%3uuCzaGMxLEPGESSXsY4Itml;SD6)^6HqU-LIifs|SlqPD#9902 zpRh{HleXJ6?q#72d?{OnYHH!hy<*+wlov7BV+3V#n;&&18}lcv5t>aKP43Jvx@dxKc@Y_ByokcoMi;qh(?${6*g_YI zpz^vf&POP1n!uqSNC}!2VniWSs|af0Lfy1!NVzhP%l{G z%q2fiBIeslOye0JGzeCCWcY(Cmj$~_HfrUB_W^IQ)ay_14qJ1iuRi?P! zzQEVzc8BbFXWw~N~ThsDZ3|f#*P$v z2UUk{r#q`CL{I&Y>kT;fs|$vQoO`)Z|EO--U4zL~#coTxu9H>vh|hA^Q0CI z3MnSkcCbQ|0|$Q&yEstFWhn<2zxO$O4$srm^L_q?js|mm_{sMRviT=PnR8H-7_t;a z9$11rM?8u;-;G5sdlhGNY{3dIY{eDFd}7^tSobMzxak+Xu%}V+#XI9Xr<)X)v;u-h z?gSKv90)3&`5F}5b0MUtFxsp*=dp2|qhY}|Kf?;;T8kSf9dW1Sh+v-+QN?S%CIi4D;%qA79{7$;htamDA j+1;g>V#S!@q$9RtReU8DoHWy^MLW}AiiOk$PaN+*^zD2X diff --git a/sd-card/mp3/0992_admin_calc.mp3 b/sd-card/mp3/0992_admin_calc.mp3 index a73c466869f2cb421efcfe45bd357127c45ce893..fc09db0eef961738d3db0b9ce3e563a959488546 100644 GIT binary patch delta 388 zcmZ{eKS;w+5XRrd*48H0c6U(3sih?LpW^h?P)bZ1N(hh#yN~;txt(q2;k8q6LGL^h+~XDyoTDNUbn!xR z!9#t7poi~*eQbq%&t%wz7rY7kllmyZ8GZ})@gU;D38tb1JNO{j#?3K;W0Ybp9AP&` z;NYT6aEWiSD{{6Kf=5h_6I`Jum_sA(NWEYDj1yer{sh4uPA3RVyh}LJ#}BS24_n;U zYrLd4o+6BSjIod?q0@i@VjRe{vn5`(nr7KDd4p+MF|Exn$p5x=Yo)?cs+vm&u#~~R us@to4EeNPp`LfaUTVc#p)PgEBwXqZ(X0>&0Ff}t*RI|DKkau?n$LtSRFm0Ux delta 180 zcmWm6s}F%;9L4dS$LspKcEwDRDJn@;Y@)2_ifS%LdY|7+VIwHU3=}*q?WG91XH>*+Vo&^`yAXqY}1T#*Q#*PJ-V9&ep#D?|X#N8SXuH64p@(33E z8c%F{H6~2^1T7woLstEQF;zfg#6dtXWzH6?d9}6qsRso&#zKM(XU05>VMon9e1`=G UHY0)_6H!5hyQrgH7R#~z4+elo{r~^~ diff --git a/sd-card/mp3/0993_admin_calc.mp3 b/sd-card/mp3/0993_admin_calc.mp3 index a9abf9721c7670987d908ea7f2b9b5a766b1f97b..bd770edb45eddcc623fb3c9765cd4fca8167e855 100644 GIT binary patch delta 340 zcmaDYu~TY-DdUfcX0sVxCSIDu7&$qgaSr3L$vjMR7%xp;$uygB)nr%ZIgE=YKLV0V zC)cygVO%>|hIKaMhRHivK_a1Sa~QWw{sbh?P3~u(&3J9HHpd*sN0U#0$yClcjGHDi zam{Azoji|g4r9nF4Vh!C+u!s$*zj!i^>y>gVFmplx7ar~@Yac%bq@t^q!d zkzfNtf*hT_<3UUYT?0LTLt|3|0|SK=Ck|Xn16&=w84UD{EliD#4RJWP0jik+0M+qW A{r~^~ delta 132 zcmdlf^;%+rDdV4sX0sVRCSIDu7&|$iaSr3D$vjMR7_UuU$uygB-DFqhIgHCDKLU~~ zC)cygVca-bhIKaMmdQI9LA-SL)k#&Cm^|KazFcQpe}8WIgD#3p8%5ACZ}@FVcb8N ziEB3FvB~qe<^V}+?m3K;Cg0$m&6qj4gl7)po5_N_Q|cW9Ttgh)T_Y~AGcc@UU|?VY zVjx%mCV>P{7z}&^jNKhQ{o;K+{d^rG7z~Y!bqp=cxY1-o{apMRv<(c5b-;ul4^%$L zHNeL)5^O+7kfXDAJctQYsON8HVQOGtppfFkflFzCtD`rAfu51Mv9XZ_4(B#NH8TJJ Dd`w!P delta 136 zcmWm5KMuiQ90uULrT(-vnDqb>GmF^~+<@7_EKZ3m+rpheWGI{2WKa|l6fp}4s?89%GQv%p7M(M=3OsOs9L_oSJKw$e!Qbh7UG#G< zX(>NfWSYkmF|8vSVtRqqkfoRK4=@!_5^f?_!SoTaN=sYVsAT$v>_Mhq*cE=pY}nEo z+N(7GAY8-sL#h_4w)7g0s#WJ=MC)H8mY(2dR4a9dS+*2}MWkvhEn&Nc=@n*ZbxeKuBP`+mQKkoQ>#cbHRL``48^@U5Bivx=ITjmK?NTGl-XFrdxOLpp z8#JEK?azdapo!@zcABg>ymL}LoIl01jJLuSOvLmpe`8vCcv_ikR?o#|raY$3=wC(S zOfy)GTXNvH=*E(89l2Ijh_zYiZnQDoM)oYzBzA@0F?$Xd?&iQFF%EN$q3 zNYg|U#3Pa#I*3RZkVu3?IoAH zP2rr;D!~a~G_RPf7L;k(6`q;53tkzo5&W>CdCyd>LV*Ky=6s`B;B>udvpE#5x#ciD zBTkb)I28^#=Q4@6LG(&a^O0k2g?+xe1(#fC6qFe97?Yw$VUd$wL5#neA6#n^Y;(}3 zv~$TP$Z)n Date: Tue, 8 Mar 2022 18:31:07 +0100 Subject: [PATCH 24/32] New Feature: repeat last card * this can be used for ShortCuts as well as for cards * fix bug not storing --- src/chip_card.hpp | 1 + src/mp3.hpp | 1 + src/state_machine.cpp | 72 +++++++++++++++++++++++++------------------ src/state_machine.hpp | 2 ++ 4 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/chip_card.hpp b/src/chip_card.hpp index 297263f8..2c71c64d 100644 --- a/src/chip_card.hpp +++ b/src/chip_card.hpp @@ -21,6 +21,7 @@ enum class mode_t: uint8_t { album_vb = 8, party_vb = 9, hoerbuch_1 = 10, + repeat_last = 11, // modifier modes sleep_timer = 1, diff --git a/src/mp3.hpp b/src/mp3.hpp index f1b8b9bb..39bdc1e6 100644 --- a/src/mp3.hpp +++ b/src/mp3.hpp @@ -27,6 +27,7 @@ enum class mp3Tracks: uint16_t { t_318_special_album = 318, t_319_special_party = 319, t_320_mode_audio_book_single = 320, + t_321_mode_repeat_last_card = 321, t_327_select_file = 327, t_328_select_first_file = 328, t_329_select_last_file = 329, diff --git a/src/state_machine.cpp b/src/state_machine.cpp index cf8f5a10..aab44923 100644 --- a/src/state_machine.cpp +++ b/src/state_machine.cpp @@ -16,6 +16,7 @@ const __FlashStringHelper* str_ChTrack () { return F("ChTrack") const __FlashStringHelper* str_ChFirstTrack () { return F("ChFirstTrack") ; } const __FlashStringHelper* str_ChLastTrack () { return F("ChLastTrack") ; } const __FlashStringHelper* str_WriteCard () { return F("WriteCard") ; } +const __FlashStringHelper* str_Base () { return F("Base") ; } const __FlashStringHelper* str_Idle () { return F("Idle") ; } const __FlashStringHelper* str_StartPlay () { return F("StartPlay") ; } const __FlashStringHelper* str_Play () { return F("Play") ; } @@ -357,7 +358,7 @@ void ChMode::entry() { folder = folderSettings{}; - numberOfOptions = 10; + numberOfOptions = 11; startMessage = mp3Tracks::t_310_select_mode; messageOffset = mp3Tracks::t_310_select_mode; preview = false; @@ -384,7 +385,13 @@ void ChMode::react(button_e const &b) { transit(); return; } - transit(); + if (folder.mode == mode_t::repeat_last) { + folder.folder = 0xff; // dummy value > 0 to make readCard() returning true + transit(); + } + else { + transit(); + } return; } }; @@ -577,7 +584,7 @@ bool Base::readCard() { if (lastCardRead.nfcFolderSettings.folder == 0) { if (lastCardRead.nfcFolderSettings.mode == mode_t::admin_card) { - LOG(state_log, s_debug, F("Base"), str_to(), str_Admin_Entry()); + LOG(state_log, s_debug, str_Base(), str_to(), str_Admin_Entry()); Admin_Entry::lastCurrentValue = 0; transit(); return false; @@ -597,6 +604,26 @@ bool Base::readCard() { return false; } +void Base::handleShortcut(uint8_t shortCut) { + if (shortCut < 3 && settings.shortCuts[shortCut].folder != 0) { + if (settings.shortCuts[shortCut].mode != mode_t::repeat_last) + tonuino.setFolder(&settings.shortCuts[shortCut]); + if (tonuino.getCard().nfcFolderSettings.folder != 0) { + LOG(state_log, s_debug, str_Base(), str_to(), str_StartPlay()); + transit(); + } + } +} + +void Base::handleReadCard() { + if (lastCardRead.nfcFolderSettings.mode != mode_t::repeat_last) + tonuino.setCard(lastCardRead); + if (tonuino.getCard().nfcFolderSettings.folder != 0) { + LOG(state_log, s_debug, str_Base(), str_to(), str_StartPlay()); + transit(); + } +} + // ####################################################### void Idle::entry() { @@ -650,14 +677,10 @@ void Idle::react(button_e const &b) { break; } - if (shortCut <= 3 && settings.shortCuts[shortCut].folder != 0) { - tonuino.setFolder(&settings.shortCuts[shortCut]); - LOG(state_log, s_debug, str_Idle(), str_to(), str_StartPlay()); - transit(); - } - else if (shortCut == 3) { + if (shortCut == 3) mp3.enqueueMp3FolderTrack(mp3Tracks::t_262_pling); - } + + handleShortcut(shortCut); }; void Idle::react(card_e const &c) { @@ -666,11 +689,8 @@ void Idle::react(card_e const &c) { } switch (c.c) { case cardEvent::inserted: - if (readCard()) { - tonuino.setCard(lastCardRead); - LOG(state_log, s_debug, str_Idle(), str_to(), str_StartPlay()); - transit(); - } + if (readCard()) + handleReadCard(); return; case cardEvent::removed: break; @@ -747,11 +767,8 @@ void Play::react(card_e const &c) { } switch (c.c) { case cardEvent::inserted: - if (readCard()) { - tonuino.setCard(lastCardRead); - LOG(state_log, s_debug, str_Play(), str_to(), str_StartPlay()); - transit(); - } + if (readCard()) + handleReadCard(); return; case cardEvent::removed: if (settings.pauseWhenCardRemoved && not tonuino.getActiveModifier().handlePause()) { @@ -820,11 +837,7 @@ void Pause::react(button_e const &b) { break; } - if (shortCut < 3 && settings.shortCuts[shortCut].folder != 0) { - tonuino.setFolder(&settings.shortCuts[shortCut]); - LOG(state_log, s_debug, str_Idle(), str_to(), str_StartPlay()); - transit(); - } + handleShortcut(shortCut); }; void Pause::react(card_e const &c) { @@ -838,9 +851,7 @@ void Pause::react(card_e const &c) { transit(); return; } - tonuino.setCard(lastCardRead); - LOG(state_log, s_debug, str_Pause(), str_to(), str_StartPlay()); - transit(); + handleReadCard(); } return; case cardEvent::removed: @@ -858,9 +869,9 @@ void StartPlay::entry() { }; void StartPlay::react(button_e const &/*b*/) { - LOG(state_log, s_debug, str_StartPlay(), str_to(), str_Play()); if (not mp3.isPlayingMp3()) { tonuino.playFolder(); + LOG(state_log, s_debug, str_StartPlay(), str_to(), str_Play()); transit(); } }; @@ -1340,7 +1351,8 @@ void Admin_ShortCut::react(button_e const &b) { } break; case end_setupCard: - settings.shortCuts[shortcut] = SM_setupCard::folder; + settings.shortCuts[shortcut-1] = SM_setupCard::folder; + settings.writeSettingsToFlash(); mp3.enqueueMp3FolderTrack(mp3Tracks::t_402_ok_settings); LOG(state_log, s_debug, str_Admin_ShortCut(), str_to(), str_Idle()); transit(); diff --git a/src/state_machine.hpp b/src/state_machine.hpp index ca711134..6a64d5bc 100644 --- a/src/state_machine.hpp +++ b/src/state_machine.hpp @@ -80,6 +80,8 @@ class Base: public SM_tonuino { protected: bool readCard(); + void handleShortcut(uint8_t shortCut); + void handleReadCard(); static nfcTagObject lastCardRead; }; From f1d019c65aaa023f071dfcb4c3e96077f22e8f0a Mon Sep 17 00:00:00 2001 From: boerge1 Date: Tue, 8 Mar 2022 18:51:13 +0100 Subject: [PATCH 25/32] Bug Fix: possibility to choose 99 folder, not only 10 --- src/state_machine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state_machine.cpp b/src/state_machine.cpp index aab44923..ecc0672e 100644 --- a/src/state_machine.cpp +++ b/src/state_machine.cpp @@ -401,7 +401,7 @@ void ChMode::react(button_e const &b) { void ChFolder::entry() { LOG(state_log, s_info, str_enter(), str_ChFolder()); - numberOfOptions = 10; + numberOfOptions = 99; startMessage = mp3Tracks::t_301_select_folder; messageOffset = mp3Tracks::t_0; preview = true; From 541e7ed53caa2e597b3557cfd6172ac89f4c2250 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Tue, 8 Mar 2022 19:25:28 +0100 Subject: [PATCH 26/32] Improvements for program size --- src/state_machine.cpp | 112 +++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 57 deletions(-) diff --git a/src/state_machine.cpp b/src/state_machine.cpp index ecc0672e..5aec9313 100644 --- a/src/state_machine.cpp +++ b/src/state_machine.cpp @@ -21,6 +21,8 @@ const __FlashStringHelper* str_Idle () { return F("Idle") ; } const __FlashStringHelper* str_StartPlay () { return F("StartPlay") ; } const __FlashStringHelper* str_Play () { return F("Play") ; } const __FlashStringHelper* str_Pause () { return F("Pause") ; } +const __FlashStringHelper* str_Admin_BaseSetting () { return F("AdmBaseSetting") ; } +const __FlashStringHelper* str_Admin_BaseWriteCard () { return F("AdmBaseWriteCard") ; } const __FlashStringHelper* str_Admin_Allow () { return F("AdmAllow") ; } const __FlashStringHelper* str_Admin_Entry () { return F("AdmEntry") ; } const __FlashStringHelper* str_Admin_NewCard () { return F("AdmNewCard") ; } @@ -115,6 +117,17 @@ class WriteCard : public SM_writeCard subState current_subState; }; +class Admin_BaseSetting: public VoiceMenu_tonuino +{ +protected: + void saveAndTransit(); +}; + +class Amin_BaseWriteCard: public VoiceMenu_tonuino { +protected: + bool handleWriteCard(button_e const &b); +}; + class Admin_Allow: public VoiceMenu_tonuino { public: @@ -148,7 +161,7 @@ class Admin_Entry: public VoiceMenu_tonuino static uint8_t lastCurrentValue ; }; -class Admin_NewCard: public SM_tonuino +class Admin_NewCard: public Amin_BaseWriteCard { public: void entry() final; @@ -160,12 +173,11 @@ class Admin_NewCard: public SM_tonuino end_setupCard, start_writeCard, run_writeCard, - end_writeCard, }; subState current_subState; }; -class Admin_SimpleSetting: public VoiceMenu_tonuino +class Admin_SimpleSetting: public Admin_BaseSetting { public: void entry() final; @@ -179,7 +191,7 @@ class Admin_SimpleSetting: public VoiceMenu_tonuino static Type type; }; -class Admin_ModCard: public VoiceMenu_tonuino +class Admin_ModCard: public Amin_BaseWriteCard { public: void entry() final; @@ -189,13 +201,12 @@ class Admin_ModCard: public VoiceMenu_tonuino enum subState: uint8_t { start_writeCard, run_writeCard, - end_writeCard, }; subState current_subState; bool readyToWrite; }; -class Admin_ShortCut: public VoiceMenu_tonuino +class Admin_ShortCut: public Admin_BaseSetting { public: void entry() final; @@ -210,14 +221,14 @@ class Admin_ShortCut: public VoiceMenu_tonuino uint8_t shortcut; }; -class Admin_StandbyTimer: public VoiceMenu_tonuino +class Admin_StandbyTimer: public Admin_BaseSetting { public: void entry() final; void react(button_e const &) final; }; -class Admin_CardsForFolder: public VoiceMenu_tonuino +class Admin_CardsForFolder: public Amin_BaseWriteCard { public: void entry() final; @@ -239,7 +250,7 @@ class Admin_CardsForFolder: public VoiceMenu_tonuino uint8_t special2; }; -class Admin_InvButtons: public VoiceMenu_tonuino +class Admin_InvButtons: public Admin_BaseSetting { public: void entry() final; @@ -253,7 +264,7 @@ class Admin_ResetEeprom: public SM_tonuino void react(button_e const &) final; }; -class Admin_LockAdmin: public VoiceMenu_tonuino +class Admin_LockAdmin: public Admin_BaseSetting { public: void entry() final; @@ -269,7 +280,7 @@ class Admin_LockAdmin: public VoiceMenu_tonuino uint8_t pin_number; }; -class Admin_PauseIfCardRemoved: public VoiceMenu_tonuino +class Admin_PauseIfCardRemoved: public Admin_BaseSetting { public: void entry() final; @@ -876,6 +887,31 @@ void StartPlay::react(button_e const &/*b*/) { } }; +// ####################################################### + +void Admin_BaseSetting::saveAndTransit() { + settings.writeSettingsToFlash(); + mp3.enqueueMp3FolderTrack(mp3Tracks::t_402_ok_settings); + LOG(state_log, s_debug, str_Admin_BaseSetting(), str_to(), F("Idle or AdmEntry")); + transit(); +} + +bool Amin_BaseWriteCard::handleWriteCard(button_e const &b) { + SM_writeCard::dispatch(b); + if (SM_writeCard::is_in_state()) { + LOG(state_log, s_debug, str_Admin_BaseWriteCard(), str_to(), F("Idle or AdmEntry")); + transit(); + return true; + } + else if (SM_writeCard::is_in_state()) { + LOG(state_log, s_info, str_Admin_BaseWriteCard(), str_abort()); + transit(); + return true; + } + return false; +} + + // ####################################################### void Admin_Allow::entry() { @@ -1137,19 +1173,9 @@ void Admin_NewCard::react(button_e const &b) { current_subState = run_writeCard; break; case run_writeCard: - SM_writeCard::dispatch(b); - if (SM_writeCard::is_in_state()) - current_subState = end_writeCard; - if (SM_writeCard::is_in_state()) { - LOG(state_log, s_info, str_Admin_NewCard(), str_abort()); - transit(); + if (handleWriteCard(b)) return; - } break; - case end_writeCard: - LOG(state_log, s_debug, str_Admin_NewCard(), str_to(), str_Idle()); - transit(); - return; default: break; } @@ -1201,10 +1227,7 @@ void Admin_SimpleSetting::react(button_e const &b) { mp3.setEq(static_cast(settings.eq - 1)) ; break; } - settings.writeSettingsToFlash(); - mp3.enqueueMp3FolderTrack(mp3Tracks::t_402_ok_settings); - LOG(state_log, s_debug, str_Admin_SimpleSetting(), str_to(), str_Idle()); - transit(); + saveAndTransit(); return; } }; @@ -1263,19 +1286,9 @@ void Admin_ModCard::react(button_e const &b) { current_subState = run_writeCard; break; case run_writeCard: - SM_writeCard::dispatch(b); - if (SM_writeCard::is_in_state()) - current_subState = end_writeCard; - if (SM_writeCard::is_in_state()) { - LOG(state_log, s_info, str_Admin_ModCard(), str_abort()); - transit(); + if (handleWriteCard(b)) return; - } break; - case end_writeCard: - LOG(state_log, s_debug, str_Admin_ModCard(), str_to(), str_Idle()); - transit(); - return; default: break; } @@ -1352,10 +1365,7 @@ void Admin_ShortCut::react(button_e const &b) { break; case end_setupCard: settings.shortCuts[shortcut-1] = SM_setupCard::folder; - settings.writeSettingsToFlash(); - mp3.enqueueMp3FolderTrack(mp3Tracks::t_402_ok_settings); - LOG(state_log, s_debug, str_Admin_ShortCut(), str_to(), str_Idle()); - transit(); + saveAndTransit(); return; default: break; @@ -1395,10 +1405,7 @@ void Admin_StandbyTimer::react(button_e const &b) { case 4: settings.standbyTimer = 60; break; case 5: settings.standbyTimer = 0; break; } - settings.writeSettingsToFlash(); - mp3.enqueueMp3FolderTrack(mp3Tracks::t_402_ok_settings); - LOG(state_log, s_debug, str_Admin_StandbyTimer(), str_to(), str_Idle()); - transit(); + saveAndTransit(); return; } }; @@ -1542,10 +1549,7 @@ void Admin_InvButtons::react(button_e const &b) { case 1: settings.invertVolumeButtons = false; break; case 2: settings.invertVolumeButtons = true ; break; } - settings.writeSettingsToFlash(); - mp3.enqueueMp3FolderTrack(mp3Tracks::t_402_ok_settings); - LOG(state_log, s_debug, str_Admin_InvButtons(), str_to(), str_Idle()); - transit(); + saveAndTransit(); return; } }; @@ -1616,10 +1620,7 @@ void Admin_LockAdmin::react(button_e const &b) { break; } case finished: - settings.writeSettingsToFlash(); - mp3.enqueueMp3FolderTrack(mp3Tracks::t_402_ok_settings); - LOG(state_log, s_debug, str_Admin_LockAdmin(), str_to(), str_Idle()); - transit(); + saveAndTransit(); return; } }; @@ -1652,10 +1653,7 @@ void Admin_PauseIfCardRemoved::react(button_e const &b) { case 1: settings.pauseWhenCardRemoved = false; break; case 2: settings.pauseWhenCardRemoved = true ; break; } - settings.writeSettingsToFlash(); - mp3.enqueueMp3FolderTrack(mp3Tracks::t_402_ok_settings); - LOG(state_log, s_debug, str_Admin_PauseIfCardRemoved(), str_to(), str_Idle()); - transit(); + saveAndTransit(); return; } }; From f4bcff36a57ec94c1976617f9f9ecd18c126925b Mon Sep 17 00:00:00 2001 From: boerge1 Date: Wed, 16 Mar 2022 18:41:53 +0100 Subject: [PATCH 27/32] Delete Shortcut + play after start if card is on --- src/chip_card.cpp | 2 +- src/chip_card.hpp | 5 +++-- src/state_machine.cpp | 3 +++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/chip_card.cpp b/src/chip_card.cpp index 62825390..dfd7b194 100644 --- a/src/chip_card.cpp +++ b/src/chip_card.cpp @@ -262,8 +262,8 @@ cardEvent Chip_card::getCardEvent() { if(result != mfrc522.STATUS_OK) { ++cardRemovedSwitch; } else { - cardRemovedSwitch.reset(); mfrc522.PICC_ReadCardSerial(); + cardRemovedSwitch.reset(); } if (cardRemovedSwitch.on()) { diff --git a/src/chip_card.hpp b/src/chip_card.hpp index 2c71c64d..b9f518ea 100644 --- a/src/chip_card.hpp +++ b/src/chip_card.hpp @@ -68,6 +68,7 @@ class delayedSwitchOn { public: delayedSwitchOn(uint8_t delay) : delaySteps(delay) + , counter(delay) {} delayedSwitchOn& operator++() { if (counter < delaySteps) ++counter; return *this; } void reset() { counter = 0; } @@ -75,7 +76,7 @@ class delayedSwitchOn { private: const uint8_t delaySteps; - uint8_t counter = 0; + uint8_t counter; }; class Chip_card { @@ -99,7 +100,7 @@ class Chip_card { Buttons &buttons; delayedSwitchOn cardRemovedSwitch; - bool cardRemoved = false; + bool cardRemoved = true; }; diff --git a/src/state_machine.cpp b/src/state_machine.cpp index 5aec9313..ec78800a 100644 --- a/src/state_machine.cpp +++ b/src/state_machine.cpp @@ -1350,6 +1350,9 @@ void Admin_ShortCut::react(button_e const &b) { else { switch (current_subState) { case start_setupCard: + settings.shortCuts[shortcut-1].folder = 0; + settings.shortCuts[shortcut-1].mode = mode_t::none; + settings.writeSettingsToFlash(); SM_setupCard::start(); current_subState = run_setupCard; break; From fa05e70019a8fd7f78aaa382b3a40b25fd432478 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Sat, 9 Apr 2022 15:50:17 +0200 Subject: [PATCH 28/32] Command sources * framework for additional command sources * implement serial input as additional command source --- src/buttons.cpp | 70 ++------ src/buttons.hpp | 40 +---- src/chip_card.cpp | 4 +- src/chip_card.hpp | 4 +- src/commands.cpp | 59 +++++++ src/commands.hpp | 57 ++++++ src/constants.hpp | 2 +- src/modifier.cpp | 2 +- src/serial_input.cpp | 28 +++ src/serial_input.hpp | 16 ++ src/state_machine.cpp | 390 +++++++++++++++++++++--------------------- src/state_machine.hpp | 36 ++-- src/tonuino.cpp | 4 +- src/tonuino.hpp | 10 +- 14 files changed, 406 insertions(+), 316 deletions(-) create mode 100644 src/commands.cpp create mode 100644 src/commands.hpp create mode 100644 src/serial_input.cpp create mode 100644 src/serial_input.hpp diff --git a/src/buttons.cpp b/src/buttons.cpp index c373a94b..2eb3a52f 100644 --- a/src/buttons.cpp +++ b/src/buttons.cpp @@ -7,16 +7,16 @@ namespace { constexpr bool buttonPinIsActiveLow = (buttonPinType == levelType::activeLow); } -Buttons::Buttons(const Settings& settings) +Buttons::Buttons() +: CommandSource() // pin dbTime puEnable invert -: buttonPause(buttonPausePin, buttonDbTime, buttonPinIsActiveLow, buttonPinIsActiveLow) +, buttonPause(buttonPausePin, buttonDbTime, buttonPinIsActiveLow, buttonPinIsActiveLow) , buttonUp (buttonUpPin , buttonDbTime, buttonPinIsActiveLow, buttonPinIsActiveLow) , buttonDown (buttonDownPin , buttonDbTime, buttonPinIsActiveLow, buttonPinIsActiveLow) #ifdef FIVEBUTTONS , buttonFour (buttonFourPin , buttonDbTime, buttonPinIsActiveLow, buttonPinIsActiveLow) , buttonFive (buttonFivePin , buttonDbTime, buttonPinIsActiveLow, buttonPinIsActiveLow) #endif -, settings(settings) { buttonPause.begin(); buttonUp .begin(); @@ -27,8 +27,8 @@ Buttons::Buttons(const Settings& settings) #endif } -buttonRaw Buttons::getButtonRaw() { - buttonRaw ret = buttonRaw::none; +commandRaw Buttons::getCommandRaw() { + commandRaw ret = commandRaw::none; readButtons(); if (( buttonPause.pressedFor(buttonLongPress) || buttonUp .pressedFor(buttonLongPress) @@ -37,103 +37,63 @@ buttonRaw Buttons::getButtonRaw() { && buttonPause.isPressed() && buttonUp .isPressed() && buttonDown .isPressed()) { - ret = buttonRaw::allLong; + ret = commandRaw::allLong; } else if (buttonPause.wasReleased()) { if (not ignorePauseButton) - ret = buttonRaw::pause; + ret = commandRaw::pause; else ignorePauseButton = false; } else if (buttonPause.pressedFor(buttonLongPress) && not ignorePauseButton) { - ret = buttonRaw::pauseLong; + ret = commandRaw::pauseLong; ignorePauseButton = true; } else if (buttonUp.wasReleased()) { if (!ignoreUpButton) { - ret = buttonRaw::up; + ret = commandRaw::up; } else ignoreUpButton = false; } else if (buttonUp.pressedFor(buttonLongPress) && not ignoreUpButton) { - ret = buttonRaw::upLong; + ret = commandRaw::upLong; ignoreUpButton = true; } else if (buttonDown.wasReleased()) { if (!ignoreDownButton) { - ret = buttonRaw::down; + ret = commandRaw::down; } else ignoreDownButton = false; } else if (buttonDown.pressedFor(buttonLongPress) && not ignoreDownButton) { - ret = buttonRaw::downLong; + ret = commandRaw::downLong; ignoreDownButton = true; } #ifdef FIVEBUTTONS else if (buttonFour.wasReleased()) { - ret = buttonRaw::four; + ret = commandRaw::four; } else if (buttonFive.wasReleased()) { - ret = buttonRaw::five; + ret = commandRaw::five; } #endif - if (ret != buttonRaw::none) { + if (ret != commandRaw::none) { LOG(button_log, s_debug, F("Button raw: "), static_cast(ret)); } return ret; } -buttonCmd Buttons::getButtonCmd(buttonRaw b) { - buttonCmd ret = buttonCmd::none; - - switch (b) { - case buttonRaw::none : ret = buttonCmd::none ; break; - case buttonRaw::pause : ret = buttonCmd::pause ; break; - case buttonRaw::pauseLong: ret = buttonCmd::track ; break; - case buttonRaw::up : ret = (!settings.invertVolumeButtons) ? buttonCmd::next : buttonCmd::volume_up ; break; - case buttonRaw::upLong : ret = (!settings.invertVolumeButtons) ? buttonCmd::volume_up : buttonCmd::next ; break; - case buttonRaw::down : ret = (!settings.invertVolumeButtons) ? buttonCmd::previous : buttonCmd::volume_down; break; - case buttonRaw::downLong : ret = (!settings.invertVolumeButtons) ? buttonCmd::volume_down : buttonCmd::previous ; break; - case buttonRaw::allLong : ret = buttonCmd::admin ; break; -#ifdef FIVEBUTTONS - case buttonRaw::four : ret = (!settings.invertVolumeButtons) ? buttonCmd::volume_up : buttonCmd::next ; break; - case buttonRaw::five : ret = (!settings.invertVolumeButtons) ? buttonCmd::volume_down : buttonCmd::previous ; break; -#endif - case buttonRaw::start : ret = buttonCmd::start; ; break; - } - - if (ret != buttonCmd::none) { - LOG(button_log, s_debug, F("Button cmd: "), static_cast(ret)); - } - return ret; -} - -uint8_t Buttons::getButtonCode(buttonRaw b) { - switch (b) { - case buttonRaw::pause: return 1; - case buttonRaw::up : return 2; - case buttonRaw::down : return 3; - default : return 0; - } -} - -bool Buttons::isNoButton() { - return not buttonPause.isPressed() - && not buttonUp .isPressed() - && not buttonDown .isPressed(); -} - bool Buttons::isReset() { constexpr int buttonActiveLevel = getLevel(buttonPinType, level::active); return (digitalRead(buttonPausePin) == buttonActiveLevel && diff --git a/src/buttons.hpp b/src/buttons.hpp index 04eec2a3..cb723d7f 100644 --- a/src/buttons.hpp +++ b/src/buttons.hpp @@ -4,46 +4,15 @@ #include #include -#include "settings.hpp" +#include "commands.hpp" #include "constants.hpp" -enum class buttonRaw: uint8_t { - none, - pause, - pauseLong, - up, - upLong, - down, - downLong, - allLong, -#ifdef FIVEBUTTONS - four, - five, -#endif - start, -}; - -enum class buttonCmd: uint8_t { - none, - admin, - pause, - track, - volume_up, - volume_down, - next, - previous, - start, -}; - -class Buttons { +class Buttons: public CommandSource { public: - Buttons(const Settings& settings); + Buttons(); - buttonRaw getButtonRaw(); - buttonCmd getButtonCmd(buttonRaw b); - static uint8_t getButtonCode(buttonRaw b); + commandRaw getCommandRaw() override; bool isReset(); - bool isNoButton(); private: @@ -60,7 +29,6 @@ class Buttons { bool ignoreUpButton = false; bool ignoreDownButton = false; - const Settings& settings; }; #endif /* SRC_BUTTONS_HPP_ */ diff --git a/src/chip_card.cpp b/src/chip_card.cpp index dfd7b194..fedfae01 100644 --- a/src/chip_card.cpp +++ b/src/chip_card.cpp @@ -4,7 +4,6 @@ #include #include "mp3.hpp" -#include "buttons.hpp" #include "constants.hpp" #include "logger.hpp" @@ -71,10 +70,9 @@ const byte sector = 1; const byte trailerBlock = 7; } // namespace -Chip_card::Chip_card(Mp3 &mp3, Buttons &buttons) +Chip_card::Chip_card(Mp3 &mp3) : mfrc522(mfrc522_SSPin, mfrc522_RSTPin) , mp3(mp3) -, buttons(buttons) , cardRemovedSwitch(cardRemoveDelay) {} diff --git a/src/chip_card.hpp b/src/chip_card.hpp index b9f518ea..451f898c 100644 --- a/src/chip_card.hpp +++ b/src/chip_card.hpp @@ -62,7 +62,6 @@ enum class cardEvent: uint8_t { }; class Mp3; // forward declaration to not have to include it here -class Buttons; class delayedSwitchOn { public: @@ -81,7 +80,7 @@ class delayedSwitchOn { class Chip_card { public: - Chip_card(Mp3 &mp3, Buttons &buttons); + Chip_card(Mp3 &mp3); bool readCard ( nfcTagObject &nfcTag); bool writeCard(const nfcTagObject &nfcTag); @@ -97,7 +96,6 @@ class Chip_card { MFRC522 mfrc522; Mp3 &mp3; - Buttons &buttons; delayedSwitchOn cardRemovedSwitch; bool cardRemoved = true; diff --git a/src/commands.cpp b/src/commands.cpp new file mode 100644 index 00000000..0e280bb5 --- /dev/null +++ b/src/commands.cpp @@ -0,0 +1,59 @@ +#include "commands.hpp" + +#include "constants.hpp" +#include "logger.hpp" + +Commands::Commands(const Settings& settings, CommandSource* source1, CommandSource* source2, CommandSource* source3) +: settings(settings) +, sources() +{ + sources[0] = source1; + sources[1] = source2; + sources[2] = source3; +} + +commandRaw Commands::getCommandRaw() { + for (auto source: sources) { + if (source != nullptr) { + const commandRaw c = source->getCommandRaw(); + if (c != commandRaw::none) + return c; + } + } + return commandRaw::none; +} + +command Commands::getCommand(commandRaw b) { + command ret = command::none; + + switch (b) { + case commandRaw::none : ret = command::none ; break; + case commandRaw::pause : ret = command::pause ; break; + case commandRaw::pauseLong: ret = command::track ; break; + case commandRaw::up : ret = (!settings.invertVolumeButtons) ? command::next : command::volume_up ; break; + case commandRaw::upLong : ret = (!settings.invertVolumeButtons) ? command::volume_up : command::next ; break; + case commandRaw::down : ret = (!settings.invertVolumeButtons) ? command::previous : command::volume_down; break; + case commandRaw::downLong : ret = (!settings.invertVolumeButtons) ? command::volume_down : command::previous ; break; + case commandRaw::allLong : ret = command::admin ; break; +#ifdef FIVEBUTTONS + case commandRaw::four : ret = (!settings.invertVolumeButtons) ? command::volume_up : command::next ; break; + case commandRaw::five : ret = (!settings.invertVolumeButtons) ? command::volume_down : command::previous ; break; +#endif + case commandRaw::start : ret = command::start; ; break; + } + + if (ret != command::none) { + LOG(button_log, s_debug, F("Command: "), static_cast(ret)); + } + return ret; +} + +uint8_t Commands::getButtonCode(commandRaw b) { + switch (b) { + case commandRaw::pause: return 1; + case commandRaw::up : return 2; + case commandRaw::down : return 3; + default : return 0; + } +} + diff --git a/src/commands.hpp b/src/commands.hpp new file mode 100644 index 00000000..ed696c3e --- /dev/null +++ b/src/commands.hpp @@ -0,0 +1,57 @@ +#ifndef SRC_COMMANDS_HPP_ +#define SRC_COMMANDS_HPP_ + +#include + +#include "settings.hpp" +#include "constants.hpp" +#include "array.hpp" + +enum class commandRaw: uint8_t { + none, + pause, + pauseLong, + up, + upLong, + down, + downLong, + allLong, +#ifdef FIVEBUTTONS + four, + five, +#endif + start, +}; + +enum class command: uint8_t { + none, + admin, + pause, + track, + volume_up, + volume_down, + next, + previous, + start, +}; + +class CommandSource { +public: + virtual commandRaw getCommandRaw()=0; +}; + +class Commands { +public: + Commands(const Settings& settings, CommandSource* source1, CommandSource* source2 = nullptr, CommandSource* source3 = nullptr); + + commandRaw getCommandRaw(); + command getCommand (commandRaw b); + + static uint8_t getButtonCode(commandRaw b); + +private: + const Settings& settings; + array sources; +}; + +#endif /* SRC_COMMANDS_HPP_ */ diff --git a/src/constants.hpp b/src/constants.hpp index 97a2c8a7..4cdc4fad 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -15,7 +15,7 @@ enum class levelType: uint8_t { }; inline constexpr int getLevel(levelType t, level l) { return (l == level::inactive) ? (t==levelType::activeHigh ? LOW : HIGH) - : (t==levelType::activeHigh ? HIGH : LOW ); } + : (t==levelType::activeHigh ? HIGH : LOW ); } // ####### buttons ##################################### diff --git a/src/modifier.cpp b/src/modifier.cpp index ff9145eb..7202bbda 100644 --- a/src/modifier.cpp +++ b/src/modifier.cpp @@ -18,7 +18,7 @@ void SleepTimer::loop() { if (sleepTimer.isActive() && sleepTimer.isExpired()) { LOG(modifier_log, s_info, str_SleepTimer(), F(" -> SLEEP!")); if (SM_tonuino::is_in_state()) - SM_tonuino::dispatch(button_e(buttonRaw::pause)); + SM_tonuino::dispatch(command_e(commandRaw::pause)); tonuino.resetActiveModifier(); } } diff --git a/src/serial_input.cpp b/src/serial_input.cpp new file mode 100644 index 00000000..51988b73 --- /dev/null +++ b/src/serial_input.cpp @@ -0,0 +1,28 @@ +#include "serial_input.hpp" + +#include "constants.hpp" +#include "logger.hpp" + +SerialInput::SerialInput() +: CommandSource() +{ +} + +commandRaw SerialInput::getCommandRaw() { + commandRaw ret = commandRaw::none; + if (Serial.available() > 0) { + int optionSerial = Serial.parseInt(); + switch (optionSerial) { + case 2: ret = commandRaw::down ; break; + case 8: ret = commandRaw::up ; break; + case 3: ret = commandRaw::downLong ; break; + case 9: ret = commandRaw::upLong ; break; + case 5: ret = commandRaw::pause ; break; + case 6: ret = commandRaw::pauseLong; break; + case 4: ret = commandRaw::allLong ; break; + default: break; + } + } + return ret; +} + diff --git a/src/serial_input.hpp b/src/serial_input.hpp new file mode 100644 index 00000000..dbebb28b --- /dev/null +++ b/src/serial_input.hpp @@ -0,0 +1,16 @@ +#ifndef SRC_SERIAL_INPUT_HPP_ +#define SRC_SERIAL_INPUT_HPP_ + +#include + +#include "commands.hpp" +#include "constants.hpp" + +class SerialInput: public CommandSource { +public: + SerialInput(); + + commandRaw getCommandRaw() override; +}; + +#endif /* SRC_SERIAL_INPUT_HPP_ */ diff --git a/src/state_machine.cpp b/src/state_machine.cpp index ec78800a..ef73b31a 100644 --- a/src/state_machine.cpp +++ b/src/state_machine.cpp @@ -50,7 +50,7 @@ class VoiceMenu : public SM { protected: void entry(bool entryPlayAfter = true); - void react(button_e const &) override; + void react(command_e const &) override; void playCurrentValue(); static uint8_t numberOfOptions ; @@ -71,42 +71,42 @@ class ChMode : public VoiceMenu_setupCard { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; }; class ChFolder : public VoiceMenu_setupCard { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; }; class ChTrack : public VoiceMenu_setupCard { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; }; class ChFirstTrack : public VoiceMenu_setupCard { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; }; class ChLastTrack : public VoiceMenu_setupCard { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; }; class WriteCard : public SM_writeCard { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; private: enum subState: uint8_t { start_waitCardInserted, @@ -125,14 +125,14 @@ class Admin_BaseSetting: public VoiceMenu_tonuino class Amin_BaseWriteCard: public VoiceMenu_tonuino { protected: - bool handleWriteCard(button_e const &b); + bool handleWriteCard(command_e const &cmd_e); }; class Admin_Allow: public VoiceMenu_tonuino { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; private: enum subState: uint8_t { select_method, @@ -157,7 +157,7 @@ class Admin_Entry: public VoiceMenu_tonuino { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; static uint8_t lastCurrentValue ; }; @@ -165,7 +165,7 @@ class Admin_NewCard: public Amin_BaseWriteCard { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; private: enum subState: uint8_t { start_setupCard, @@ -181,7 +181,7 @@ class Admin_SimpleSetting: public Admin_BaseSetting { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; enum Type: uint8_t { maxVolume, minVolume, @@ -195,7 +195,7 @@ class Admin_ModCard: public Amin_BaseWriteCard { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; private: mode_t mode; enum subState: uint8_t { @@ -210,7 +210,7 @@ class Admin_ShortCut: public Admin_BaseSetting { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; private: enum subState: uint8_t { start_setupCard, @@ -225,14 +225,14 @@ class Admin_StandbyTimer: public Admin_BaseSetting { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; }; class Admin_CardsForFolder: public Amin_BaseWriteCard { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; private: enum subState: uint8_t { start_getFolder, @@ -254,21 +254,21 @@ class Admin_InvButtons: public Admin_BaseSetting { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; }; class Admin_ResetEeprom: public SM_tonuino { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; }; class Admin_LockAdmin: public Admin_BaseSetting { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; private: enum subState: uint8_t { get_mode, @@ -284,15 +284,15 @@ class Admin_PauseIfCardRemoved: public Admin_BaseSetting { public: void entry() final; - void react(button_e const &) final; + void react(command_e const &) final; }; // ####################################################### template -bool SM::isAbort(button_e const &b) { - if (b.b == buttonRaw::pauseLong) { +bool SM::isAbort(command_e const &b) { + if (b.cmd_raw == commandRaw::pauseLong) { SM::mp3.enqueueMp3FolderTrack(mp3Tracks::t_802_reset_aborted); LOG(state_log, s_info, F("SM"), str_abort()); this->template transit(); @@ -320,12 +320,13 @@ void VoiceMenu::playCurrentValue() { } template -void VoiceMenu::react(button_e const &b) { +void VoiceMenu::react(command_e const &cmd_e) { if ( currentValue != 0 && preview && not previewStarted && previewTimer.isExpired() - && not SM::mp3.isPlaying()) { + && not SM::mp3.isPlaying()) + { LOG(state_log, s_debug, str_VoiceMenu(), F("::react() start preview "), currentValue); if (previewFromFolder == 0) SM::mp3.enqueueTrack(currentValue, 1); @@ -334,30 +335,30 @@ void VoiceMenu::react(button_e const &b) { previewStarted = true; } - switch(b.b) { - case buttonRaw::upLong: + switch(cmd_e.cmd_raw) { + case commandRaw::upLong: currentValue = min(currentValue + 10, numberOfOptions); playCurrentValue(); break; - case buttonRaw::up: + case commandRaw::up: currentValue = min(currentValue + 1, numberOfOptions); playCurrentValue(); break; - case buttonRaw::downLong: + case commandRaw::downLong: currentValue = max(currentValue - 10, 1); playCurrentValue(); break; - case buttonRaw::down: + case commandRaw::down: currentValue = max(currentValue - 1, 1); playCurrentValue(); break; default: break; } - if (b.b != buttonRaw::none) { + if (cmd_e.cmd_raw != commandRaw::none) { LOG(state_log, s_info, str_VoiceMenu(), F(" currentVal: "), currentValue); } }; @@ -378,16 +379,16 @@ void ChMode::entry() { VoiceMenu::entry(); }; -void ChMode::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_ChMode(), F("::react() "), static_cast(b.b)); +void ChMode::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_ChMode(), F("::react() "), static_cast(cmd_e.cmd_raw)); } - VoiceMenu::react(b); + VoiceMenu::react(cmd_e); - if (isAbort(b)) + if (isAbort(cmd_e)) return; - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + if ((cmd_e.cmd_raw == commandRaw::pause) && (currentValue != 0)) { folder.mode = static_cast(currentValue); LOG(state_log, s_info, str_ChMode(), F(": "), currentValue); if (folder.mode == mode_t::admin) { @@ -420,16 +421,16 @@ void ChFolder::entry() { VoiceMenu::entry(); }; -void ChFolder::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_ChFolder(), F("::react() "), static_cast(b.b)); +void ChFolder::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_ChFolder(), F("::react() "), static_cast(cmd_e.cmd_raw)); } - VoiceMenu::react(b); + VoiceMenu::react(cmd_e); - if (isAbort(b)) + if (isAbort(cmd_e)) return; - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + if ((cmd_e.cmd_raw == commandRaw::pause) && (currentValue != 0)) { folder.folder = currentValue; LOG(state_log, s_info, str_ChFolder(), F(": "), currentValue); if (folder.mode == mode_t::einzel) { @@ -460,16 +461,16 @@ void ChTrack::entry() { VoiceMenu::entry(); }; -void ChTrack::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_ChTrack(), F("::react() "), static_cast(b.b)); +void ChTrack::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_ChTrack(), F("::react() "), static_cast(cmd_e.cmd_raw)); } - VoiceMenu::react(b); + VoiceMenu::react(cmd_e); - if (isAbort(b)) + if (isAbort(cmd_e)) return; - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + if ((cmd_e.cmd_raw == commandRaw::pause) && (currentValue != 0)) { folder.special = currentValue; LOG(state_log, s_info, str_ChTrack(), F(": "), currentValue); transit(); @@ -490,16 +491,16 @@ void ChFirstTrack::entry() { VoiceMenu::entry(); }; -void ChFirstTrack::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_ChFirstTrack(), F("::react() "), static_cast(b.b)); +void ChFirstTrack::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_ChFirstTrack(), F("::react() "), static_cast(cmd_e.cmd_raw)); } - VoiceMenu::react(b); + VoiceMenu::react(cmd_e); - if (isAbort(b)) + if (isAbort(cmd_e)) return; - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + if ((cmd_e.cmd_raw == commandRaw::pause) && (currentValue != 0)) { folder.special = currentValue; LOG(state_log, s_info, str_ChFirstTrack(), F(": "), currentValue); transit(); @@ -523,16 +524,16 @@ void ChLastTrack::entry() { currentValue = folder.special; }; -void ChLastTrack::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_ChLastTrack(), F("::react() "), static_cast(b.b)); +void ChLastTrack::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_ChLastTrack(), F("::react() "), static_cast(cmd_e.cmd_raw)); } - VoiceMenu::react(b); + VoiceMenu::react(cmd_e); - if (isAbort(b)) + if (isAbort(cmd_e)) return; - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + if ((cmd_e.cmd_raw == commandRaw::pause) && (currentValue != 0)) { folder.special2 = currentValue; LOG(state_log, s_info, str_ChLastTrack(), F(": "), currentValue); transit(); @@ -547,12 +548,12 @@ void WriteCard::entry() { current_subState = start_waitCardInserted; }; -void WriteCard::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_WriteCard(), F("::react() "), static_cast(b.b)); +void WriteCard::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_WriteCard(), F("::react() "), static_cast(cmd_e.cmd_raw)); } - if (isAbort(b)) + if (isAbort(cmd_e)) return; switch (current_subState) { @@ -642,46 +643,46 @@ void Idle::entry() { tonuino.setStandbyTimer(); }; -void Idle::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Idle(), F("::react(b) "), static_cast(b.b)); +void Idle::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_Idle(), F("::react(cmd_e) "), static_cast(cmd_e.cmd_raw)); } - const buttonCmd cmd = buttons.getButtonCmd(b.b); - uint8_t shortCut = 0xff; + const command cmd = commands.getCommand(cmd_e.cmd_raw); + uint8_t shortCut = 0xff; switch (cmd) { - case buttonCmd::admin: + case command::admin: LOG(state_log, s_debug, str_Idle(), str_to(), str_Admin_Allow()); transit(); return; - case buttonCmd::pause: - case buttonCmd::track: + case command::pause: + case command::track: if (tonuino.getActiveModifier().handlePause()) break; shortCut = 0; break; - case buttonCmd::volume_up: + case command::volume_up: if (tonuino.getActiveModifier().handleVolumeUp()) break; shortCut = 1; break; - case buttonCmd::next: + case command::next: if (tonuino.getActiveModifier().handleNextButton()) break; shortCut = 1; break; - case buttonCmd::volume_down: + case command::volume_down: if (tonuino.getActiveModifier().handleVolumeDown()) break; shortCut = 2; break; - case buttonCmd::previous: + case command::previous: if (tonuino.getActiveModifier().handlePreviousButton()) break; shortCut = 2; break; - case buttonCmd::start: + case command::start: shortCut = 3; break; default: @@ -694,11 +695,11 @@ void Idle::react(button_e const &b) { handleShortcut(shortCut); }; -void Idle::react(card_e const &c) { - if (c.c != cardEvent::none) { - LOG(state_log, s_debug, str_Idle(), F("::react(c) "), static_cast(c.c)); +void Idle::react(card_e const &c_e) { + if (c_e.card_ev != cardEvent::none) { + LOG(state_log, s_debug, str_Idle(), F("::react(c) "), static_cast(c_e.card_ev)); } - switch (c.c) { + switch (c_e.card_ev) { case cardEvent::inserted: if (readCard()) handleReadCard(); @@ -718,46 +719,46 @@ void Play::entry() { mp3.start(); }; -void Play::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Play(), F("::react(b) "), static_cast(b.b)); +void Play::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_Play(), F("::react(cmd_e) "), static_cast(cmd_e.cmd_raw)); } - buttonCmd cmd = buttons.getButtonCmd(b.b); + command cmd = commands.getCommand(cmd_e.cmd_raw); switch (cmd) { - case buttonCmd::admin: + case command::admin: LOG(state_log, s_debug, str_Play(), str_to(), str_Admin_Allow()); transit(); return; - case buttonCmd::pause: + case command::pause: LOG(state_log, s_debug, F("Pause Taste")); if (tonuino.getActiveModifier().handlePause()) break; LOG(state_log, s_debug, str_Play(), str_to(), str_Pause()); transit(); return; - case buttonCmd::track: + case command::track: if (tonuino.getActiveModifier().handlePause()) break; tonuino.playTrackNumber(); break; - case buttonCmd::volume_up: + case command::volume_up: if (tonuino.getActiveModifier().handleVolumeUp()) break; mp3.increaseVolume(); break; - case buttonCmd::next: + case command::next: if (tonuino.getActiveModifier().handleNextButton()) break; tonuino.nextTrack(); break; - case buttonCmd::volume_down: + case command::volume_down: if (tonuino.getActiveModifier().handleVolumeDown()) break; mp3.decreaseVolume(); break; - case buttonCmd::previous: + case command::previous: if (tonuino.getActiveModifier().handlePreviousButton()) break; tonuino.previousTrack(); @@ -772,11 +773,11 @@ void Play::react(button_e const &b) { } }; -void Play::react(card_e const &c) { - if (c.c != cardEvent::none) { - LOG(state_log, s_debug, str_Play(), F("::react(c) "), static_cast(c.c)); +void Play::react(card_e const &c_e) { + if (c_e.card_ev != cardEvent::none) { + LOG(state_log, s_debug, str_Play(), F("::react(c) "), static_cast(c_e.card_ev)); } - switch (c.c) { + switch (c_e.card_ev) { case cardEvent::inserted: if (readCard()) handleReadCard(); @@ -800,46 +801,46 @@ void Pause::entry() { mp3.pause(); }; -void Pause::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Pause(), F("::react(b) "), static_cast(b.b)); +void Pause::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_Pause(), F("::react(b) "), static_cast(cmd_e.cmd_raw)); } - const buttonCmd cmd = buttons.getButtonCmd(b.b); + const command cmd = commands.getCommand(cmd_e.cmd_raw); uint8_t shortCut = 99; switch (cmd) { - case buttonCmd::admin: + case command::admin: LOG(state_log, s_debug, str_Pause(), str_to(), str_Admin_Allow()); transit(); return; - case buttonCmd::pause: + case command::pause: if (tonuino.getActiveModifier().handlePause()) break; LOG(state_log, s_debug, str_Pause(), str_to(), str_Play()); transit(); return; - case buttonCmd::track: + case command::track: if (tonuino.getActiveModifier().handlePause()) break; shortCut = 0; break; - case buttonCmd::volume_up: + case command::volume_up: if (tonuino.getActiveModifier().handleVolumeUp()) break; shortCut = 1; break; - case buttonCmd::next: + case command::next: if (tonuino.getActiveModifier().handleNextButton()) break; shortCut = 1; break; - case buttonCmd::volume_down: + case command::volume_down: if (tonuino.getActiveModifier().handleVolumeDown()) break; shortCut = 2; break; - case buttonCmd::previous: + case command::previous: if (tonuino.getActiveModifier().handlePreviousButton()) break; shortCut = 2; @@ -851,11 +852,11 @@ void Pause::react(button_e const &b) { handleShortcut(shortCut); }; -void Pause::react(card_e const &c) { - if (c.c != cardEvent::none) { - LOG(state_log, s_debug, str_Pause(), F("::react(c) "), static_cast(c.c)); +void Pause::react(card_e const &c_e) { + if (c_e.card_ev != cardEvent::none) { + LOG(state_log, s_debug, str_Pause(), F("::react(c) "), static_cast(c_e.card_ev)); } - switch (c.c) { + switch (c_e.card_ev) { case cardEvent::inserted: if (readCard()) { if (settings.pauseWhenCardRemoved && tonuino.getCard() == lastCardRead && not tonuino.getActiveModifier().handlePause()) { @@ -879,7 +880,7 @@ void StartPlay::entry() { mp3.enqueueMp3FolderTrack(mp3Tracks::t_262_pling); }; -void StartPlay::react(button_e const &/*b*/) { +void StartPlay::react(command_e const &/*cmd_e*/) { if (not mp3.isPlayingMp3()) { tonuino.playFolder(); LOG(state_log, s_debug, str_StartPlay(), str_to(), str_Play()); @@ -896,8 +897,8 @@ void Admin_BaseSetting::saveAndTransit() { transit(); } -bool Amin_BaseWriteCard::handleWriteCard(button_e const &b) { - SM_writeCard::dispatch(b); +bool Amin_BaseWriteCard::handleWriteCard(command_e const &cmd_e) { + SM_writeCard::dispatch(cmd_e); if (SM_writeCard::is_in_state()) { LOG(state_log, s_debug, str_Admin_BaseWriteCard(), str_to(), F("Idle or AdmEntry")); transit(); @@ -920,14 +921,11 @@ void Admin_Allow::entry() { tonuino.resetActiveModifier(); }; -void Admin_Allow::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Admin_Allow(), F("::react() "), static_cast(b.b)); +void Admin_Allow::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_Admin_Allow(), F("::react() "), static_cast(cmd_e.cmd_raw)); } - if (isAbort(b)) - return; - switch (current_subState) { case select_method : if (settings.adminMenuLocked == 0) { @@ -940,6 +938,7 @@ void Admin_Allow::react(button_e const &b) { mp3.enqueueMp3FolderTrack(mp3Tracks::t_991_admin_pin); pin_number = 0; current_subState = wait_for_no_button; + timer.start(dfPlayer_timeUntilStarts); } // else if (settings.adminMenuLocked == 3) { // current_subState = start_match; @@ -947,14 +946,14 @@ void Admin_Allow::react(button_e const &b) { else { current_subState = not_allow; } - break; + return; // do not abort here case wait_for_no_button: - if (buttons.isNoButton()) + if (timer.isExpired() && not mp3.isPlaying()) current_subState = get_pin; - break; + return; // do not abort here case get_pin : { - const uint8_t c = Buttons::getButtonCode(b.b); + const uint8_t c = Commands::getButtonCode(cmd_e.cmd_raw); if (c != 0) { LOG(state_log, s_debug, F("pin: "), c); pin[pin_number++] = c; @@ -1020,7 +1019,7 @@ void Admin_Allow::react(button_e const &b) { // break; // case get_match_c : // VoiceMenu::react(b); -// if ((b.b == buttonRaw::pause) && (currentValue != 0)) { +// if ((b.b == commandRaw::pause) && (currentValue != 0)) { // if (current_subState == cv) // current_subState = allow; // else @@ -1037,6 +1036,9 @@ void Admin_Allow::react(button_e const &b) { transit(); return; } + + if (isAbort(cmd_e)) + return; }; // ####################################################### @@ -1062,19 +1064,19 @@ void Admin_Entry::entry() { currentValue = lastCurrentValue; }; -void Admin_Entry::react(button_e const &b) { +void Admin_Entry::react(command_e const &cmd_e) { if (not chip_card.isCardRemoved()) return; - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Admin_Entry(), F("::react() "), static_cast(b.b)); + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_Admin_Entry(), F("::react() "), static_cast(cmd_e.cmd_raw)); } - VoiceMenu::react(b); + VoiceMenu::react(cmd_e); - if (isAbort(b)) + if (isAbort(cmd_e)) return; - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + if ((cmd_e.cmd_raw == commandRaw::pause) && (currentValue != 0)) { lastCurrentValue = currentValue; switch (currentValue) { case 0: break; @@ -1145,9 +1147,9 @@ void Admin_NewCard::entry() { current_subState = start_setupCard; }; -void Admin_NewCard::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Admin_NewCard(), F("::react() "), static_cast(b.b)); +void Admin_NewCard::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_Admin_NewCard(), F("::react() "), static_cast(cmd_e.cmd_raw)); } switch (current_subState) { case start_setupCard: @@ -1155,7 +1157,7 @@ void Admin_NewCard::react(button_e const &b) { current_subState = run_setupCard; break; case run_setupCard: - SM_setupCard::dispatch(b); + SM_setupCard::dispatch(cmd_e); if (SM_setupCard::is_in_state()) current_subState = end_setupCard; if (SM_setupCard::is_in_state()) { @@ -1173,7 +1175,7 @@ void Admin_NewCard::react(button_e const &b) { current_subState = run_writeCard; break; case run_writeCard: - if (handleWriteCard(b)) + if (handleWriteCard(cmd_e)) return; break; default: @@ -1209,16 +1211,16 @@ void Admin_SimpleSetting::entry() { type == eq ? settings.eq : 0; }; -void Admin_SimpleSetting::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Admin_SimpleSetting(), F("::react() "), static_cast(b.b)); +void Admin_SimpleSetting::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_Admin_SimpleSetting(), F("::react() "), static_cast(cmd_e.cmd_raw)); } - VoiceMenu::react(b); + VoiceMenu::react(cmd_e); - if (isAbort(b)) + if (isAbort(cmd_e)) return; - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + if ((cmd_e.cmd_raw == commandRaw::pause) && (currentValue != 0)) { switch (type) { case maxVolume : settings.maxVolume = currentValue + settings.minVolume ; break; case minVolume : settings.minVolume = currentValue ; break; @@ -1250,13 +1252,13 @@ void Admin_ModCard::entry() { readyToWrite = false; }; -void Admin_ModCard::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Admin_ModCard(), F("::react() "), static_cast(b.b)); +void Admin_ModCard::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_Admin_ModCard(), F("::react() "), static_cast(cmd_e.cmd_raw)); } - VoiceMenu::react(b); + VoiceMenu::react(cmd_e); - if (isAbort(b)) + if (isAbort(cmd_e)) return; if (readyToWrite) { @@ -1286,7 +1288,7 @@ void Admin_ModCard::react(button_e const &b) { current_subState = run_writeCard; break; case run_writeCard: - if (handleWriteCard(b)) + if (handleWriteCard(cmd_e)) return; break; default: @@ -1295,7 +1297,7 @@ void Admin_ModCard::react(button_e const &b) { return; } - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + if ((cmd_e.cmd_raw == commandRaw::pause) && (currentValue != 0)) { if (mode == mode_t::none) { mode = static_cast(currentValue); if (mode != mode_t::sleep_timer) { @@ -1332,17 +1334,17 @@ void Admin_ShortCut::entry() { shortcut = 0; }; -void Admin_ShortCut::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Admin_ShortCut(), F("::react() "), static_cast(b.b)); +void Admin_ShortCut::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_Admin_ShortCut(), F("::react() "), static_cast(cmd_e.cmd_raw)); } - VoiceMenu::react(b); + VoiceMenu::react(cmd_e); - if (isAbort(b)) + if (isAbort(cmd_e)) return; if (shortcut == 0) { - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + if ((cmd_e.cmd_raw == commandRaw::pause) && (currentValue != 0)) { shortcut = currentValue; current_subState = start_setupCard; } @@ -1357,7 +1359,7 @@ void Admin_ShortCut::react(button_e const &b) { current_subState = run_setupCard; break; case run_setupCard: - SM_setupCard::dispatch(b); + SM_setupCard::dispatch(cmd_e); if (SM_setupCard::is_in_state()) current_subState = end_setupCard; if (SM_setupCard::is_in_state()) { @@ -1391,16 +1393,16 @@ void Admin_StandbyTimer::entry() { VoiceMenu::entry(false); }; -void Admin_StandbyTimer::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Admin_StandbyTimer(), F("::react() "), static_cast(b.b)); +void Admin_StandbyTimer::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_Admin_StandbyTimer(), F("::react() "), static_cast(cmd_e.cmd_raw)); } - VoiceMenu::react(b); + VoiceMenu::react(cmd_e); - if (isAbort(b)) + if (isAbort(cmd_e)) return; - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + if ((cmd_e.cmd_raw == commandRaw::pause) && (currentValue != 0)) { switch (currentValue) { case 1: settings.standbyTimer = 5; break; case 2: settings.standbyTimer = 15; break; @@ -1423,12 +1425,12 @@ void Admin_CardsForFolder::entry() { current_subState = start_getFolder; }; -void Admin_CardsForFolder::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Admin_CardsForFolder(), F("::react() "), static_cast(b.b)); +void Admin_CardsForFolder::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_Admin_CardsForFolder(), F("::react() "), static_cast(cmd_e.cmd_raw)); } - if (isAbort(b)) + if (isAbort(cmd_e)) return; switch (current_subState) { @@ -1443,8 +1445,8 @@ void Admin_CardsForFolder::react(button_e const &b) { current_subState = run_getFolder; break; case run_getFolder: - VoiceMenu::react(b); - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + VoiceMenu::react(cmd_e); + if ((cmd_e.cmd_raw == commandRaw::pause) && (currentValue != 0)) { folder.folder = currentValue; current_subState = start_getSpecial; } @@ -1460,8 +1462,8 @@ void Admin_CardsForFolder::react(button_e const &b) { current_subState = run_getSpecial; break; case run_getSpecial: - VoiceMenu::react(b); - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + VoiceMenu::react(cmd_e); + if ((cmd_e.cmd_raw == commandRaw::pause) && (currentValue != 0)) { special = currentValue; current_subState = start_getSpecial2; } @@ -1479,8 +1481,8 @@ void Admin_CardsForFolder::react(button_e const &b) { current_subState = run_getSpecial2; break; case run_getSpecial2: - VoiceMenu::react(b); - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + VoiceMenu::react(cmd_e); + if ((cmd_e.cmd_raw == commandRaw::pause) && (currentValue != 0)) { special2 = currentValue; mp3.enqueueMp3FolderTrack(mp3Tracks::t_936_batch_cards_intro); current_subState = prepare_writeCard; @@ -1508,7 +1510,7 @@ void Admin_CardsForFolder::react(button_e const &b) { } break; case run_writeCard: - SM_writeCard::dispatch(b); + SM_writeCard::dispatch(cmd_e); if (SM_writeCard::is_in_state()) { ++special; current_subState = prepare_writeCard; @@ -1538,16 +1540,16 @@ void Admin_InvButtons::entry() { VoiceMenu::entry(false); }; -void Admin_InvButtons::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Admin_InvButtons(), F("::react() "), static_cast(b.b)); +void Admin_InvButtons::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_Admin_InvButtons(), F("::react() "), static_cast(cmd_e.cmd_raw)); } - VoiceMenu::react(b); + VoiceMenu::react(cmd_e); - if (isAbort(b)) + if (isAbort(cmd_e)) return; - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + if ((cmd_e.cmd_raw == commandRaw::pause) && (currentValue != 0)) { switch (currentValue) { case 1: settings.invertVolumeButtons = false; break; case 2: settings.invertVolumeButtons = true ; break; @@ -1566,7 +1568,7 @@ void Admin_ResetEeprom::entry() { mp3.enqueueMp3FolderTrack(mp3Tracks::t_999_reset_ok); }; -void Admin_ResetEeprom::react(button_e const &/*b*/) { +void Admin_ResetEeprom::react(command_e const &/*cmd_e*/) { LOG(state_log, s_debug, str_Admin_ResetEeprom(), str_to(), str_Idle()); transit(); return; @@ -1588,18 +1590,18 @@ void Admin_LockAdmin::entry() { current_subState = get_mode; }; -void Admin_LockAdmin::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Admin_LockAdmin(), F("::react() "), static_cast(b.b)); +void Admin_LockAdmin::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_Admin_LockAdmin(), F("::react() "), static_cast(cmd_e.cmd_raw)); } - if (isAbort(b)) + if (isAbort(cmd_e)) return; switch(current_subState) { case get_mode: - VoiceMenu::react(b); - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + VoiceMenu::react(cmd_e); + if ((cmd_e.cmd_raw == commandRaw::pause) && (currentValue != 0)) { settings.adminMenuLocked = currentValue-1; if (settings.adminMenuLocked == 2) { current_subState = get_pin; @@ -1611,7 +1613,7 @@ void Admin_LockAdmin::react(button_e const &b) { break; case get_pin : { - const uint8_t c = Buttons::getButtonCode(b.b); + const uint8_t c = Commands::getButtonCode(cmd_e.cmd_raw); if (c != 0) { LOG(state_log, s_debug, F("pin: "), c); pin[pin_number++] = c; @@ -1642,16 +1644,16 @@ void Admin_PauseIfCardRemoved::entry() { VoiceMenu::entry(false); }; -void Admin_PauseIfCardRemoved::react(button_e const &b) { - if (b.b != buttonRaw::none) { - LOG(state_log, s_debug, str_Admin_PauseIfCardRemoved(), F("::react() "), static_cast(b.b)); +void Admin_PauseIfCardRemoved::react(command_e const &cmd_e) { + if (cmd_e.cmd_raw != commandRaw::none) { + LOG(state_log, s_debug, str_Admin_PauseIfCardRemoved(), F("::react() "), static_cast(cmd_e.cmd_raw)); } - VoiceMenu::react(b); + VoiceMenu::react(cmd_e); - if (isAbort(b)) + if (isAbort(cmd_e)) return; - if ((b.b == buttonRaw::pause) && (currentValue != 0)) { + if ((cmd_e.cmd_raw == commandRaw::pause) && (currentValue != 0)) { switch (currentValue) { case 1: settings.pauseWhenCardRemoved = false; break; case 2: settings.pauseWhenCardRemoved = true ; break; @@ -1674,7 +1676,7 @@ Tonuino &SM::tonuino = Tonuino::getTonuino(); template Mp3 &SM::mp3 = Tonuino::getTonuino().getMp3(); template -Buttons &SM::buttons = Tonuino::getTonuino().getButtons(); +Commands &SM::commands = Tonuino::getTonuino().getCommands(); template Settings &SM::settings = Tonuino::getTonuino().getSettings(); template diff --git a/src/state_machine.hpp b/src/state_machine.hpp index 6a64d5bc..f68eaac9 100644 --- a/src/state_machine.hpp +++ b/src/state_machine.hpp @@ -2,7 +2,7 @@ #define SRC_STATE_MACHINE_HPP_ #include "tinyfsm.hpp" -#include "buttons.hpp" +#include "commands.hpp" #include "chip_card.hpp" #include "mp3.hpp" #include "timer.hpp" @@ -12,13 +12,13 @@ class Tonuino; // ---------------------------------------------------------------------------- // Event Declarations // -struct button_e: tinyfsm::Event { - button_e(buttonRaw b):b{b} {} - buttonRaw b; +struct command_e: tinyfsm::Event { + command_e(commandRaw cmd_raw):cmd_raw{cmd_raw} {} + commandRaw cmd_raw; }; struct card_e : tinyfsm::Event { - card_e(cardEvent c): c{c} {} - cardEvent c; + card_e(cardEvent card_ev): card_ev{card_ev} {} + cardEvent card_ev; }; // ---------------------------------------------------------------------------- @@ -52,19 +52,19 @@ class SM: public tinyfsm::Fsm> Idle>::type >::type finished_abort; - virtual void react(button_e const &) { }; - virtual void react(card_e const &) { }; + virtual void react(command_e const &) { }; + virtual void react(card_e const &) { }; virtual void entry(void) { }; - void exit(void) { waitForPlayFinish = false; }; + void exit (void) { waitForPlayFinish = false; }; - bool isAbort(button_e const &b); + bool isAbort(command_e const &); static folderSettings folder; protected: static Tonuino &tonuino; static Mp3 &mp3; - static Buttons &buttons; + static Commands &commands; static Settings &settings; static Chip_card &chip_card; @@ -89,31 +89,31 @@ class Idle: public Base { public: void entry() override; - void react(button_e const &) override; - void react(card_e const &) override; + void react(command_e const &) override; + void react(card_e const &) override; }; class StartPlay: public Base { public: void entry() override; - void react(button_e const &) override; + void react(command_e const &) override; }; class Play: public Base { public: void entry() override; - void react(button_e const &) override; - void react(card_e const &) override; + void react(command_e const &) override; + void react(card_e const &) override; }; class Pause: public Base { public: void entry() override; - void react(button_e const &) override; - void react(card_e const &) override; + void react(command_e const &) override; + void react(card_e const &) override; }; // ---------------------------------------------------------------------------- diff --git a/src/tonuino.cpp b/src/tonuino.cpp index 586c4271..e8ba849f 100644 --- a/src/tonuino.cpp +++ b/src/tonuino.cpp @@ -41,7 +41,7 @@ void Tonuino::setup() { SM_tonuino::start(); // Start Shortcut "at Startup" - e.g. Welcome Sound - SM_tonuino::dispatch(button_e(buttonRaw::start)); + SM_tonuino::dispatch(command_e(commandRaw::start)); } void Tonuino::loop() { @@ -53,7 +53,7 @@ void Tonuino::loop() { activeModifier->loop(); - SM_tonuino::dispatch(button_e(buttons.getButtonRaw())); + SM_tonuino::dispatch(command_e(commands.getCommandRaw())); SM_tonuino::dispatch(card_e(chip_card.getCardEvent())); unsigned long stop_cycle = millis(); diff --git a/src/tonuino.hpp b/src/tonuino.hpp index 2c448727..e1febaee 100644 --- a/src/tonuino.hpp +++ b/src/tonuino.hpp @@ -2,7 +2,9 @@ #define SRC_TONUINO_HPP_ #include "settings.hpp" +#include "commands.hpp" #include "buttons.hpp" +#include "serial_input.hpp" #include "mp3.hpp" #include "modifier.hpp" #include "timer.hpp" @@ -32,7 +34,7 @@ class Tonuino { void setFolder(folderSettings *newFolder ) { myFolder = newFolder; } Mp3& getMp3 () { return mp3 ; } - Buttons& getButtons () { return buttons ; } + Commands& getCommands () { return commands ; } Settings& getSettings () { return settings ; } Chip_card& getChipCard() { return chip_card; } @@ -44,8 +46,10 @@ class Tonuino { Settings settings {}; Mp3 mp3 {settings}; - Buttons buttons {settings}; - Chip_card chip_card {mp3, buttons}; + Buttons buttons {}; + SerialInput serialInput {}; + Commands commands {settings, &buttons, &serialInput}; + Chip_card chip_card {mp3}; friend class Base; From 50726efc5817102ae4920f26adaad0219e11b3b9 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Thu, 5 May 2022 19:06:01 +0200 Subject: [PATCH 29/32] add hint for using -std=gnu++17 in Readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 1eec07f1..e0f5ae1f 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,9 @@ Die DIY Musikbox (nicht nur) für Kinder - Das Admin Menü kan an jeder Stelle abgrbrochen werden - Viele weitere Verbesserungen und Bug Fixes +**Achtung:** es ist jetzt c++17 erforderlich. Wer nicht plattformio verwendet, muss die Datei platform.txt editieren +`compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -std=gnu++17 -fpermissive ...` + ## Version 2.1 (xx.xx.xxxx) noch WIP - Partymodus hat nun eine Queue -> jedes Lied kommt nur genau 1x vorkommt - Neue Wiedergabe-Modi "Spezialmodus Von-Bis" - Hörspiel, Album und Party -> erlaubt z.B. verschiedene Alben in einem Ordner zu haben und je mit einer Karte zu verknüpfen From a37540ddc3660f0290ae76aa4d5a947e6ce133d6 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Thu, 5 May 2022 19:14:19 +0200 Subject: [PATCH 30/32] Disable serial input as command source for save memory --- src/tonuino.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tonuino.hpp b/src/tonuino.hpp index e1febaee..8bf5876c 100644 --- a/src/tonuino.hpp +++ b/src/tonuino.hpp @@ -47,8 +47,9 @@ class Tonuino { Settings settings {}; Mp3 mp3 {settings}; Buttons buttons {}; - SerialInput serialInput {}; - Commands commands {settings, &buttons, &serialInput}; +// SerialInput serialInput {}; +// Commands commands {settings, &buttons, &serialInput}; + Commands commands {settings, &buttons}; Chip_card chip_card {mp3}; friend class Base; From a007005d0cc00f9380d1b127570deba0d2a3be48 Mon Sep 17 00:00:00 2001 From: boerge1 Date: Thu, 5 May 2022 19:22:07 +0200 Subject: [PATCH 31/32] Fix Readme --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e0f5ae1f..26748e67 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,9 @@ Die DIY Musikbox (nicht nur) für Kinder - Die Main-Loop läuft jetzt stabil mit 50 ms - Neues Feature: neuer Mode: Hörbuch einzeln (nur ein Titel wird gespielt und Fortschritt gespeichert - Neues Feature: Pause, wenn Karte entfernt wird (lässt sich per Einstellungen steuern) -- Das Admin Menü wir nach einer Einstellung nicht verlassen (kann in der Software geändert leicht werden) -- Das Admin Menü kan an jeder Stelle abgrbrochen werden +- Das Admin Menü wir nach einer Einstellung nicht verlassen (kann in der Software leicht geändert werden) +- Das Admin Menü kann an jeder Stelle abgebrochen werden +- Außer den Buttons können andere Command Sources hinzugefügt werden (Bsp.: Serial Input) - Viele weitere Verbesserungen und Bug Fixes **Achtung:** es ist jetzt c++17 erforderlich. Wer nicht plattformio verwendet, muss die Datei platform.txt editieren From fdccc962809f2ab7de57c254b0d7d9619bba57a0 Mon Sep 17 00:00:00 2001 From: boerge1 <5250486+boerge1@users.noreply.github.com> Date: Thu, 3 Nov 2022 15:39:13 +0100 Subject: [PATCH 32/32] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 26748e67..29fe114b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +# Achtung +wird fortgesetzt im Projekt https://github.com/tonuino/TonUINO-TNG + # TonUINO Die DIY Musikbox (nicht nur) für Kinder