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