diff --git a/esphome/components/esp32_ble/ble.h b/esphome/components/esp32_ble/ble.h index 0674d293e1..6381e12448 100644 --- a/esphome/components/esp32_ble/ble.h +++ b/esphome/components/esp32_ble/ble.h @@ -156,8 +156,8 @@ class ESP32BLE : public Component { #endif static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); - // Handle non-ACTIVE state transitions (DISABLE, ENABLE, OFF, DISABLED). - // Extracted from loop() to keep the hot event-processing path small. + // Handle DISABLE and ENABLE transitions when not in the ACTIVE state. + // Other non-ACTIVE states (e.g. OFF, DISABLED) are currently treated as no-ops. void __attribute__((noinline)) loop_handle_state_transition_not_active_(); bool ble_setup_(); diff --git a/esphome/components/esp8266/preferences.cpp b/esphome/components/esp8266/preferences.cpp index f037b881a8..e749b1f633 100644 --- a/esphome/components/esp8266/preferences.cpp +++ b/esphome/components/esp8266/preferences.cpp @@ -33,6 +33,10 @@ static constexpr uint32_t MAX_PREFERENCE_WORDS = 255; #define ESP_RTC_USER_MEM ((uint32_t *) ESP_RTC_USER_MEM_START) +// Flash storage size depends on esp8266 -> restore_from_flash YAML option (default: false). +// When enabled (USE_ESP8266_PREFERENCES_FLASH), all preferences default to flash and need +// 128 words (512 bytes). When disabled, only explicit flash prefs use this storage so +// 64 words (256 bytes) suffices since most preferences go to RTC memory instead. #ifdef USE_ESP8266_PREFERENCES_FLASH static constexpr uint32_t ESP8266_FLASH_STORAGE_SIZE = 128; #else @@ -127,9 +131,11 @@ static bool load_from_rtc(size_t offset, uint32_t *data, size_t len) { return true; } -// Stack buffer size - 16 words total: up to 15 words of preference data + 1 word CRC (60 bytes of preference data) -// This handles virtually all real-world preferences without heap allocation -static constexpr size_t PREF_BUFFER_WORDS = 16; +// Maximum buffer for any single preference - bounded by storage sizes. +// Flash prefs: bounded by ESP8266_FLASH_STORAGE_SIZE (128 or 64 words). +// RTC prefs: bounded by RTC_NORMAL_REGION_WORDS (96) - a single pref can't span both RTC regions. +static constexpr size_t PREF_MAX_BUFFER_WORDS = + ESP8266_FLASH_STORAGE_SIZE > RTC_NORMAL_REGION_WORDS ? ESP8266_FLASH_STORAGE_SIZE : RTC_NORMAL_REGION_WORDS; class ESP8266PreferenceBackend : public ESPPreferenceBackend { public: @@ -141,15 +147,13 @@ class ESP8266PreferenceBackend : public ESPPreferenceBackend { bool save(const uint8_t *data, size_t len) override { if (bytes_to_words(len) != this->length_words) return false; - const size_t buffer_size = static_cast(this->length_words) + 1; - SmallBufferWithHeapFallback buffer_alloc(buffer_size); - uint32_t *buffer = buffer_alloc.get(); + if (buffer_size > PREF_MAX_BUFFER_WORDS) + return false; + uint32_t buffer[PREF_MAX_BUFFER_WORDS]; memset(buffer, 0, buffer_size * sizeof(uint32_t)); - memcpy(buffer, data, len); buffer[this->length_words] = calculate_crc(buffer, buffer + this->length_words, this->type); - return this->in_flash ? save_to_flash(this->offset, buffer, buffer_size) : save_to_rtc(this->offset, buffer, buffer_size); } @@ -157,19 +161,16 @@ class ESP8266PreferenceBackend : public ESPPreferenceBackend { bool load(uint8_t *data, size_t len) override { if (bytes_to_words(len) != this->length_words) return false; - const size_t buffer_size = static_cast(this->length_words) + 1; - SmallBufferWithHeapFallback buffer_alloc(buffer_size); - uint32_t *buffer = buffer_alloc.get(); - + if (buffer_size > PREF_MAX_BUFFER_WORDS) + return false; + uint32_t buffer[PREF_MAX_BUFFER_WORDS]; bool ret = this->in_flash ? load_from_flash(this->offset, buffer, buffer_size) : load_from_rtc(this->offset, buffer, buffer_size); if (!ret) return false; - if (buffer[this->length_words] != calculate_crc(buffer, buffer + this->length_words, this->type)) return false; - memcpy(data, buffer, len); return true; } diff --git a/esphome/components/rp2040/preferences.cpp b/esphome/components/rp2040/preferences.cpp index 172da32adc..fa72fd9a24 100644 --- a/esphome/components/rp2040/preferences.cpp +++ b/esphome/components/rp2040/preferences.cpp @@ -25,8 +25,8 @@ static uint8_t s_flash_storage[RP2040_FLASH_STORAGE_SIZE]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) static bool s_flash_dirty = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -// Stack buffer size for preferences - covers virtually all real-world preferences without heap allocation -static constexpr size_t PREF_BUFFER_SIZE = 64; +// No preference can exceed the total flash storage, so stack buffer covers all cases. +static constexpr size_t PREF_MAX_BUFFER_SIZE = RP2040_FLASH_STORAGE_SIZE; extern "C" uint8_t _EEPROM_start; @@ -46,14 +46,14 @@ class RP2040PreferenceBackend : public ESPPreferenceBackend { bool save(const uint8_t *data, size_t len) override { const size_t buffer_size = len + 1; - SmallBufferWithHeapFallback buffer_alloc(buffer_size); - uint8_t *buffer = buffer_alloc.get(); - + if (buffer_size > PREF_MAX_BUFFER_SIZE) + return false; + uint8_t buffer[PREF_MAX_BUFFER_SIZE]; memcpy(buffer, data, len); - buffer[len] = calculate_crc(buffer, buffer + len, type); + buffer[len] = calculate_crc(buffer, buffer + len, this->type); for (size_t i = 0; i < buffer_size; i++) { - uint32_t j = offset + i; + uint32_t j = this->offset + i; if (j >= RP2040_FLASH_STORAGE_SIZE) return false; uint8_t v = buffer[i]; @@ -66,17 +66,18 @@ class RP2040PreferenceBackend : public ESPPreferenceBackend { } bool load(uint8_t *data, size_t len) override { const size_t buffer_size = len + 1; - SmallBufferWithHeapFallback buffer_alloc(buffer_size); - uint8_t *buffer = buffer_alloc.get(); + if (buffer_size > PREF_MAX_BUFFER_SIZE) + return false; + uint8_t buffer[PREF_MAX_BUFFER_SIZE]; for (size_t i = 0; i < buffer_size; i++) { - uint32_t j = offset + i; + uint32_t j = this->offset + i; if (j >= RP2040_FLASH_STORAGE_SIZE) return false; buffer[i] = s_flash_storage[j]; } - uint8_t crc = calculate_crc(buffer, buffer + len, type); + uint8_t crc = calculate_crc(buffer, buffer + len, this->type); if (buffer[len] != crc) { return false; } diff --git a/esphome/components/rtttl/rtttl.cpp b/esphome/components/rtttl/rtttl.cpp index 5a64f37da4..6e86405b74 100644 --- a/esphome/components/rtttl/rtttl.cpp +++ b/esphome/components/rtttl/rtttl.cpp @@ -4,28 +4,72 @@ #include "esphome/core/log.h" #include "esphome/core/progmem.h" -namespace esphome { -namespace rtttl { +namespace esphome::rtttl { static const char *const TAG = "rtttl"; -static const uint32_t DOUBLE_NOTE_GAP_MS = 10; - // These values can also be found as constants in the Tone library (Tone.h) static const uint16_t NOTES[] = {0, 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951}; -static const uint16_t I2S_SPEED = 1000; +#if defined(USE_OUTPUT) || defined(USE_SPEAKER) +static const uint32_t DOUBLE_NOTE_GAP_MS = 10; +#endif // USE_OUTPUT || USE_SPEAKER -#undef HALF_PI -static const double HALF_PI = 1.5707963267948966192313216916398; +#ifdef USE_SPEAKER +static const size_t SAMPLE_BUFFER_SIZE = 2048; + +struct SpeakerSample { + int8_t left{0}; + int8_t right{0}; +}; inline double deg2rad(double degrees) { static const double PI_ON_180 = 4.0 * atan(1.0) / 180.0; return degrees * PI_ON_180; } +#endif // USE_SPEAKER + +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE +// RTTTL state strings indexed by State enum (0-4): STOPPED, INIT, STARTING, RUNNING, STOPPING, plus UNKNOWN fallback +PROGMEM_STRING_TABLE(RtttlStateStrings, "State::STOPPED", "State::INIT", "State::STARTING", "State::RUNNING", + "State::STOPPING", "UNKNOWN"); + +static const LogString *state_to_string(State state) { + return RtttlStateStrings::get_log_str(static_cast(state), RtttlStateStrings::LAST_INDEX); +} +#endif // ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE + +static uint8_t note_index_from_char(char note) { + switch (note) { + case 'c': + return 1; + // 'c#': 2 + case 'd': + return 3; + // 'd#': 4 + case 'e': + return 5; + case 'f': + return 6; + // 'f#': 7 + case 'g': + return 8; + // 'g#': 9 + case 'a': + return 10; + // 'a#': 11 + // Support both 'b' (English notation for B natural) and 'h' (German notation for B natural) + case 'b': + case 'h': + return 12; + case 'p': + default: + return 0; + } +} void Rtttl::dump_config() { ESP_LOGCONFIG(TAG, @@ -34,161 +78,34 @@ void Rtttl::dump_config() { this->gain_); } -void Rtttl::play(std::string rtttl) { - if (this->state_ != State::STATE_STOPPED && this->state_ != State::STATE_STOPPING) { - size_t pos = this->rtttl_.find(':'); - size_t len = (pos != std::string::npos) ? pos : this->rtttl_.length(); - ESP_LOGW(TAG, "Already playing: %.*s", (int) len, this->rtttl_.c_str()); - return; - } - - this->rtttl_ = std::move(rtttl); - - this->default_duration_ = 4; - this->default_octave_ = 6; - this->note_duration_ = 0; - - int bpm = 63; - uint8_t num; - - // Get name - this->position_ = this->rtttl_.find(':'); - - // it's somewhat documented to be up to 10 characters but let's be a bit flexible here - if (this->position_ == std::string::npos || this->position_ > 15) { - ESP_LOGE(TAG, "Unable to determine name; missing ':'"); - return; - } - - ESP_LOGD(TAG, "Playing song %.*s", (int) this->position_, this->rtttl_.c_str()); - - // get default duration - this->position_ = this->rtttl_.find("d=", this->position_); - if (this->position_ == std::string::npos) { - ESP_LOGE(TAG, "Missing 'd='"); - return; - } - this->position_ += 2; - num = this->get_integer_(); - if (num > 0) - this->default_duration_ = num; - - // get default octave - this->position_ = this->rtttl_.find("o=", this->position_); - if (this->position_ == std::string::npos) { - ESP_LOGE(TAG, "Missing 'o="); - return; - } - this->position_ += 2; - num = get_integer_(); - if (num >= 3 && num <= 7) - this->default_octave_ = num; - - // get BPM - this->position_ = this->rtttl_.find("b=", this->position_); - if (this->position_ == std::string::npos) { - ESP_LOGE(TAG, "Missing b="); - return; - } - this->position_ += 2; - num = get_integer_(); - if (num != 0) - bpm = num; - - this->position_ = this->rtttl_.find(':', this->position_); - if (this->position_ == std::string::npos) { - ESP_LOGE(TAG, "Missing second ':'"); - return; - } - this->position_++; - - // BPM usually expresses the number of quarter notes per minute - this->wholenote_ = 60 * 1000L * 4 / bpm; // this is the time for whole note (in milliseconds) - - this->output_freq_ = 0; - this->last_note_ = millis(); - this->note_duration_ = 1; - -#ifdef USE_SPEAKER - if (this->speaker_ != nullptr) { - this->set_state_(State::STATE_INIT); - this->samples_sent_ = 0; - this->samples_count_ = 0; - } -#endif -#ifdef USE_OUTPUT - if (this->output_ != nullptr) { - this->set_state_(State::STATE_RUNNING); - } -#endif -} - -void Rtttl::stop() { -#ifdef USE_OUTPUT - if (this->output_ != nullptr) { - this->output_->set_level(0.0); - this->set_state_(STATE_STOPPED); - } -#endif -#ifdef USE_SPEAKER - if (this->speaker_ != nullptr) { - if (this->speaker_->is_running()) { - this->speaker_->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->state_ == State::STATE_STOPPED) { + if (this->state_ == State::STOPPED) { this->disable_loop(); return; } +#ifdef USE_OUTPUT + if (this->output_ != nullptr && millis() - this->last_note_ < this->note_duration_) { + return; + } +#endif // USE_OUTPUT + #ifdef USE_SPEAKER if (this->speaker_ != nullptr) { - if (this->state_ == State::STATE_STOPPING) { + if (this->state_ == State::STOPPING) { if (this->speaker_->is_stopped()) { - this->set_state_(State::STATE_STOPPED); + this->set_state_(State::STOPPED); } else { return; } - } else if (this->state_ == State::STATE_INIT) { + } else if (this->state_ == State::INIT) { if (this->speaker_->is_stopped()) { this->speaker_->start(); - this->set_state_(State::STATE_STARTING); + this->set_state_(State::STARTING); } - } else if (this->state_ == State::STATE_STARTING) { + } else if (this->state_ == State::STARTING) { if (this->speaker_->is_running()) { - this->set_state_(State::STATE_RUNNING); + this->set_state_(State::RUNNING); } } if (!this->speaker_->is_running()) { @@ -230,19 +147,17 @@ void Rtttl::loop() { } } } -#endif -#ifdef USE_OUTPUT - if (this->output_ != nullptr && millis() - this->last_note_ < this->note_duration_) - return; -#endif +#endif // USE_SPEAKER + if (this->position_ >= this->rtttl_.length()) { this->finish_(); return; } // align to note: most rtttl's out there does not add and space after the ',' separator but just in case... - while (this->rtttl_[this->position_] == ',' || this->rtttl_[this->position_] == ' ') + while (this->rtttl_[this->position_] == ',' || this->rtttl_[this->position_] == ' ') { this->position_++; + } // first, get note duration, if available uint8_t num = this->get_integer_(); @@ -254,35 +169,8 @@ void Rtttl::loop() { this->wholenote_ / this->default_duration_; // we will need to check if we are a dotted note after } - uint8_t note; + uint8_t note = note_index_from_char(this->rtttl_[this->position_]); - switch (this->rtttl_[this->position_]) { - case 'c': - note = 1; - break; - case 'd': - note = 3; - break; - case 'e': - note = 5; - break; - case 'f': - note = 6; - break; - case 'g': - note = 8; - break; - case 'a': - note = 10; - break; - case 'h': - case 'b': - note = 12; - break; - case 'p': - default: - note = 0; - } this->position_++; // now, get optional '#' sharp @@ -292,7 +180,7 @@ void Rtttl::loop() { } // now, get scale - uint8_t scale = get_integer_(); + uint8_t scale = this->get_integer_(); if (scale == 0) { scale = this->default_octave_; } @@ -345,7 +233,8 @@ void Rtttl::loop() { this->output_->set_level(0.0); } } -#endif +#endif // USE_OUTPUT + #ifdef USE_SPEAKER if (this->speaker_ != nullptr) { this->samples_sent_ = 0; @@ -370,20 +259,152 @@ void Rtttl::loop() { } // Convert from frequency in Hz to high and low samples in fixed point } -#endif +#endif // USE_SPEAKER this->last_note_ = millis(); } -#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE -// RTTTL state strings indexed by State enum (0-4): STOPPED, INIT, STARTING, RUNNING, STOPPING, plus UNKNOWN fallback -PROGMEM_STRING_TABLE(RtttlStateStrings, "STATE_STOPPED", "STATE_INIT", "STATE_STARTING", "STATE_RUNNING", - "STATE_STOPPING", "UNKNOWN"); +void Rtttl::play(std::string rtttl) { + if (this->state_ != State::STOPPED && this->state_ != State::STOPPING) { + size_t pos = this->rtttl_.find(':'); + size_t len = (pos != std::string::npos) ? pos : this->rtttl_.length(); + ESP_LOGW(TAG, "Already playing: %.*s", (int) len, this->rtttl_.c_str()); + return; + } -static const LogString *state_to_string(State state) { - return RtttlStateStrings::get_log_str(static_cast(state), RtttlStateStrings::LAST_INDEX); + this->rtttl_ = std::move(rtttl); + + this->default_duration_ = 4; + this->default_octave_ = 6; + this->note_duration_ = 0; + + int bpm = 63; + uint8_t num; + + // Get name + this->position_ = this->rtttl_.find(':'); + + // it's somewhat documented to be up to 10 characters but let's be a bit flexible here + if (this->position_ == std::string::npos || this->position_ > 15) { + ESP_LOGE(TAG, "Unable to determine name; missing ':'"); + return; + } + + ESP_LOGD(TAG, "Playing song %.*s", (int) this->position_, this->rtttl_.c_str()); + + // get default duration + this->position_ = this->rtttl_.find("d=", this->position_); + if (this->position_ == std::string::npos) { + ESP_LOGE(TAG, "Missing 'd='"); + return; + } + this->position_ += 2; + num = this->get_integer_(); + if (num > 0) { + this->default_duration_ = num; + } + + // get default octave + this->position_ = this->rtttl_.find("o=", this->position_); + if (this->position_ == std::string::npos) { + ESP_LOGE(TAG, "Missing 'o="); + return; + } + this->position_ += 2; + num = this->get_integer_(); + if (num >= 3 && num <= 7) { + this->default_octave_ = num; + } + + // get BPM + this->position_ = this->rtttl_.find("b=", this->position_); + if (this->position_ == std::string::npos) { + ESP_LOGE(TAG, "Missing b="); + return; + } + this->position_ += 2; + num = this->get_integer_(); + if (num != 0) { + bpm = num; + } + + this->position_ = this->rtttl_.find(':', this->position_); + if (this->position_ == std::string::npos) { + ESP_LOGE(TAG, "Missing second ':'"); + return; + } + this->position_++; + + // BPM usually expresses the number of quarter notes per minute + this->wholenote_ = 60 * 1000L * 4 / bpm; // this is the time for whole note (in milliseconds) + + this->output_freq_ = 0; + this->last_note_ = millis(); + this->note_duration_ = 1; + +#ifdef USE_OUTPUT + if (this->output_ != nullptr) { + this->set_state_(State::RUNNING); + } +#endif // USE_OUTPUT + +#ifdef USE_SPEAKER + if (this->speaker_ != nullptr) { + this->set_state_(State::INIT); + this->samples_sent_ = 0; + this->samples_count_ = 0; + } +#endif // USE_SPEAKER +} + +void Rtttl::stop() { +#ifdef USE_OUTPUT + if (this->output_ != nullptr) { + this->output_->set_level(0.0); + this->set_state_(State::STOPPED); + } +#endif // USE_OUTPUT + +#ifdef USE_SPEAKER + if (this->speaker_ != nullptr) { + if (this->speaker_->is_running()) { + this->speaker_->stop(); + } + this->set_state_(State::STOPPING); + } +#endif // USE_SPEAKER + + 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::STOPPED); + } +#endif // USE_OUTPUT + +#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::STOPPING); + } +#endif // USE_SPEAKER + + // Ensure no more notes are played in case finish_() is called for an error. + this->position_ = this->rtttl_.length(); + this->note_duration_ = 0; } -#endif void Rtttl::set_state_(State state) { State old_state = this->state_; @@ -391,15 +412,14 @@ void Rtttl::set_state_(State state) { ESP_LOGV(TAG, "State changed from %s to %s", LOG_STR_ARG(state_to_string(old_state)), LOG_STR_ARG(state_to_string(state))); - // Clear loop_done when transitioning from STOPPED to any other state - if (state == State::STATE_STOPPED) { + // Clear loop_done when transitioning from `State::STOPPED` to any other state + if (state == State::STOPPED) { this->disable_loop(); this->on_finished_playback_callback_.call(); ESP_LOGD(TAG, "Playback finished"); - } else if (old_state == State::STATE_STOPPED) { + } else if (old_state == State::STOPPED) { this->enable_loop(); } } -} // namespace rtttl -} // namespace esphome +} // namespace esphome::rtttl diff --git a/esphome/components/rtttl/rtttl.h b/esphome/components/rtttl/rtttl.h index 1e924a897c..6f5df07766 100644 --- a/esphome/components/rtttl/rtttl.h +++ b/esphome/components/rtttl/rtttl.h @@ -5,48 +5,41 @@ #ifdef USE_OUTPUT #include "esphome/components/output/float_output.h" -#endif +#endif // USE_OUTPUT #ifdef USE_SPEAKER #include "esphome/components/speaker/speaker.h" -#endif +#endif // USE_SPEAKER -namespace esphome { -namespace rtttl { +namespace esphome::rtttl { -enum State : uint8_t { - STATE_STOPPED = 0, - STATE_INIT, - STATE_STARTING, - STATE_RUNNING, - STATE_STOPPING, +enum class State : uint8_t { + STOPPED = 0, + INIT, + STARTING, + RUNNING, + STOPPING, }; -#ifdef USE_SPEAKER -static const size_t SAMPLE_BUFFER_SIZE = 2048; - -struct SpeakerSample { - int8_t left{0}; - int8_t right{0}; -}; -#endif - class Rtttl : public Component { public: #ifdef USE_OUTPUT void set_output(output::FloatOutput *output) { this->output_ = output; } -#endif +#endif // USE_OUTPUT + #ifdef USE_SPEAKER void set_speaker(speaker::Speaker *speaker) { this->speaker_ = speaker; } -#endif - float get_gain() { return gain_; } - void set_gain(float gain) { this->gain_ = clamp(gain, 0.0f, 1.0f); } +#endif // USE_SPEAKER + + void dump_config() override; + void loop() override; void play(std::string rtttl); void stop(); - void dump_config() override; - bool is_playing() { return this->state_ != State::STATE_STOPPED; } - void loop() override; + float get_gain() { return this->gain_; } + void set_gain(float gain) { this->gain_ = clamp(gain, 0.0f, 1.0f); } + + bool is_playing() { return this->state_ != State::STOPPED; } void add_on_finished_playback_callback(std::function callback) { this->on_finished_playback_callback_.add(std::move(callback)); @@ -90,12 +83,12 @@ class Rtttl : public Component { /// The gain of the output. float gain_{0.6f}; /// The current state of the RTTTL player. - State state_{State::STATE_STOPPED}; + State state_{State::STOPPED}; #ifdef USE_OUTPUT /// The output to write the sound to. output::FloatOutput *output_; -#endif +#endif // USE_OUTPUT #ifdef USE_SPEAKER /// The speaker to write the sound to. @@ -110,8 +103,7 @@ class Rtttl : public Component { int samples_count_{0}; /// The number of samples for the gap between notes. int samples_gap_{0}; - -#endif +#endif // USE_SPEAKER /// The callback to call when playback is finished. CallbackManager on_finished_playback_callback_; @@ -145,5 +137,4 @@ class FinishedPlaybackTrigger : public Trigger<> { } }; -} // namespace rtttl -} // namespace esphome +} // namespace esphome::rtttl