1
0
mirror of https://github.com/esphome/esphome.git synced 2025-02-22 12:58:15 +00:00

Add RTTTL volume control. (#5968)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
NP v/d Spek 2024-02-27 23:31:33 +01:00 committed by GitHub
parent 37138d4f28
commit c43c9ad1c5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 32 additions and 15 deletions

View File

@ -12,6 +12,7 @@ from esphome.const import (
CONF_PLATFORM, CONF_PLATFORM,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_SPEAKER, CONF_SPEAKER,
CONF_GAIN,
) )
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -38,6 +39,7 @@ CONFIG_SCHEMA = cv.All(
cv.GenerateID(CONF_ID): cv.declare_id(Rtttl), cv.GenerateID(CONF_ID): cv.declare_id(Rtttl),
cv.Optional(CONF_OUTPUT): cv.use_id(FloatOutput), cv.Optional(CONF_OUTPUT): cv.use_id(FloatOutput),
cv.Optional(CONF_SPEAKER): cv.use_id(Speaker), cv.Optional(CONF_SPEAKER): cv.use_id(Speaker),
cv.Optional(CONF_GAIN, default="0.6"): cv.percentage,
cv.Optional(CONF_ON_FINISHED_PLAYBACK): automation.validate_automation( cv.Optional(CONF_ON_FINISHED_PLAYBACK): automation.validate_automation(
{ {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
@ -98,6 +100,8 @@ async def to_code(config):
out = await cg.get_variable(config[CONF_SPEAKER]) out = await cg.get_variable(config[CONF_SPEAKER])
cg.add(var.set_speaker(out)) cg.add(var.set_speaker(out))
cg.add(var.set_gain(config[CONF_GAIN]))
for conf in config.get(CONF_ON_FINISHED_PLAYBACK, []): for conf in config.get(CONF_ON_FINISHED_PLAYBACK, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf) await automation.build_automation(trigger, [], conf)

View File

@ -16,7 +16,7 @@ static const uint16_t NOTES[] = {0, 262, 277, 294, 311, 330, 349, 370,
1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 2093, 2217, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 2093, 2217,
2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951}; 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951};
static const uint16_t I2S_SPEED = 1600; static const uint16_t I2S_SPEED = 1000;
#undef HALF_PI #undef HALF_PI
static const double HALF_PI = 1.5707963267948966192313216916398; static const double HALF_PI = 1.5707963267948966192313216916398;
@ -136,7 +136,7 @@ void Rtttl::loop() {
if (this->samples_per_wave_ != 0 && this->samples_sent_ >= this->samples_gap_) { // Play note// if (this->samples_per_wave_ != 0 && this->samples_sent_ >= this->samples_gap_) { // Play note//
rem = ((this->samples_sent_ << 10) % this->samples_per_wave_) * (360.0 / this->samples_per_wave_); rem = ((this->samples_sent_ << 10) % this->samples_per_wave_) * (360.0 / this->samples_per_wave_);
int16_t val = 8192 * sin(deg2rad(rem)); int16_t val = (49152 * this->gain_) * sin(deg2rad(rem));
sample[x].left = val; sample[x].left = val;
sample[x].right = val; sample[x].right = val;
@ -269,7 +269,7 @@ void Rtttl::loop() {
} }
if (this->output_freq_ != 0) { if (this->output_freq_ != 0) {
this->output_->update_frequency(this->output_freq_); this->output_->update_frequency(this->output_freq_);
this->output_->set_level(0.5); this->output_->set_level(this->gain_);
} else { } else {
this->output_->set_level(0.0); this->output_->set_level(0.0);
} }
@ -278,18 +278,23 @@ void Rtttl::loop() {
#ifdef USE_SPEAKER #ifdef USE_SPEAKER
if (this->speaker_ != nullptr) { if (this->speaker_ != nullptr) {
this->samples_sent_ = 0; this->samples_sent_ = 0;
this->samples_count_ = (this->sample_rate_ * this->note_duration_) / I2S_SPEED; this->samples_gap_ = 0;
// Convert from frequency in Hz to high and low samples in fixed point this->samples_per_wave_ = 0;
this->samples_count_ = (this->sample_rate_ * this->note_duration_) / 1600; //(ms);
if (need_note_gap) {
this->samples_gap_ = (this->sample_rate_ * DOUBLE_NOTE_GAP_MS) / 1600; //(ms);
}
if (this->output_freq_ != 0) { if (this->output_freq_ != 0) {
this->samples_per_wave_ = (this->sample_rate_ << 10) / this->output_freq_; this->samples_per_wave_ = (this->sample_rate_ << 10) / this->output_freq_;
} else {
this->samples_per_wave_ = 0; // make sure there is enough samples to add a full last sinus.
} uint16_t division = ((this->samples_count_ << 10) / this->samples_per_wave_) + 1;
if (need_note_gap) { uint16_t x = this->samples_count_;
this->samples_gap_ = (this->sample_rate_ * DOUBLE_NOTE_GAP_MS) / I2S_SPEED; this->samples_count_ = (division * this->samples_per_wave_);
} else { ESP_LOGD(TAG, "play time old: %d div: %d new: %d %d", x, division, this->samples_count_, this->samples_per_wave_);
this->samples_gap_ = 0; this->samples_count_ = this->samples_count_ >> 10;
} }
// Convert from frequency in Hz to high and low samples in fixed point
} }
#endif #endif

View File

@ -15,7 +15,7 @@ namespace esphome {
namespace rtttl { namespace rtttl {
#ifdef USE_SPEAKER #ifdef USE_SPEAKER
static const size_t SAMPLE_BUFFER_SIZE = 256; static const size_t SAMPLE_BUFFER_SIZE = 512;
struct SpeakerSample { struct SpeakerSample {
int16_t left{0}; int16_t left{0};
@ -31,6 +31,13 @@ class Rtttl : public Component {
#ifdef USE_SPEAKER #ifdef USE_SPEAKER
void set_speaker(speaker::Speaker *speaker) { this->speaker_ = speaker; } void set_speaker(speaker::Speaker *speaker) { this->speaker_ = speaker; }
#endif #endif
void set_gain(float gain) {
if (gain < 0.1f)
gain = 0.1f;
if (gain > 1.0f)
gain = 1.0f;
this->gain_ = gain;
}
void play(std::string rtttl); void play(std::string rtttl);
void stop(); void stop();
void dump_config() override; void dump_config() override;
@ -60,6 +67,7 @@ class Rtttl : public Component {
uint16_t note_duration_; uint16_t note_duration_;
uint32_t output_freq_; uint32_t output_freq_;
float gain_{0.6f};
#ifdef USE_OUTPUT #ifdef USE_OUTPUT
output::FloatOutput *output_; output::FloatOutput *output_;
@ -68,13 +76,13 @@ class Rtttl : public Component {
void play_output_(); void play_output_();
#ifdef USE_SPEAKER #ifdef USE_SPEAKER
speaker::Speaker *speaker_; speaker::Speaker *speaker_{nullptr};
void play_speaker_();
int sample_rate_{16000}; int sample_rate_{16000};
int samples_per_wave_{0}; int samples_per_wave_{0};
int samples_sent_{0}; int samples_sent_{0};
int samples_count_{0}; int samples_count_{0};
int samples_gap_{0}; int samples_gap_{0};
#endif #endif
CallbackManager<void()> on_finished_playback_callback_; CallbackManager<void()> on_finished_playback_callback_;