diff --git a/src/melody_player.cpp b/src/melody_player.cpp index 9261368..c8ad26c 100644 --- a/src/melody_player.cpp +++ b/src/melody_player.cpp @@ -86,11 +86,14 @@ void changeTone(MelodyPlayer* player) { + " iteration=" + player->melodyState->getIndex()); if (player->melodyState->isSilence()) { + if(!player->muted) + { #ifdef ESP32 - ledcWriteTone(player->pwmChannel, 0); + ledcWriteTone(player->pwmChannel, 0); #else - tone(player->pin, 0); + tone(player->pin, 0); #endif + } #ifdef ESP32 player->ticker.once_ms(duration, changeTone, player); @@ -98,11 +101,15 @@ void changeTone(MelodyPlayer* player) { player->ticker.once_ms_scheduled(duration, std::bind(changeTone, player)); #endif } else { + if(!player->muted) + { #ifdef ESP32 - ledcWriteTone(player->pwmChannel, computedNote.frequency); + ledcWriteTone(player->pwmChannel, computedNote.frequency); + ledcWrite(player->pwmChannel,player->volume); #else - tone(player->pin, computedNote.frequency); + tone(player->pin, computedNote.frequency); #endif + } #ifdef ESP32 player->ticker.once_ms(duration, changeTone, player); @@ -112,7 +119,16 @@ void changeTone(MelodyPlayer* player) { } player->supportSemiNote = millis() + duration; } else { + // End of the melody player->stop(); + if(player->loop) + { // Loop mode => start over + player->playAsync(); + } + else if(player->stopCallback != NULL) + { + player->stopCallback(); + } } } @@ -130,9 +146,11 @@ void MelodyPlayer::playAsync() { #endif } -void MelodyPlayer::playAsync(Melody& melody) { +void MelodyPlayer::playAsync(Melody& melody, bool loopMelody, void(*callback)(void)) { if (!melody) { return; } melodyState = make_unique(melody); + loop = loopMelody; + stopCallback = callback; playAsync(); } @@ -211,7 +229,17 @@ void MelodyPlayer::turnOn() { // 2000 is a frequency, it will be changed at the first play ledcSetup(pwmChannel, 2000, resolution); ledcAttachPin(pin, pwmChannel); - ledcWrite(pwmChannel, 125); + ledcWrite(pwmChannel, volume); +#endif +} + +void MelodyPlayer::setVolume(byte newVolume) { + volume = newVolume/2; +#ifdef ESP32 + if(state == State::PLAY) + { + ledcWrite(pwmChannel, volume); + } #endif } @@ -228,3 +256,19 @@ void MelodyPlayer::turnOff() { pinMode(pin, OUTPUT); digitalWrite(pin, offLevel); } + +void MelodyPlayer::mute() { + muted = true; +} + +void MelodyPlayer::unmute() { +#ifdef ESP32 + ledcAttachPin(pin, pwmChannel); +#endif + muted = false; +} + +void MelodyPlayer::changeTempo(int newTempo) { + if (melodyState == nullptr) { return; } + melodyState->changeTempo(newTempo); +} diff --git a/src/melody_player.h b/src/melody_player.h index af290e9..8f4d771 100644 --- a/src/melody_player.h +++ b/src/melody_player.h @@ -57,8 +57,10 @@ class MelodyPlayer { /** * Play the given melody in asynchronous way (return immediately). * If the melody is not valid, this call has no effect. + * Set loop to true if you want the melody to start over after at the end. + * A call back can be provided to be called at the end of the melody (only used if loop is false) */ - void playAsync(Melody& melody); + void playAsync(Melody& melody, bool loop = false, void(*stopCallback)(void) = NULL); /** * Stop the current melody. @@ -73,6 +75,17 @@ class MelodyPlayer { */ void pause(); + /** + * Mute the sound of the current melody without stoppig it + * When unmute will be called the melody be resumed + */ + void mute(); + + /** + * Unmute the current melody + */ + void unmute(); + /** * Tell if playing. */ @@ -80,6 +93,19 @@ class MelodyPlayer { return state == State::PLAY; } + /** + * Change the tempo of the current melody to the proided value + */ + void changeTempo(int newTempo); + + /** + * Set the volume (0-255 value) of the player (only works on ESP32 platform) + * The volume is changed by adjusting the duty-cycle of the pwm signal sent to the piezo + * A value of 0 will produce no sound while a value of 255 will set the duty cycle to 50%, + * wich will produce the highest possible volume for the piezo. + */ + void setVolume(byte volume); + /** * Move the current melody and player's state to the given destination Player. * The source player stops and lose the reference to the actual melody (i.e. you have to call @@ -96,6 +122,10 @@ class MelodyPlayer { private: unsigned char pin; + byte volume = 125; + bool loop = false; + bool muted = false; + void (*stopCallback)(void) = NULL; #ifdef ESP32 unsigned char pwmChannel; @@ -116,9 +146,9 @@ class MelodyPlayer { */ class MelodyState { public: - MelodyState() : first(true), index(0), remainingNoteTime(0){}; + MelodyState() : first(true), index(0), remainingNoteTime(0), timeUnit(0) {}; MelodyState(const Melody& melody) - : melody(melody), first(true), silence(false), index(0), remainingNoteTime(0){}; + : melody(melody), first(true), silence(false), index(0), remainingNoteTime(0), timeUnit(melody.getTimeUnit()){}; Melody melody; unsigned short getIndex() const { @@ -129,6 +159,10 @@ class MelodyPlayer { return silence; } + void changeTempo(int newTempo) { + timeUnit = (60 * 1000 * 4 / newTempo / 32); + } + /** * Advance the melody index by one step. If there is a pending partial note it hasn't any * effect. @@ -192,7 +226,7 @@ class MelodyPlayer { */ NoteDuration getCurrentComputedNote() const { NoteDuration note = melody.getNote(getIndex()); - note.duration = melody.getTimeUnit() * note.duration; + note.duration = timeUnit * note.duration; // because the fixed point notation note.duration /= 2; return note; @@ -202,6 +236,7 @@ class MelodyPlayer { bool first; bool silence; unsigned short index; + unsigned short timeUnit; /** * Variable to support precise pauses and move/duplicate melodies between Players.