mirror of
https://github.com/esphome/esphome.git
synced 2025-09-01 10:52:19 +01:00
[rtttl] Fix RTTTL for speakers (#10381)
This commit is contained in:
@@ -138,11 +138,37 @@ void Rtttl::stop() {
|
||||
this->set_state_(STATE_STOPPING);
|
||||
}
|
||||
#endif
|
||||
this->position_ = this->rtttl_.length();
|
||||
this->note_duration_ = 0;
|
||||
}
|
||||
|
||||
void Rtttl::finish_() {
|
||||
ESP_LOGV(TAG, "Rtttl::finish_()");
|
||||
#ifdef USE_OUTPUT
|
||||
if (this->output_ != nullptr) {
|
||||
this->output_->set_level(0.0);
|
||||
this->set_state_(State::STATE_STOPPED);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SPEAKER
|
||||
if (this->speaker_ != nullptr) {
|
||||
SpeakerSample sample[2];
|
||||
sample[0].left = 0;
|
||||
sample[0].right = 0;
|
||||
sample[1].left = 0;
|
||||
sample[1].right = 0;
|
||||
this->speaker_->play((uint8_t *) (&sample), 8);
|
||||
this->speaker_->finish();
|
||||
this->set_state_(State::STATE_STOPPING);
|
||||
}
|
||||
#endif
|
||||
// Ensure no more notes are played in case finish_() is called for an error.
|
||||
this->position_ = this->rtttl_.length();
|
||||
this->note_duration_ = 0;
|
||||
}
|
||||
|
||||
void Rtttl::loop() {
|
||||
if (this->note_duration_ == 0 || this->state_ == State::STATE_STOPPED) {
|
||||
if (this->state_ == State::STATE_STOPPED) {
|
||||
this->disable_loop();
|
||||
return;
|
||||
}
|
||||
@@ -152,6 +178,8 @@ void Rtttl::loop() {
|
||||
if (this->state_ == State::STATE_STOPPING) {
|
||||
if (this->speaker_->is_stopped()) {
|
||||
this->set_state_(State::STATE_STOPPED);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if (this->state_ == State::STATE_INIT) {
|
||||
if (this->speaker_->is_stopped()) {
|
||||
@@ -207,7 +235,7 @@ void Rtttl::loop() {
|
||||
if (this->output_ != nullptr && millis() - this->last_note_ < this->note_duration_)
|
||||
return;
|
||||
#endif
|
||||
if (!this->rtttl_[this->position_]) {
|
||||
if (this->position_ >= this->rtttl_.length()) {
|
||||
this->finish_();
|
||||
return;
|
||||
}
|
||||
@@ -346,31 +374,6 @@ void Rtttl::loop() {
|
||||
this->last_note_ = millis();
|
||||
}
|
||||
|
||||
void Rtttl::finish_() {
|
||||
#ifdef USE_OUTPUT
|
||||
if (this->output_ != nullptr) {
|
||||
this->output_->set_level(0.0);
|
||||
this->set_state_(State::STATE_STOPPED);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SPEAKER
|
||||
if (this->speaker_ != nullptr) {
|
||||
SpeakerSample sample[2];
|
||||
sample[0].left = 0;
|
||||
sample[0].right = 0;
|
||||
sample[1].left = 0;
|
||||
sample[1].right = 0;
|
||||
this->speaker_->play((uint8_t *) (&sample), 8);
|
||||
|
||||
this->speaker_->finish();
|
||||
this->set_state_(State::STATE_STOPPING);
|
||||
}
|
||||
#endif
|
||||
this->note_duration_ = 0;
|
||||
this->on_finished_playback_callback_.call();
|
||||
ESP_LOGD(TAG, "Playback finished");
|
||||
}
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
|
||||
static const LogString *state_to_string(State state) {
|
||||
switch (state) {
|
||||
@@ -397,7 +400,11 @@ void Rtttl::set_state_(State state) {
|
||||
LOG_STR_ARG(state_to_string(state)));
|
||||
|
||||
// Clear loop_done when transitioning from STOPPED to any other state
|
||||
if (old_state == State::STATE_STOPPED && state != State::STATE_STOPPED) {
|
||||
if (state == State::STATE_STOPPED) {
|
||||
this->disable_loop();
|
||||
this->on_finished_playback_callback_.call();
|
||||
ESP_LOGD(TAG, "Playback finished");
|
||||
} else if (old_state == State::STATE_STOPPED) {
|
||||
this->enable_loop();
|
||||
}
|
||||
}
|
||||
|
@@ -60,35 +60,60 @@ class Rtttl : public Component {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* @brief Finalizes the playback of the RTTTL string.
|
||||
*
|
||||
* This method is called internally when the end of the RTTTL string is reached
|
||||
* or when a parsing error occurs. It stops the output, sets the component state,
|
||||
* and triggers the on_finished_playback_callback_.
|
||||
*/
|
||||
void finish_();
|
||||
void set_state_(State state);
|
||||
|
||||
/// The RTTTL string to play.
|
||||
std::string rtttl_{""};
|
||||
/// The current position in the RTTTL string.
|
||||
size_t position_{0};
|
||||
/// The duration of a whole note in milliseconds.
|
||||
uint16_t wholenote_;
|
||||
/// The default duration of a note (e.g. 4 for a quarter note).
|
||||
uint16_t default_duration_;
|
||||
/// The default octave for a note.
|
||||
uint16_t default_octave_;
|
||||
/// The time the last note was started.
|
||||
uint32_t last_note_;
|
||||
/// The duration of the current note in milliseconds.
|
||||
uint16_t note_duration_;
|
||||
|
||||
/// The frequency of the current note in Hz.
|
||||
uint32_t output_freq_;
|
||||
/// The gain of the output.
|
||||
float gain_{0.6f};
|
||||
/// The current state of the RTTTL player.
|
||||
State state_{State::STATE_STOPPED};
|
||||
|
||||
#ifdef USE_OUTPUT
|
||||
/// The output to write the sound to.
|
||||
output::FloatOutput *output_;
|
||||
#endif
|
||||
|
||||
#ifdef USE_SPEAKER
|
||||
/// The speaker to write the sound to.
|
||||
speaker::Speaker *speaker_{nullptr};
|
||||
/// The sample rate of the speaker.
|
||||
int sample_rate_{16000};
|
||||
/// The number of samples for one full cycle of a note's waveform, in Q10 fixed-point format.
|
||||
int samples_per_wave_{0};
|
||||
/// The number of samples sent.
|
||||
int samples_sent_{0};
|
||||
/// The total number of samples to send.
|
||||
int samples_count_{0};
|
||||
/// The number of samples for the gap between notes.
|
||||
int samples_gap_{0};
|
||||
|
||||
#endif
|
||||
|
||||
/// The callback to call when playback is finished.
|
||||
CallbackManager<void()> on_finished_playback_callback_;
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user