[speaker][speaker_source] Make a volume of zero silent with an audio DAC (#19305)

This commit is contained in:
Kevin Ahrendt
2026-09-14 16:46:30 -05:00
committed by GitHub
parent 328077c4f8
commit 2472838e13
4 changed files with 28 additions and 12 deletions
@@ -177,7 +177,7 @@ void I2SAudioSpeakerBase::post_software_gain_(uint32_t rate_samples) {
#endif // USE_AUDIO_DAC
// Software volume control. The ramp treats 0 dB as unity and skips processing there.
float target_db;
if (this->mute_state_ || this->volume_ <= 0.0f) {
if (this->is_silent_()) {
target_db = -INFINITY;
} else if (this->volume_ >= 1.0f) {
target_db = 0.0f;
@@ -612,7 +612,7 @@ void SpeakerMediaPlayer::set_volume_(float volume, bool publish) {
}
// Turn on the mute state if the volume is effectively zero, off otherwise
if (volume < 0.001f) {
if (volume < speaker::SILENT_VOLUME_THRESHOLD) {
this->set_mute_state_(true);
} else {
this->set_mute_state_(false);
+25 -9
View File
@@ -18,6 +18,9 @@
namespace esphome::speaker {
/// Volumes below this are treated as zero
static constexpr float SILENT_VOLUME_THRESHOLD = 0.001f;
enum State : uint8_t {
STATE_STOPPED = 0,
STATE_STARTING,
@@ -65,13 +68,15 @@ class Speaker {
bool is_running() const { return this->state_ == STATE_RUNNING; }
bool is_stopped() const { return this->state_ == STATE_STOPPED; }
// Volume control is handled by a configured audio dac component. Individual speaker components can
// override and implement in software if an audio dac isn't available.
// Volume and mute are independent: changing one never alters the other's stored state. Volume control is
// handled by a configured audio dac component. Individual speaker components can override and implement in
// software if an audio dac isn't available.
virtual void set_volume(float volume) {
this->volume_ = volume;
#ifdef USE_AUDIO_DAC
if (this->audio_dac_ != nullptr) {
this->audio_dac_->set_volume(volume);
this->apply_audio_dac_mute_();
}
#endif
};
@@ -80,13 +85,7 @@ class Speaker {
virtual void set_mute_state(bool mute_state) {
this->mute_state_ = mute_state;
#ifdef USE_AUDIO_DAC
if (this->audio_dac_) {
if (mute_state) {
this->audio_dac_->set_mute_on();
} else {
this->audio_dac_->set_mute_off();
}
}
this->apply_audio_dac_mute_();
#endif
}
virtual bool get_mute_state() { return this->mute_state_; }
@@ -110,6 +109,23 @@ class Speaker {
}
protected:
/// @brief Whether the output should be silent: muted, or the volume is effectively zero.
/// Volume steps from media players can leave a positive value near float epsilon instead of exactly zero.
bool is_silent_() const { return this->mute_state_ || this->volume_ < SILENT_VOLUME_THRESHOLD; }
#ifdef USE_AUDIO_DAC
/// @brief Uses the audio dac's mute as the silence mechanism, since a dac's minimum volume is often audible.
void apply_audio_dac_mute_() {
if (this->audio_dac_ == nullptr)
return;
if (this->is_silent_()) {
this->audio_dac_->set_mute_on();
} else {
this->audio_dac_->set_mute_off();
}
}
#endif
State state_{STATE_STOPPED};
audio::AudioStreamInfo audio_stream_info_;
float volume_{1.0f};
@@ -831,7 +831,7 @@ void SpeakerSourceMediaPlayer::set_volume_(float volume, bool publish) {
// Turn on the mute state if the volume is effectively zero, off otherwise.
// Pass publish=false to avoid saving twice.
if (volume < 0.001f) {
if (volume < speaker::SILENT_VOLUME_THRESHOLD) {
this->set_mute_state_(true, false);
} else {
this->set_mute_state_(false, false);