Skip to content

Commit

Permalink
implement BufferPool to reduce memory reallocations
Browse files Browse the repository at this point in the history
  • Loading branch information
stas-sl committed Oct 3, 2024
1 parent 068c0c3 commit 27a7a69
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 15 deletions.
13 changes: 9 additions & 4 deletions components/sound_level_meter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,9 @@
}
)


def config_group_schema(value):
return CONFIG_GROUP_SCHEMA(value)


CONFIG_GROUP_SCHEMA = (
cv.Schema({
cv.GenerateID(): cv.declare_id(SensorGroup),
Expand Down Expand Up @@ -148,7 +146,6 @@ def config_group_schema(value):
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])
Expand All @@ -173,7 +170,6 @@ async def groups_to_code(config, component, parent):
cg.add(s.set_update_interval(sc[CONF_UPDATE_INTERVAL]))
cg.add(g.add_sensor(s))


async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
Expand All @@ -185,6 +181,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))
if CONF_MIC_SENSITIVITY in config:
cg.add(var.set_mic_sensitivity(config[CONF_MIC_SENSITIVITY]))
if CONF_MIC_SENSITIVITY_REF in config:
Expand All @@ -195,6 +192,14 @@ async def to_code(config):
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)
Expand Down
51 changes: 41 additions & 10 deletions components/sound_level_meter/sound_level_meter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ void SoundLevelMeter::set_mic_sensitivity_ref(optional<float> mic_sensitivity_re
optional<float> SoundLevelMeter::get_mic_sensitivity_ref() { return this->mic_sensitivity_ref_; }
void SoundLevelMeter::set_offset(optional<float> offset) { this->offset_ = offset; }
optional<float> 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::dump_config() {
ESP_LOGCONFIG(TAG, "Sound Level Meter:");
Expand Down Expand Up @@ -92,11 +93,11 @@ bool SoundLevelMeter::is_on() { return this->is_on_; }

void SoundLevelMeter::task(void *param) {
SoundLevelMeter *this_ = reinterpret_cast<SoundLevelMeter *>(param);
std::vector<float> buffer(this_->buffer_size_);
BufferPool<float> buffers(this_->buffer_size_, this_->max_groups_depth_);

auto warmup_start = millis();
while (millis() - warmup_start < this_->warmup_interval_)
this_->i2s_->read_samples(buffer);
this_->i2s_->read_samples(buffers);

uint32_t process_time = 0, process_count = 0;
uint64_t process_start;
Expand All @@ -105,14 +106,14 @@ void SoundLevelMeter::task(void *param) {
std::unique_lock<std::mutex> lock(this_->on_mutex_);
this_->on_cv_.wait(lock, [this_] { return this_->is_on_; });
}
if (this_->i2s_->read_samples(buffer)) {
if (this_->i2s_->read_samples(buffers)) {
process_start = esp_timer_get_time();

for (auto *g : this_->groups_)
g->process(buffer);
g->process(buffers);

process_time += esp_timer_get_time() - process_start;
process_count += buffer.size();
process_count += buffers.current().size();

auto sr = this_->get_sample_rate();
if (process_count >= sr * (this_->update_interval_ / 1000.f)) {
Expand Down Expand Up @@ -155,17 +156,21 @@ void SensorGroup::dump_config(const char *prefix) {
}
}

void SensorGroup::process(std::vector<float> &buffer) {
std::vector<float> &&data = this->filters_.size() > 0 ? std::vector<float>(buffer) : buffer;
void SensorGroup::process(BufferPool<float> &buffers) {
if (this->filters_.size() > 0)
buffers++;
if (this->filters_.size() > 0)
for (auto f : this->filters_)
f->process(data);
f->process(buffers);

for (auto s : this->sensors_)
s->process(data);
s->process(buffers);

for (auto g : this->groups_)
g->process(data);
g->process(buffers);

if (this->filters_.size() > 0)
buffers--;
}

void SensorGroup::reset() {
Expand Down Expand Up @@ -361,5 +366,31 @@ void SOS_Filter::reset() {
for (auto &s : this->state_)
s = {0.f, 0.f};
}

/* BufferPool */

template<typename T>
BufferPool<T>::BufferPool(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<typename T> std::vector<T> &BufferPool<T>::current() { return this->buffers_[this->index_]; }

template<typename T> BufferPool<T> &BufferPool<T>::operator++(int) {
assert(this->index_ < this->max_depth_ - 1 && "Index out of bounds");
this->index_++;
this->buffers_[this->index_] = this->buffers_[this->index_ - 1];
return *this;
}

template<typename T> BufferPool<T> &BufferPool<T>::operator--(int) {
assert(this->index_ >= 1 && "Index out of bounds");
this->index_--;
return *this;
}

template<typename T> BufferPool<T>::operator std::vector<T> &() { return this->current(); }

} // namespace sound_level_meter
} // namespace esphome
20 changes: 19 additions & 1 deletion components/sound_level_meter/sound_level_meter.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ namespace sound_level_meter {
class SensorGroup;
class SoundLevelMeterSensor;
class Filter;
template<typename T> class BufferPool;

class SoundLevelMeter : public Component {
friend class SoundLevelMeterSensor;
Expand All @@ -35,6 +36,7 @@ class SoundLevelMeter : public Component {
optional<float> get_mic_sensitivity_ref();
void set_offset(optional<float> offset);
optional<float> get_offset();
void set_max_groups_depth(uint8_t max_groups_depth);
virtual void setup() override;
virtual void loop() override;
virtual void dump_config() override;
Expand All @@ -60,6 +62,7 @@ class SoundLevelMeter : public Component {
bool is_on_{true};
std::mutex on_mutex_;
std::condition_variable on_cv_;
uint8_t max_groups_depth_{1};

static void task(void *param);
// epshome's scheduler is not thred safe, so we have to use custom thread safe implementation
Expand All @@ -74,7 +77,7 @@ class SensorGroup {
void add_sensor(SoundLevelMeterSensor *sensor);
void add_group(SensorGroup *group);
void add_filter(Filter *filter);
void process(std::vector<float> &buffer);
void process(BufferPool<float> &buffers);
void dump_config(const char *prefix);
void reset();

Expand Down Expand Up @@ -174,6 +177,21 @@ class SOS_Filter : public Filter {
virtual void reset() override;
};

template<typename T> class BufferPool {
public:
BufferPool(uint32_t buffer_size, uint32_t max_depth);
std::vector<T> &current();
BufferPool<T> &operator++(int);
BufferPool<T> &operator--(int);
operator std::vector<T> &();

private:
uint32_t buffer_size_;
uint32_t max_depth_;
uint32_t index_{0};
std::vector<std::vector<T>> buffers_;
};

template<typename... Ts> class TurnOnAction : public Action<Ts...> {
public:
explicit TurnOnAction(SoundLevelMeter *sound_level_meter) : sound_level_meter_(sound_level_meter) {}
Expand Down

0 comments on commit 27a7a69

Please sign in to comment.