This commit is contained in:
Stephan Mühl
2023-03-22 12:15:18 +01:00
committed by GitHub
parent 3e12414a87
commit adb5102869
203 changed files with 35010 additions and 0 deletions

View 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);
}