From 84740dfed0ad4dc2d5f6fd1133196ebe5243ccc7 Mon Sep 17 00:00:00 2001 From: Stas Sl Date: Tue, 8 Oct 2024 00:47:45 +0300 Subject: [PATCH] flat config --- components/sound_level_meter/__init__.py | 244 ++++++++++-------- .../sound_level_meter/sound_level_meter.cpp | 120 ++++----- .../sound_level_meter/sound_level_meter.h | 48 ++-- 3 files changed, 211 insertions(+), 201 deletions(-) diff --git a/components/sound_level_meter/__init__.py b/components/sound_level_meter/__init__.py index 2bfc7be..4f2f6da 100644 --- a/components/sound_level_meter/__init__.py +++ b/components/sound_level_meter/__init__.py @@ -2,18 +2,17 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome import automation +from esphome import automation, core from esphome.automation import maybe_simple_id from esphome.components import sensor, i2s from esphome.const import ( CONF_ID, CONF_SENSORS, - CONF_FILTERS, CONF_WINDOW_SIZE, CONF_UPDATE_INTERVAL, CONF_TYPE, UNIT_DECIBEL, - STATE_CLASS_MEASUREMENT + STATE_CLASS_MEASUREMENT, ) CODEOWNERS = ["@stas-sl"] @@ -23,13 +22,21 @@ sound_level_meter_ns = cg.esphome_ns.namespace("sound_level_meter") SoundLevelMeter = sound_level_meter_ns.class_("SoundLevelMeter", cg.Component) -SoundLevelMeterSensor = sound_level_meter_ns.class_("SoundLevelMeterSensor", sensor.Sensor) -SoundLevelMeterSensorEq = sound_level_meter_ns.class_("SoundLevelMeterSensorEq", SoundLevelMeterSensor, sensor.Sensor) -SoundLevelMeterSensorMax = sound_level_meter_ns.class_("SoundLevelMeterSensorMax", SoundLevelMeterSensor, sensor.Sensor) -SoundLevelMeterSensorMin = sound_level_meter_ns.class_("SoundLevelMeterSensorMin", SoundLevelMeterSensor, sensor.Sensor) +SoundLevelMeterSensor = sound_level_meter_ns.class_( + "SoundLevelMeterSensor", sensor.Sensor +) +SoundLevelMeterSensorEq = sound_level_meter_ns.class_( + "SoundLevelMeterSensorEq", SoundLevelMeterSensor, sensor.Sensor +) +SoundLevelMeterSensorMax = sound_level_meter_ns.class_( + "SoundLevelMeterSensorMax", SoundLevelMeterSensor, sensor.Sensor +) +SoundLevelMeterSensorMin = sound_level_meter_ns.class_( + "SoundLevelMeterSensorMin", SoundLevelMeterSensor, sensor.Sensor +) SoundLevelMeterSensorPeak = sound_level_meter_ns.class_( - "SoundLevelMeterSensorPeak", SoundLevelMeterSensor, sensor.Sensor) -SensorGroup = sound_level_meter_ns.class_("SensorGroup") + "SoundLevelMeterSensorPeak", SoundLevelMeterSensor, sensor.Sensor +) Filter = sound_level_meter_ns.class_("Filter") SOS_Filter = sound_level_meter_ns.class_("SOS_Filter", Filter) ToggleAction = sound_level_meter_ns.class_("ToggleAction", automation.Action) @@ -38,7 +45,6 @@ CONF_I2S_ID = "i2s_id" -CONF_GROUPS = "groups" CONF_EQ = "eq" CONF_MAX = "max" CONF_MIN = "min" @@ -54,9 +60,25 @@ CONF_MIC_SENSITIVITY_REF = "mic_sensitivity_ref" CONF_OFFSET = "offset" CONF_IS_ON = "is_on" +CONF_DSP_FILTERS = "dsp_filters" ICON_WAVEFORM = "mdi:waveform" +CONFIG_DSP_FILTER_SCHEMA = cv.typed_schema( + { + CONF_SOS: cv.Schema( + { + cv.GenerateID(): cv.declare_id(SOS_Filter), + cv.Required(CONF_COEFFS): [[cv.float_]], + } + ) + } +) + +CONFIG_SENSOR_DSP_FILTER_SCHEMA = cv.ensure_list( + cv.Any(cv.use_id(Filter), CONFIG_DSP_FILTER_SCHEMA) +) + CONFIG_SENSOR_SCHEMA = cv.typed_schema( { CONF_EQ: sensor.sensor_schema( @@ -64,111 +86,120 @@ unit_of_measurement=UNIT_DECIBEL, accuracy_decimals=2, state_class=STATE_CLASS_MEASUREMENT, - icon=ICON_WAVEFORM - ).extend({ - cv.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds - }), + icon=ICON_WAVEFORM, + ).extend( + { + cv.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, + cv.Optional( + CONF_DSP_FILTERS, default=[] + ): CONFIG_SENSOR_DSP_FILTER_SCHEMA, + } + ), CONF_MAX: sensor.sensor_schema( SoundLevelMeterSensorMax, unit_of_measurement=UNIT_DECIBEL, accuracy_decimals=2, state_class=STATE_CLASS_MEASUREMENT, - icon=ICON_WAVEFORM - ).extend({ - cv.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, - cv.Required(CONF_WINDOW_SIZE): cv.positive_time_period_milliseconds - }), + icon=ICON_WAVEFORM, + ).extend( + { + cv.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, + cv.Required(CONF_WINDOW_SIZE): cv.positive_time_period_milliseconds, + cv.Optional( + CONF_DSP_FILTERS, default=[] + ): CONFIG_SENSOR_DSP_FILTER_SCHEMA, + } + ), CONF_MIN: sensor.sensor_schema( SoundLevelMeterSensorMin, unit_of_measurement=UNIT_DECIBEL, accuracy_decimals=2, state_class=STATE_CLASS_MEASUREMENT, - icon=ICON_WAVEFORM - ).extend({ - cv.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, - cv.Required(CONF_WINDOW_SIZE): cv.positive_time_period_milliseconds - }), + icon=ICON_WAVEFORM, + ).extend( + { + cv.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, + cv.Required(CONF_WINDOW_SIZE): cv.positive_time_period_milliseconds, + cv.Optional( + CONF_DSP_FILTERS, default=[] + ): CONFIG_SENSOR_DSP_FILTER_SCHEMA, + } + ), CONF_PEAK: sensor.sensor_schema( SoundLevelMeterSensorPeak, unit_of_measurement=UNIT_DECIBEL, accuracy_decimals=2, state_class=STATE_CLASS_MEASUREMENT, - icon=ICON_WAVEFORM - ).extend({ - cv.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds - }) + icon=ICON_WAVEFORM, + ).extend( + { + cv.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_milliseconds, + cv.Optional( + CONF_DSP_FILTERS, default=[] + ): CONFIG_SENSOR_DSP_FILTER_SCHEMA, + } + ), } ) -CONFIG_FILTER_SCHEMA = cv.typed_schema( +CONFIG_SCHEMA = cv.Schema( { - CONF_SOS: cv.Schema({ - cv.GenerateID(): cv.declare_id(SOS_Filter), - cv.Required(CONF_COEFFS): [[cv.float_]] - }) + cv.GenerateID(): cv.declare_id(SoundLevelMeter), + cv.GenerateID(CONF_I2S_ID): cv.use_id(i2s.I2SComponent), + cv.Optional( + CONF_UPDATE_INTERVAL, default="60s" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_IS_ON, default=True): cv.boolean, + cv.Optional(CONF_BUFFER_SIZE, default=1024): cv.positive_not_null_int, + cv.Optional( + CONF_WARMUP_INTERVAL, default="500ms" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_TASK_STACK_SIZE, default=4096): cv.positive_not_null_int, + cv.Optional(CONF_TASK_PRIORITY, default=2): cv.uint8_t, + cv.Optional(CONF_TASK_CORE, default=1): cv.int_range(0, 1), + cv.Optional(CONF_MIC_SENSITIVITY): cv.decibel, + cv.Optional(CONF_MIC_SENSITIVITY_REF): cv.decibel, + cv.Optional(CONF_OFFSET): cv.decibel, + cv.Optional(CONF_DSP_FILTERS, default=[]): [CONFIG_DSP_FILTER_SCHEMA], + cv.Optional(CONF_SENSORS, default=[]): [CONFIG_SENSOR_SCHEMA], } +).extend(cv.COMPONENT_SCHEMA) + +SOUND_LEVEL_METER_ACTION_SCHEMA = maybe_simple_id( + {cv.GenerateID(): cv.use_id(SoundLevelMeter)} ) -def config_group_schema(value): - return CONFIG_GROUP_SCHEMA(value) -CONFIG_GROUP_SCHEMA = ( - cv.Schema({ - cv.GenerateID(): cv.declare_id(SensorGroup), - cv.Optional(CONF_FILTERS): [CONFIG_FILTER_SCHEMA], - cv.Optional(CONF_SENSORS): [CONFIG_SENSOR_SCHEMA], - cv.Optional(CONF_GROUPS): [config_group_schema] - }) -) +async def add_dsp_filter(config, parent): + f = None + if config[CONF_TYPE] == CONF_SOS: + f = cg.new_Pvariable(config[CONF_ID], config[CONF_COEFFS]) + assert f is not None + cg.add(parent.add_dsp_filter(f)) + return f -CONFIG_SCHEMA = ( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(SoundLevelMeter), - cv.GenerateID(CONF_I2S_ID): cv.use_id(i2s.I2SComponent), - cv.Optional(CONF_UPDATE_INTERVAL, default="60s"): cv.positive_time_period_milliseconds, - cv.Optional(CONF_IS_ON, default=True): cv.boolean, - cv.Optional(CONF_BUFFER_SIZE, default=1024): cv.positive_not_null_int, - cv.Optional(CONF_WARMUP_INTERVAL, default="500ms"): cv.positive_time_period_milliseconds, - cv.Optional(CONF_TASK_STACK_SIZE, default=4096): cv.positive_not_null_int, - cv.Optional(CONF_TASK_PRIORITY, default=2): cv.uint8_t, - cv.Optional(CONF_TASK_CORE, default=1): cv.int_range(0, 1), - cv.Optional(CONF_MIC_SENSITIVITY): cv.decibel, - cv.Optional(CONF_MIC_SENSITIVITY_REF): cv.decibel, - cv.Optional(CONF_OFFSET): cv.decibel, - cv.Required(CONF_GROUPS): [CONFIG_GROUP_SCHEMA] - } - ) - .extend(cv.COMPONENT_SCHEMA) -) -SOUND_LEVEL_METER_ACTION_SCHEMA = maybe_simple_id({ - cv.GenerateID(): cv.use_id(SoundLevelMeter) -}) - -async def groups_to_code(config, component, parent): - for gc in config: - g = cg.new_Pvariable(gc[CONF_ID]) - cg.add(g.set_parent(component)) - cg.add(parent.add_group(g)) - if CONF_FILTERS in gc: - for fc in gc[CONF_FILTERS]: - f = None - if fc[CONF_TYPE] == CONF_SOS: - f = cg.new_Pvariable(fc[CONF_ID], fc[CONF_COEFFS]) - if f is not None: - cg.add(g.add_filter(f)) - if CONF_GROUPS in gc: - await groups_to_code(gc[CONF_GROUPS], component, g) - if CONF_SENSORS in gc: - for sc in gc[CONF_SENSORS]: - s = await sensor.new_sensor(sc) - cg.add(s.set_parent(component)) - if CONF_WINDOW_SIZE in sc: - cg.add(s.set_window_size(sc[CONF_WINDOW_SIZE])) - if CONF_UPDATE_INTERVAL in sc: - cg.add(s.set_update_interval(sc[CONF_UPDATE_INTERVAL])) - cg.add(g.add_sensor(s)) +async def add_sensor(config, parent): + s = await sensor.new_sensor(config) + cg.add(s.set_parent(parent)) + if CONF_WINDOW_SIZE in config: + cg.add(s.set_window_size(config[CONF_WINDOW_SIZE])) + if CONF_UPDATE_INTERVAL in config: + cg.add(s.set_update_interval(config[CONF_UPDATE_INTERVAL])) + for fc in config[CONF_DSP_FILTERS]: + f = None + if isinstance(fc, core.ID): + f = await cg.get_variable(fc) + elif isinstance(fc, dict): + f = await add_dsp_filter(fc, parent) + assert f is not None + cg.add(s.add_dsp_filter(f)) + cg.add(parent.add_sensor(s)) + + +def get_max_buffers(config): + return max(len(sc[CONF_DSP_FILTERS]) for sc in config[CONF_SENSORS]) + 1 + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) @@ -181,7 +212,7 @@ async def to_code(config): cg.add(var.set_task_stack_size(config[CONF_TASK_STACK_SIZE])) cg.add(var.set_task_priority(config[CONF_TASK_PRIORITY])) cg.add(var.set_task_core(config[CONF_TASK_CORE])) - cg.add(var.set_max_groups_depth(get_groups_depth(config[CONF_GROUPS]) + 1)) + cg.add(var.set_max_buffers(get_max_buffers(config))) if CONF_MIC_SENSITIVITY in config: cg.add(var.set_mic_sensitivity(config[CONF_MIC_SENSITIVITY])) if CONF_MIC_SENSITIVITY_REF in config: @@ -190,20 +221,23 @@ async def to_code(config): cg.add(var.set_offset(config[CONF_OFFSET])) if not config[CONF_IS_ON]: cg.add(var.turn_off()) - await groups_to_code(config[CONF_GROUPS], var, var) - -def get_groups_depth(cur): - max_d = 0 - for g in cur: - d = int(CONF_FILTERS in g) - if CONF_GROUPS in g: - d += get_groups_depth(g[CONF_GROUPS]) - max_d = max(d, max_d) - return max_d - -@automation.register_action("sound_level_meter.toggle", ToggleAction, SOUND_LEVEL_METER_ACTION_SCHEMA) -@automation.register_action("sound_level_meter.turn_off", TurnOffAction, SOUND_LEVEL_METER_ACTION_SCHEMA) -@automation.register_action("sound_level_meter.turn_on", TurnOnAction, SOUND_LEVEL_METER_ACTION_SCHEMA) + + for fc in config[CONF_DSP_FILTERS]: + await add_dsp_filter(fc, var) + + for sc in config[CONF_SENSORS]: + await add_sensor(sc, var) + + +@automation.register_action( + "sound_level_meter.toggle", ToggleAction, SOUND_LEVEL_METER_ACTION_SCHEMA +) +@automation.register_action( + "sound_level_meter.turn_off", TurnOffAction, SOUND_LEVEL_METER_ACTION_SCHEMA +) +@automation.register_action( + "sound_level_meter.turn_on", TurnOnAction, SOUND_LEVEL_METER_ACTION_SCHEMA +) async def switch_toggle_to_code(config, action_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) return cg.new_Pvariable(action_id, template_arg, paren) diff --git a/components/sound_level_meter/sound_level_meter.cpp b/components/sound_level_meter/sound_level_meter.cpp index 3f628fe..4de09fa 100644 --- a/components/sound_level_meter/sound_level_meter.cpp +++ b/components/sound_level_meter/sound_level_meter.cpp @@ -20,7 +20,6 @@ void SoundLevelMeter::set_buffer_size(uint32_t buffer_size) { this->buffer_size_ uint32_t SoundLevelMeter::get_buffer_size() { return this->buffer_size_; } uint32_t SoundLevelMeter::get_sample_rate() { return this->i2s_->get_sample_rate(); } void SoundLevelMeter::set_i2s(i2s::I2SComponent *i2s) { this->i2s_ = i2s; } -void SoundLevelMeter::add_group(SensorGroup *group) { this->groups_.push_back(group); } void SoundLevelMeter::set_warmup_interval(uint32_t warmup_interval) { this->warmup_interval_ = warmup_interval; } void SoundLevelMeter::set_task_stack_size(uint32_t task_stack_size) { this->task_stack_size_ = task_stack_size; } void SoundLevelMeter::set_task_priority(uint8_t task_priority) { this->task_priority_ = task_priority; } @@ -33,7 +32,9 @@ void SoundLevelMeter::set_mic_sensitivity_ref(optional mic_sensitivity_re optional SoundLevelMeter::get_mic_sensitivity_ref() { return this->mic_sensitivity_ref_; } void SoundLevelMeter::set_offset(optional offset) { this->offset_ = offset; } optional SoundLevelMeter::get_offset() { return this->offset_; } -void SoundLevelMeter::set_max_groups_depth(uint8_t max_groups_depth) { this->max_groups_depth_ = max_groups_depth; } +void SoundLevelMeter::set_max_buffers(uint8_t max_buffers) { this->max_buffers_ = max_buffers; } +void SoundLevelMeter::add_sensor(SoundLevelMeterSensor *sensor) { this->sensors_.push_back(sensor); } +void SoundLevelMeter::add_dsp_filter(Filter *dsp_filter) { this->dsp_filters_.push_back(dsp_filter); } void SoundLevelMeter::dump_config() { ESP_LOGCONFIG(TAG, "Sound Level Meter:"); @@ -43,16 +44,13 @@ void SoundLevelMeter::dump_config() { ESP_LOGCONFIG(TAG, " Task Priority: %u", this->task_priority_); ESP_LOGCONFIG(TAG, " Task Core: %u", this->task_core_); LOG_UPDATE_INTERVAL(this); - if (this->groups_.size() > 0) { - ESP_LOGCONFIG(TAG, " Groups:"); - for (int i = 0; i < this->groups_.size(); i++) { - ESP_LOGCONFIG(TAG, " Group %u:", i); - this->groups_[i]->dump_config(" "); - } - } + ESP_LOGCONFIG(TAG, "Sensors:"); + for (auto *s : this->sensors_) + LOG_SENSOR(" ", "Sound Pressure Level", s); } void SoundLevelMeter::setup() { + this->sort_sensors(); xTaskCreatePinnedToCore(SoundLevelMeter::task, "sound_level_meter", this->task_stack_size_, this, this->task_priority_, nullptr, this->task_core_); } @@ -93,7 +91,7 @@ bool SoundLevelMeter::is_on() { return this->is_on_; } void SoundLevelMeter::task(void *param) { SoundLevelMeter *this_ = reinterpret_cast(param); - BufferPool buffers(this_->buffer_size_, this_->max_groups_depth_); + BufferStack buffers(this_->buffer_size_, this_->max_buffers_); auto warmup_start = millis(); while (millis() - warmup_start < this_->warmup_interval_) @@ -106,11 +104,11 @@ void SoundLevelMeter::task(void *param) { std::unique_lock lock(this_->on_mutex_); this_->on_cv_.wait(lock, [this_] { return this_->is_on_; }); } + buffers.reset(); if (this_->i2s_->read_samples(buffers)) { process_start = esp_timer_get_time(); - for (auto *g : this_->groups_) - g->process(buffers); + this_->process(buffers); process_time += esp_timer_get_time() - process_start; process_count += buffers.current().size(); @@ -118,68 +116,53 @@ void SoundLevelMeter::task(void *param) { auto sr = this_->get_sample_rate(); if (process_count >= sr * (this_->update_interval_ / 1000.f)) { auto t = uint32_t(float(process_time) / process_count * (sr / 1000.f)); - ESP_LOGD(TAG, "Processing time per 1s of audio data (%u samples): %u ms", sr, t); + auto cpu_util = float(process_time) / 1000 / this_->update_interval_; + ESP_LOGE(TAG, "CPU (Core %u) Utilization: %.1f %%", xPortGetCoreID(), cpu_util * 100); process_time = process_count = 0; } } } } -void SoundLevelMeter::defer(std::function &&f) { - std::lock_guard lock(this->defer_mutex_); - this->defer_queue_.push(std::move(f)); -} - -void SoundLevelMeter::reset() { - for (auto *g : this->groups_) - g->reset(); -} - -/* SensorGroup */ - -void SensorGroup::set_parent(SoundLevelMeter *parent) { this->parent_ = parent; } -void SensorGroup::add_sensor(SoundLevelMeterSensor *sensor) { this->sensors_.push_back(sensor); } -void SensorGroup::add_group(SensorGroup *group) { this->groups_.push_back(group); } -void SensorGroup::add_filter(Filter *filter) { this->filters_.push_back(filter); } - -void SensorGroup::dump_config(const char *prefix) { - ESP_LOGCONFIG(TAG, "%sSensors:", prefix); - for (auto *s : this->sensors_) - LOG_SENSOR((std::string(prefix) + " ").c_str(), "Sound Pressure Level", s); - - if (this->groups_.size() > 0) { - ESP_LOGCONFIG(TAG, "%sGroups:", prefix); - for (int i = 0; i < this->groups_.size(); i++) { - ESP_LOGCONFIG(TAG, "%s Group %u:", prefix, i); - this->groups_[i]->dump_config((std::string(prefix) + " ").c_str()); +// sorting sensors in such a way that sensors with same filters (or prefix) come +// near each other +void SoundLevelMeter::sort_sensors() { + std::sort(this->sensors_.begin(), this->sensors_.end(), [](SoundLevelMeterSensor *a, SoundLevelMeterSensor *b) { + return std::lexicographical_compare(a->dsp_filters_.begin(), a->dsp_filters_.end(), b->dsp_filters_.begin(), + b->dsp_filters_.end()); + }); +} + +void SoundLevelMeter::process(BufferStack &buffers) { + std::vector prefix; + for (auto s : this->sensors_) { + int i = 0, n = s->dsp_filters_.size(), m = prefix.size(); + for (; i < n && i < m && s->dsp_filters_[i] == prefix[i]; i++) + ; + while (prefix.size() > i) { + prefix.pop_back(); + buffers.pop(); } - } -} - -void SensorGroup::process(BufferPool &buffers) { - if (this->filters_.size() > 0) - buffers++; - if (this->filters_.size() > 0) - for (auto f : this->filters_) + for (; i < s->dsp_filters_.size(); i++) { + auto f = s->dsp_filters_[i]; + prefix.push_back(f); + buffers.push(); f->process(buffers); - - for (auto s : this->sensors_) + } s->process(buffers); + } +} - for (auto g : this->groups_) - g->process(buffers); - - if (this->filters_.size() > 0) - buffers--; +void SoundLevelMeter::defer(std::function &&f) { + std::lock_guard lock(this->defer_mutex_); + this->defer_queue_.push(std::move(f)); } -void SensorGroup::reset() { - for (auto f : this->filters_) +void SoundLevelMeter::reset() { + for (auto f : this->dsp_filters_) f->reset(); for (auto s : this->sensors_) s->reset(); - for (auto g : this->groups_) - g->reset(); } /* SoundLevelMeterSensor */ @@ -193,6 +176,8 @@ void SoundLevelMeterSensor::set_update_interval(uint32_t update_interval) { this->update_samples_ = this->parent_->get_sample_rate() * (update_interval / 1000.f); } +void SoundLevelMeterSensor::add_dsp_filter(Filter *dsp_filter) { this->dsp_filters_.push_back(dsp_filter); } + void SoundLevelMeterSensor::defer_publish_state(float state) { this->parent_->defer([this, state]() { this->publish_state(state); }); } @@ -367,17 +352,18 @@ void SOS_Filter::reset() { s = {0.f, 0.f}; } -/* BufferPool */ +/* BufferStack */ template -BufferPool::BufferPool(uint32_t buffer_size, uint32_t max_depth) : buffer_size_(buffer_size), max_depth_(max_depth) { +BufferStack::BufferStack(uint32_t buffer_size, uint32_t max_depth) + : buffer_size_(buffer_size), max_depth_(max_depth) { this->buffers_.resize(max_depth); this->buffers_[0].resize(buffer_size); } -template std::vector &BufferPool::current() { return this->buffers_[this->index_]; } +template std::vector &BufferStack::current() { return this->buffers_[this->index_]; } -template BufferPool &BufferPool::operator++(int) { +template void BufferStack::push() { assert(this->index_ < this->max_depth_ - 1 && "Index out of bounds"); this->index_++; auto &dst = this->buffers_[this->index_]; @@ -385,16 +371,16 @@ template BufferPool &BufferPool::operator++(int) { auto n = src.size(); dst.resize(n); memcpy(&dst[0], &src[0], n * sizeof(T)); - return *this; } -template BufferPool &BufferPool::operator--(int) { +template void BufferStack::pop() { assert(this->index_ >= 1 && "Index out of bounds"); this->index_--; - return *this; } -template BufferPool::operator std::vector &() { return this->current(); } +template void BufferStack::reset() { this->index_ = 0; } + +template BufferStack::operator std::vector &() { return this->current(); } } // namespace sound_level_meter } // namespace esphome \ No newline at end of file diff --git a/components/sound_level_meter/sound_level_meter.h b/components/sound_level_meter/sound_level_meter.h index 88da685..d5118b1 100644 --- a/components/sound_level_meter/sound_level_meter.h +++ b/components/sound_level_meter/sound_level_meter.h @@ -10,10 +10,9 @@ namespace esphome { namespace sound_level_meter { -class SensorGroup; class SoundLevelMeterSensor; class Filter; -template class BufferPool; +template class BufferStack; class SoundLevelMeter : public Component { friend class SoundLevelMeterSensor; @@ -25,7 +24,6 @@ class SoundLevelMeter : public Component { uint32_t get_buffer_size(); uint32_t get_sample_rate(); void set_i2s(i2s::I2SComponent *i2s); - void add_group(SensorGroup *group); void set_warmup_interval(uint32_t warmup_interval); void set_task_stack_size(uint32_t task_stack_size); void set_task_priority(uint8_t task_priority); @@ -36,7 +34,9 @@ class SoundLevelMeter : public Component { optional get_mic_sensitivity_ref(); void set_offset(optional offset); optional get_offset(); - void set_max_groups_depth(uint8_t max_groups_depth); + void set_max_buffers(uint8_t max_buffers); + void add_sensor(SoundLevelMeterSensor *sensor); + void add_dsp_filter(Filter *dsp_filter); virtual void setup() override; virtual void loop() override; virtual void dump_config() override; @@ -47,7 +47,8 @@ class SoundLevelMeter : public Component { protected: i2s::I2SComponent *i2s_{nullptr}; - std::vector groups_; + std::vector dsp_filters_; + std::vector sensors_; size_t buffer_size_{256}; uint32_t warmup_interval_{500}; uint32_t task_stack_size_{1024}; @@ -62,43 +63,31 @@ class SoundLevelMeter : public Component { bool is_on_{true}; std::mutex on_mutex_; std::condition_variable on_cv_; - uint8_t max_groups_depth_{1}; + uint8_t max_buffers_{1}; - static void task(void *param); + void sort_sensors(); + void process(BufferStack &buffers); // epshome's scheduler is not thred safe, so we have to use custom thread safe implementation // to execute sensor updates in main loop void defer(std::function &&f); void reset(); -}; - -class SensorGroup { - public: - void set_parent(SoundLevelMeter *parent); - void add_sensor(SoundLevelMeterSensor *sensor); - void add_group(SensorGroup *group); - void add_filter(Filter *filter); - void process(BufferPool &buffers); - void dump_config(const char *prefix); - void reset(); - protected: - SoundLevelMeter *parent_{nullptr}; - std::vector groups_; - std::vector sensors_; - std::vector filters_; + static void task(void *param); }; class SoundLevelMeterSensor : public sensor::Sensor { - friend class SensorGroup; + friend SoundLevelMeter; public: void set_parent(SoundLevelMeter *parent); void set_update_interval(uint32_t update_interval); + void add_dsp_filter(Filter *dsp_filter); virtual void process(std::vector &buffer) = 0; void defer_publish_state(float state); protected: SoundLevelMeter *parent_{nullptr}; + std::vector dsp_filters_; uint32_t update_samples_{0}; float adjust_dB(float dB, bool is_rms = true); @@ -156,7 +145,7 @@ class SoundLevelMeterSensorPeak : public SoundLevelMeterSensor { }; class Filter { - friend class SensorGroup; + friend SoundLevelMeter; public: virtual void process(std::vector &data) = 0; @@ -177,12 +166,13 @@ class SOS_Filter : public Filter { virtual void reset() override; }; -template class BufferPool { +template class BufferStack { public: - BufferPool(uint32_t buffer_size, uint32_t max_depth); + BufferStack(uint32_t buffer_size, uint32_t buffer_count); std::vector ¤t(); - BufferPool &operator++(int); - BufferPool &operator--(int); + void push(); + void pop(); + void reset(); operator std::vector &(); private: