mirror of
https://github.com/esphome/esphome.git
synced 2025-09-01 19:02:18 +01:00
[rtttl] Fix RTTTL for speakers (#10381)
This commit is contained in:
committed by
Jesse Hills
parent
e513c0f004
commit
015977cfdf
@@ -138,11 +138,37 @@ void Rtttl::stop() {
|
|||||||
this->set_state_(STATE_STOPPING);
|
this->set_state_(STATE_STOPPING);
|
||||||
}
|
}
|
||||||
#endif
|
#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;
|
this->note_duration_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rtttl::loop() {
|
void Rtttl::loop() {
|
||||||
if (this->note_duration_ == 0 || this->state_ == State::STATE_STOPPED) {
|
if (this->state_ == State::STATE_STOPPED) {
|
||||||
this->disable_loop();
|
this->disable_loop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -152,6 +178,8 @@ void Rtttl::loop() {
|
|||||||
if (this->state_ == State::STATE_STOPPING) {
|
if (this->state_ == State::STATE_STOPPING) {
|
||||||
if (this->speaker_->is_stopped()) {
|
if (this->speaker_->is_stopped()) {
|
||||||
this->set_state_(State::STATE_STOPPED);
|
this->set_state_(State::STATE_STOPPED);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else if (this->state_ == State::STATE_INIT) {
|
} else if (this->state_ == State::STATE_INIT) {
|
||||||
if (this->speaker_->is_stopped()) {
|
if (this->speaker_->is_stopped()) {
|
||||||
@@ -207,7 +235,7 @@ void Rtttl::loop() {
|
|||||||
if (this->output_ != nullptr && millis() - this->last_note_ < this->note_duration_)
|
if (this->output_ != nullptr && millis() - this->last_note_ < this->note_duration_)
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
if (!this->rtttl_[this->position_]) {
|
if (this->position_ >= this->rtttl_.length()) {
|
||||||
this->finish_();
|
this->finish_();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -346,31 +374,6 @@ void Rtttl::loop() {
|
|||||||
this->last_note_ = millis();
|
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
|
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
|
||||||
static const LogString *state_to_string(State state) {
|
static const LogString *state_to_string(State state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
@@ -397,7 +400,11 @@ void Rtttl::set_state_(State state) {
|
|||||||
LOG_STR_ARG(state_to_string(state)));
|
LOG_STR_ARG(state_to_string(state)));
|
||||||
|
|
||||||
// Clear loop_done when transitioning from STOPPED to any other 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();
|
this->enable_loop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -60,35 +60,60 @@ class Rtttl : public Component {
|
|||||||
}
|
}
|
||||||
return ret;
|
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 finish_();
|
||||||
void set_state_(State state);
|
void set_state_(State state);
|
||||||
|
|
||||||
|
/// The RTTTL string to play.
|
||||||
std::string rtttl_{""};
|
std::string rtttl_{""};
|
||||||
|
/// The current position in the RTTTL string.
|
||||||
size_t position_{0};
|
size_t position_{0};
|
||||||
|
/// The duration of a whole note in milliseconds.
|
||||||
uint16_t wholenote_;
|
uint16_t wholenote_;
|
||||||
|
/// The default duration of a note (e.g. 4 for a quarter note).
|
||||||
uint16_t default_duration_;
|
uint16_t default_duration_;
|
||||||
|
/// The default octave for a note.
|
||||||
uint16_t default_octave_;
|
uint16_t default_octave_;
|
||||||
|
/// The time the last note was started.
|
||||||
uint32_t last_note_;
|
uint32_t last_note_;
|
||||||
|
/// The duration of the current note in milliseconds.
|
||||||
uint16_t note_duration_;
|
uint16_t note_duration_;
|
||||||
|
|
||||||
|
/// The frequency of the current note in Hz.
|
||||||
uint32_t output_freq_;
|
uint32_t output_freq_;
|
||||||
|
/// The gain of the output.
|
||||||
float gain_{0.6f};
|
float gain_{0.6f};
|
||||||
|
/// The current state of the RTTTL player.
|
||||||
State state_{State::STATE_STOPPED};
|
State state_{State::STATE_STOPPED};
|
||||||
|
|
||||||
#ifdef USE_OUTPUT
|
#ifdef USE_OUTPUT
|
||||||
|
/// The output to write the sound to.
|
||||||
output::FloatOutput *output_;
|
output::FloatOutput *output_;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_SPEAKER
|
#ifdef USE_SPEAKER
|
||||||
|
/// The speaker to write the sound to.
|
||||||
speaker::Speaker *speaker_{nullptr};
|
speaker::Speaker *speaker_{nullptr};
|
||||||
|
/// The sample rate of the speaker.
|
||||||
int sample_rate_{16000};
|
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};
|
int samples_per_wave_{0};
|
||||||
|
/// The number of samples sent.
|
||||||
int samples_sent_{0};
|
int samples_sent_{0};
|
||||||
|
/// The total number of samples to send.
|
||||||
int samples_count_{0};
|
int samples_count_{0};
|
||||||
|
/// The number of samples for the gap between notes.
|
||||||
int samples_gap_{0};
|
int samples_gap_{0};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// The callback to call when playback is finished.
|
||||||
CallbackManager<void()> on_finished_playback_callback_;
|
CallbackManager<void()> on_finished_playback_callback_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user