mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	[rtttl] Fix RTTTL for speakers (#10381)
This commit is contained in:
		| @@ -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