release
This commit is contained in:
230
lib/Melody Player/src/melody_player.cpp
Normal file
230
lib/Melody Player/src/melody_player.cpp
Normal file
@@ -0,0 +1,230 @@
|
||||
/***************************************************************************
|
||||
* This file is part of Melody Player, a library for Arduino *
|
||||
* to play notes on piezoelectric buzzers. *
|
||||
* *
|
||||
* Copyright (C) 2020-2022 Fabiano Riccardi *
|
||||
* *
|
||||
* This library is free software; you can redistribute *
|
||||
* it and/or modify it under the terms of the GNU Lesser General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2.1 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this library; if not, see <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
#include "melody_player.h"
|
||||
|
||||
/**
|
||||
* https://stackoverflow.com/questions/24609271/errormake-unique-is-not-a-member-of-std
|
||||
*/
|
||||
template<typename T, typename... Args> std::unique_ptr<T> make_unique(Args&&... args) {
|
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
void MelodyPlayer::play() {
|
||||
if (melodyState == nullptr) { return; }
|
||||
|
||||
turnOn();
|
||||
state = State::PLAY;
|
||||
|
||||
melodyState->advance();
|
||||
while (melodyState->getIndex() + melodyState->isSilence() < melodyState->melody.getLength()) {
|
||||
NoteDuration computedNote = melodyState->getCurrentComputedNote();
|
||||
if (debug)
|
||||
Serial.println(String("Playing: frequency:") + computedNote.frequency
|
||||
+ " duration:" + computedNote.duration);
|
||||
if (melodyState->isSilence()) {
|
||||
#ifdef ESP32
|
||||
ledcWriteTone(pwmChannel, 0);
|
||||
#else
|
||||
noTone(pin);
|
||||
#endif
|
||||
delay(0.3f * computedNote.duration);
|
||||
} else {
|
||||
#ifdef ESP32
|
||||
ledcWriteTone(pwmChannel, computedNote.frequency);
|
||||
#else
|
||||
tone(pin, computedNote.frequency);
|
||||
#endif
|
||||
delay(computedNote.duration);
|
||||
}
|
||||
melodyState->advance();
|
||||
}
|
||||
stop();
|
||||
}
|
||||
|
||||
void MelodyPlayer::play(Melody& melody) {
|
||||
if (!melody) { return; }
|
||||
melodyState = make_unique<MelodyState>(melody);
|
||||
play();
|
||||
}
|
||||
|
||||
void changeTone(MelodyPlayer* player) {
|
||||
// The last silence is not reproduced
|
||||
player->melodyState->advance();
|
||||
if (player->melodyState->getIndex() + player->melodyState->isSilence()
|
||||
< player->melodyState->melody.getLength()) {
|
||||
NoteDuration computedNote(player->melodyState->getCurrentComputedNote());
|
||||
|
||||
float duration = player->melodyState->getRemainingNoteDuration();
|
||||
if (duration > 0) {
|
||||
player->melodyState->resetRemainingNoteDuration();
|
||||
} else {
|
||||
if (player->melodyState->isSilence()) {
|
||||
duration = 0.3f * computedNote.duration;
|
||||
} else {
|
||||
duration = computedNote.duration;
|
||||
}
|
||||
}
|
||||
if (player->debug)
|
||||
Serial.println(String("Playing async: freq=") + computedNote.frequency + " dur=" + duration
|
||||
+ " iteration=" + player->melodyState->getIndex());
|
||||
|
||||
if (player->melodyState->isSilence()) {
|
||||
#ifdef ESP32
|
||||
ledcWriteTone(player->pwmChannel, 0);
|
||||
#else
|
||||
tone(player->pin, 0);
|
||||
#endif
|
||||
|
||||
#ifdef ESP32
|
||||
player->ticker.once_ms(duration, changeTone, player);
|
||||
#else
|
||||
player->ticker.once_ms_scheduled(duration, std::bind(changeTone, player));
|
||||
#endif
|
||||
} else {
|
||||
#ifdef ESP32
|
||||
ledcWriteTone(player->pwmChannel, computedNote.frequency);
|
||||
#else
|
||||
tone(player->pin, computedNote.frequency);
|
||||
#endif
|
||||
|
||||
#ifdef ESP32
|
||||
player->ticker.once_ms(duration, changeTone, player);
|
||||
#else
|
||||
player->ticker.once_ms_scheduled(duration, std::bind(changeTone, player));
|
||||
#endif
|
||||
}
|
||||
player->supportSemiNote = millis() + duration;
|
||||
} else {
|
||||
player->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void MelodyPlayer::playAsync() {
|
||||
if (melodyState == nullptr) { return; }
|
||||
|
||||
turnOn();
|
||||
state = State::PLAY;
|
||||
|
||||
// Start immediately
|
||||
#ifdef ESP32
|
||||
ticker.once(0, changeTone, this);
|
||||
#else
|
||||
ticker.once_scheduled(0, std::bind(changeTone, this));
|
||||
#endif
|
||||
}
|
||||
|
||||
void MelodyPlayer::playAsync(Melody& melody) {
|
||||
if (!melody) { return; }
|
||||
melodyState = make_unique<MelodyState>(melody);
|
||||
playAsync();
|
||||
}
|
||||
|
||||
void MelodyPlayer::stop() {
|
||||
if (melodyState == nullptr) { return; }
|
||||
|
||||
haltPlay();
|
||||
state = State::STOP;
|
||||
melodyState->reset();
|
||||
}
|
||||
|
||||
void MelodyPlayer::pause() {
|
||||
if (melodyState == nullptr) { return; }
|
||||
|
||||
haltPlay();
|
||||
state = State::PAUSE;
|
||||
melodyState->saveRemainingNoteDuration(supportSemiNote);
|
||||
}
|
||||
|
||||
void MelodyPlayer::transferMelodyTo(MelodyPlayer& destPlayer) {
|
||||
if (melodyState == nullptr) { return; }
|
||||
|
||||
destPlayer.stop();
|
||||
|
||||
bool playing = isPlaying();
|
||||
|
||||
haltPlay();
|
||||
state = State::STOP;
|
||||
melodyState->saveRemainingNoteDuration(supportSemiNote);
|
||||
destPlayer.melodyState = std::move(melodyState);
|
||||
|
||||
if (playing) {
|
||||
destPlayer.playAsync();
|
||||
} else {
|
||||
destPlayer.state = state;
|
||||
}
|
||||
}
|
||||
|
||||
void MelodyPlayer::duplicateMelodyTo(MelodyPlayer& destPlayer) {
|
||||
if (melodyState == nullptr) { return; }
|
||||
|
||||
destPlayer.stop();
|
||||
destPlayer.melodyState = make_unique<MelodyState>(*(this->melodyState));
|
||||
destPlayer.melodyState->saveRemainingNoteDuration(supportSemiNote);
|
||||
|
||||
if (isPlaying()) {
|
||||
destPlayer.playAsync();
|
||||
} else {
|
||||
destPlayer.state = state;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
MelodyPlayer::MelodyPlayer(unsigned char pin, unsigned char pwmChannel, bool offLevel)
|
||||
: pin(pin), pwmChannel(pwmChannel), offLevel(offLevel), state(State::STOP), melodyState(nullptr) {
|
||||
pinMode(pin, OUTPUT);
|
||||
digitalWrite(pin, offLevel);
|
||||
};
|
||||
#else
|
||||
MelodyPlayer::MelodyPlayer(unsigned char pin, bool offLevel)
|
||||
: pin(pin), offLevel(offLevel), state(State::STOP), melodyState(nullptr) {
|
||||
pinMode(pin, OUTPUT);
|
||||
digitalWrite(pin, offLevel);
|
||||
};
|
||||
#endif
|
||||
|
||||
void MelodyPlayer::haltPlay() {
|
||||
// Stop player, but do not reset the melodyState
|
||||
ticker.detach();
|
||||
turnOff();
|
||||
}
|
||||
|
||||
void MelodyPlayer::turnOn() {
|
||||
#ifdef ESP32
|
||||
const int resolution = 8;
|
||||
// 2000 is a frequency, it will be changed at the first play
|
||||
ledcSetup(pwmChannel, 2000, resolution);
|
||||
ledcAttachPin(pin, pwmChannel);
|
||||
ledcWrite(pwmChannel, 125);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MelodyPlayer::turnOff() {
|
||||
#ifdef ESP32
|
||||
ledcWrite(pwmChannel, 0);
|
||||
ledcDetachPin(pin);
|
||||
#else
|
||||
// Remember that this will set LOW output, it doesn't mean that buzzer is off (look at offLevel
|
||||
// for more info).
|
||||
noTone(pin);
|
||||
#endif
|
||||
|
||||
pinMode(pin, LOW);
|
||||
digitalWrite(pin, offLevel);
|
||||
}
|
||||
Reference in New Issue
Block a user