mirror of
https://github.com/esphome/esphome.git
synced 2026-05-10 05:37:55 +08:00
[core] Move core ring buffer to helper component (#16298)
This commit is contained in:
@@ -416,6 +416,7 @@ esphome/components/resampler/speaker/* @kahrendt
|
||||
esphome/components/restart/* @esphome/core
|
||||
esphome/components/rf_bridge/* @jesserockz
|
||||
esphome/components/rgbct/* @jesserockz
|
||||
esphome/components/ring_buffer/* @kahrendt
|
||||
esphome/components/rp2040/* @jesserockz
|
||||
esphome/components/rp2040_ble/* @bdraco
|
||||
esphome/components/rp2040_pio_led_strip/* @Papa-DMan
|
||||
|
||||
@@ -16,6 +16,7 @@ from esphome.const import (
|
||||
from esphome.core import CORE
|
||||
import esphome.final_validate as fv
|
||||
|
||||
AUTO_LOAD = ["ring_buffer"]
|
||||
CODEOWNERS = ["@kahrendt"]
|
||||
DOMAIN = "audio"
|
||||
audio_ns = cg.esphome_ns.namespace("audio")
|
||||
|
||||
@@ -19,7 +19,7 @@ AudioDecoder::AudioDecoder(size_t input_buffer_size, size_t output_buffer_size)
|
||||
this->output_transfer_buffer_ = AudioSinkTransferBuffer::create(output_buffer_size);
|
||||
}
|
||||
|
||||
esp_err_t AudioDecoder::add_source(std::weak_ptr<RingBuffer> &input_ring_buffer) {
|
||||
esp_err_t AudioDecoder::add_source(std::weak_ptr<ring_buffer::RingBuffer> &input_ring_buffer) {
|
||||
auto source = AudioSourceTransferBuffer::create(this->input_buffer_size_);
|
||||
if (source == nullptr) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
@@ -36,7 +36,7 @@ esp_err_t AudioDecoder::add_source(const uint8_t *data_pointer, size_t length) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t AudioDecoder::add_sink(std::weak_ptr<RingBuffer> &output_ring_buffer) {
|
||||
esp_err_t AudioDecoder::add_sink(std::weak_ptr<ring_buffer::RingBuffer> &output_ring_buffer) {
|
||||
if (this->output_transfer_buffer_ != nullptr) {
|
||||
this->output_transfer_buffer_->set_sink(output_ring_buffer);
|
||||
return ESP_OK;
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#include "audio.h"
|
||||
#include "audio_transfer_buffer.h"
|
||||
|
||||
#include "esphome/components/ring_buffer/ring_buffer.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/ring_buffer.h"
|
||||
|
||||
#ifdef USE_SPEAKER
|
||||
#include "esphome/components/speaker/speaker.h"
|
||||
@@ -70,12 +70,12 @@ class AudioDecoder {
|
||||
/// @brief Adds a source ring buffer for raw file data. Takes ownership of the ring buffer in a shared_ptr.
|
||||
/// @param input_ring_buffer weak_ptr of a shared_ptr of the sink ring buffer to transfer ownership
|
||||
/// @return ESP_OK if successsful, ESP_ERR_NO_MEM if the transfer buffer wasn't allocated
|
||||
esp_err_t add_source(std::weak_ptr<RingBuffer> &input_ring_buffer);
|
||||
esp_err_t add_source(std::weak_ptr<ring_buffer::RingBuffer> &input_ring_buffer);
|
||||
|
||||
/// @brief Adds a sink ring buffer for decoded audio. Takes ownership of the ring buffer in a shared_ptr.
|
||||
/// @param output_ring_buffer weak_ptr of a shared_ptr of the sink ring buffer to transfer ownership
|
||||
/// @return ESP_OK if successsful, ESP_ERR_NO_MEM if the transfer buffer wasn't allocated
|
||||
esp_err_t add_sink(std::weak_ptr<RingBuffer> &output_ring_buffer);
|
||||
esp_err_t add_sink(std::weak_ptr<ring_buffer::RingBuffer> &output_ring_buffer);
|
||||
|
||||
#ifdef USE_SPEAKER
|
||||
/// @brief Adds a sink speaker for decoded audio.
|
||||
|
||||
@@ -54,7 +54,7 @@ enum HttpStatus {
|
||||
|
||||
AudioReader::~AudioReader() { this->cleanup_connection_(); }
|
||||
|
||||
esp_err_t AudioReader::add_sink(const std::weak_ptr<RingBuffer> &output_ring_buffer) {
|
||||
esp_err_t AudioReader::add_sink(const std::weak_ptr<ring_buffer::RingBuffer> &output_ring_buffer) {
|
||||
if (current_audio_file_ != nullptr) {
|
||||
// A transfer buffer isn't ncessary for a local file
|
||||
this->file_ring_buffer_ = output_ring_buffer.lock();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "audio.h"
|
||||
#include "audio_transfer_buffer.h"
|
||||
|
||||
#include "esphome/core/ring_buffer.h"
|
||||
#include "esphome/components/ring_buffer/ring_buffer.h"
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
@@ -35,7 +35,7 @@ class AudioReader {
|
||||
/// @brief Adds a sink ring buffer for audio data. Takes ownership of the ring buffer in a shared_ptr
|
||||
/// @param output_ring_buffer weak_ptr of a shared_ptr of the sink ring buffer to transfer ownership
|
||||
/// @return ESP_OK if successful, ESP_ERR_INVALID_STATE otherwise
|
||||
esp_err_t add_sink(const std::weak_ptr<RingBuffer> &output_ring_buffer);
|
||||
esp_err_t add_sink(const std::weak_ptr<ring_buffer::RingBuffer> &output_ring_buffer);
|
||||
|
||||
/// @brief Starts reading an audio file from an http source. The transfer buffer is allocated here.
|
||||
/// @param uri Web url to the http file.
|
||||
@@ -60,7 +60,7 @@ class AudioReader {
|
||||
AudioReaderState file_read_();
|
||||
AudioReaderState http_read_();
|
||||
|
||||
std::shared_ptr<RingBuffer> file_ring_buffer_;
|
||||
std::shared_ptr<ring_buffer::RingBuffer> file_ring_buffer_;
|
||||
std::unique_ptr<AudioSinkTransferBuffer> output_transfer_buffer_;
|
||||
void cleanup_connection_();
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ AudioResampler::AudioResampler(size_t input_buffer_size, size_t output_buffer_si
|
||||
this->output_transfer_buffer_ = AudioSinkTransferBuffer::create(output_buffer_size);
|
||||
}
|
||||
|
||||
esp_err_t AudioResampler::add_source(std::weak_ptr<RingBuffer> &input_ring_buffer) {
|
||||
esp_err_t AudioResampler::add_source(std::weak_ptr<ring_buffer::RingBuffer> &input_ring_buffer) {
|
||||
if (this->input_transfer_buffer_ != nullptr) {
|
||||
this->input_transfer_buffer_->set_source(input_ring_buffer);
|
||||
return ESP_OK;
|
||||
@@ -24,7 +24,7 @@ esp_err_t AudioResampler::add_source(std::weak_ptr<RingBuffer> &input_ring_buffe
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
esp_err_t AudioResampler::add_sink(std::weak_ptr<RingBuffer> &output_ring_buffer) {
|
||||
esp_err_t AudioResampler::add_sink(std::weak_ptr<ring_buffer::RingBuffer> &output_ring_buffer) {
|
||||
if (this->output_transfer_buffer_ != nullptr) {
|
||||
this->output_transfer_buffer_->set_sink(output_ring_buffer);
|
||||
return ESP_OK;
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#include "audio.h"
|
||||
#include "audio_transfer_buffer.h"
|
||||
|
||||
#include "esphome/components/ring_buffer/ring_buffer.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/ring_buffer.h"
|
||||
|
||||
#ifdef USE_SPEAKER
|
||||
#include "esphome/components/speaker/speaker.h"
|
||||
@@ -40,12 +40,12 @@ class AudioResampler {
|
||||
/// @brief Adds a source ring buffer for audio data. Takes ownership of the ring buffer in a shared_ptr.
|
||||
/// @param input_ring_buffer weak_ptr of a shared_ptr of the sink ring buffer to transfer ownership
|
||||
/// @return ESP_OK if successsful, ESP_ERR_NO_MEM if the transfer buffer wasn't allocated
|
||||
esp_err_t add_source(std::weak_ptr<RingBuffer> &input_ring_buffer);
|
||||
esp_err_t add_source(std::weak_ptr<ring_buffer::RingBuffer> &input_ring_buffer);
|
||||
|
||||
/// @brief Adds a sink ring buffer for resampled audio. Takes ownership of the ring buffer in a shared_ptr.
|
||||
/// @param output_ring_buffer weak_ptr of a shared_ptr of the sink ring buffer to transfer ownership
|
||||
/// @return ESP_OK if successsful, ESP_ERR_NO_MEM if the transfer buffer wasn't allocated
|
||||
esp_err_t add_sink(std::weak_ptr<RingBuffer> &output_ring_buffer);
|
||||
esp_err_t add_sink(std::weak_ptr<ring_buffer::RingBuffer> &output_ring_buffer);
|
||||
|
||||
#ifdef USE_SPEAKER
|
||||
/// @brief Adds a sink speaker for decoded audio.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#include "esphome/components/ring_buffer/ring_buffer.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/ring_buffer.h"
|
||||
|
||||
#ifdef USE_SPEAKER
|
||||
#include "esphome/components/speaker/speaker.h"
|
||||
@@ -76,7 +76,7 @@ class AudioTransferBuffer {
|
||||
void deallocate_buffer_();
|
||||
|
||||
// A possible source or sink for the transfer buffer
|
||||
std::shared_ptr<RingBuffer> ring_buffer_;
|
||||
std::shared_ptr<ring_buffer::RingBuffer> ring_buffer_;
|
||||
|
||||
uint8_t *buffer_{nullptr};
|
||||
uint8_t *data_start_{nullptr};
|
||||
@@ -105,7 +105,7 @@ class AudioSinkTransferBuffer : public AudioTransferBuffer {
|
||||
|
||||
/// @brief Adds a ring buffer as the transfer buffer's sink.
|
||||
/// @param ring_buffer weak_ptr to the allocated ring buffer
|
||||
void set_sink(const std::weak_ptr<RingBuffer> &ring_buffer) { this->ring_buffer_ = ring_buffer.lock(); }
|
||||
void set_sink(const std::weak_ptr<ring_buffer::RingBuffer> &ring_buffer) { this->ring_buffer_ = ring_buffer.lock(); }
|
||||
|
||||
#ifdef USE_SPEAKER
|
||||
/// @brief Adds a speaker as the transfer buffer's sink.
|
||||
@@ -179,7 +179,9 @@ class AudioSourceTransferBuffer : public AudioTransferBuffer, public AudioReadab
|
||||
|
||||
/// @brief Adds a ring buffer as the transfer buffer's source.
|
||||
/// @param ring_buffer weak_ptr to the allocated ring buffer
|
||||
void set_source(const std::weak_ptr<RingBuffer> &ring_buffer) { this->ring_buffer_ = ring_buffer.lock(); };
|
||||
void set_source(const std::weak_ptr<ring_buffer::RingBuffer> &ring_buffer) {
|
||||
this->ring_buffer_ = ring_buffer.lock();
|
||||
};
|
||||
|
||||
// AudioReadableBuffer interface
|
||||
const uint8_t *data() const override { return this->data_start_; }
|
||||
|
||||
@@ -195,7 +195,7 @@ size_t I2SAudioSpeakerBase::play(const uint8_t *data, size_t length, TickType_t
|
||||
|
||||
size_t bytes_written = 0;
|
||||
if (this->state_ == speaker::STATE_RUNNING) {
|
||||
std::shared_ptr<RingBuffer> temp_ring_buffer = this->audio_ring_buffer_.lock();
|
||||
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = this->audio_ring_buffer_.lock();
|
||||
if (temp_ring_buffer != nullptr) {
|
||||
// The weak_ptr locks successfully only while the speaker task owns the ring buffer, so it is safe to write
|
||||
bytes_written = temp_ring_buffer->write_without_replacement((void *) data, length, ticks_to_wait);
|
||||
@@ -207,7 +207,7 @@ size_t I2SAudioSpeakerBase::play(const uint8_t *data, size_t length, TickType_t
|
||||
|
||||
bool I2SAudioSpeakerBase::has_buffered_data() const {
|
||||
if (this->audio_ring_buffer_.use_count() > 0) {
|
||||
std::shared_ptr<RingBuffer> temp_ring_buffer = this->audio_ring_buffer_.lock();
|
||||
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = this->audio_ring_buffer_.lock();
|
||||
return temp_ring_buffer->available() > 0;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
#include <freertos/FreeRTOS.h>
|
||||
|
||||
#include "esphome/components/audio/audio.h"
|
||||
#include "esphome/components/ring_buffer/ring_buffer.h"
|
||||
#include "esphome/components/speaker/speaker.h"
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/gpio.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/ring_buffer.h"
|
||||
|
||||
namespace esphome::i2s_audio {
|
||||
|
||||
@@ -143,7 +143,7 @@ class I2SAudioSpeakerBase : public I2SAudioOut, public speaker::Speaker, public
|
||||
|
||||
QueueHandle_t i2s_event_queue_{nullptr};
|
||||
|
||||
std::weak_ptr<RingBuffer> audio_ring_buffer_;
|
||||
std::weak_ptr<ring_buffer::RingBuffer> audio_ring_buffer_;
|
||||
|
||||
uint32_t buffer_duration_ms_;
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ void I2SAudioSpeaker::run_speaker_task() {
|
||||
audio::AudioSourceTransferBuffer::create(bytes_to_fill_single_dma_buffer);
|
||||
|
||||
if (transfer_buffer != nullptr) {
|
||||
std::shared_ptr<RingBuffer> temp_ring_buffer = RingBuffer::create(ring_buffer_size);
|
||||
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = ring_buffer::RingBuffer::create(ring_buffer_size);
|
||||
if (temp_ring_buffer.use_count() == 1) {
|
||||
transfer_buffer->set_source(temp_ring_buffer);
|
||||
this->audio_ring_buffer_ = temp_ring_buffer;
|
||||
|
||||
@@ -30,6 +30,7 @@ from esphome.core import CORE, HexInt
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
AUTO_LOAD = ["ring_buffer"]
|
||||
CODEOWNERS = ["@kahrendt", "@jesserockz"]
|
||||
DEPENDENCIES = ["microphone"]
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ void MicroWakeWord::setup() {
|
||||
if (this->state_ == State::STOPPED) {
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<RingBuffer> temp_ring_buffer = this->ring_buffer_.lock();
|
||||
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = this->ring_buffer_.lock();
|
||||
if (this->ring_buffer_.use_count() > 1) {
|
||||
size_t bytes_free = temp_ring_buffer->free();
|
||||
|
||||
@@ -156,7 +156,7 @@ void MicroWakeWord::inference_task(void *params) {
|
||||
|
||||
if (!(xEventGroupGetBits(this_mww->event_group_) & ERROR_BITS)) {
|
||||
// Allocate ring buffer
|
||||
std::shared_ptr<RingBuffer> temp_ring_buffer = RingBuffer::create(
|
||||
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = ring_buffer::RingBuffer::create(
|
||||
this_mww->microphone_source_->get_audio_stream_info().ms_to_bytes(RING_BUFFER_DURATION_MS));
|
||||
if (temp_ring_buffer.use_count() == 0) {
|
||||
xEventGroupSetBits(this_mww->event_group_, EventGroupBits::ERROR_MEMORY);
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
#include "streaming_model.h"
|
||||
|
||||
#include "esphome/components/microphone/microphone_source.h"
|
||||
#include "esphome/components/ring_buffer/ring_buffer.h"
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/ring_buffer.h"
|
||||
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
#include "esphome/components/ota/ota_backend.h"
|
||||
@@ -80,7 +80,7 @@ class MicroWakeWord : public Component
|
||||
Trigger<std::string> wake_word_detected_trigger_;
|
||||
State state_{State::STOPPED};
|
||||
|
||||
std::weak_ptr<RingBuffer> ring_buffer_;
|
||||
std::weak_ptr<ring_buffer::RingBuffer> ring_buffer_;
|
||||
std::vector<WakeWordModel *> wake_word_models_;
|
||||
|
||||
#ifdef USE_MICRO_WAKE_WORD_VAD
|
||||
|
||||
@@ -226,7 +226,7 @@ size_t SourceSpeaker::play(const uint8_t *data, size_t length, TickType_t ticks_
|
||||
this->start();
|
||||
}
|
||||
size_t bytes_written = 0;
|
||||
std::shared_ptr<RingBuffer> temp_ring_buffer = this->ring_buffer_.lock();
|
||||
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = this->ring_buffer_.lock();
|
||||
if (temp_ring_buffer.use_count() > 0) {
|
||||
// Only write to the ring buffer if the reference is valid
|
||||
bytes_written = temp_ring_buffer->write_without_replacement(data, length, ticks_to_wait);
|
||||
@@ -263,9 +263,9 @@ esp_err_t SourceSpeaker::start_() {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
std::shared_ptr<RingBuffer> temp_ring_buffer = this->ring_buffer_.lock();
|
||||
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = this->ring_buffer_.lock();
|
||||
if (!temp_ring_buffer) {
|
||||
temp_ring_buffer = RingBuffer::create(ring_buffer_size);
|
||||
temp_ring_buffer = ring_buffer::RingBuffer::create(ring_buffer_size);
|
||||
this->ring_buffer_ = temp_ring_buffer;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "esphome/components/audio/audio.h"
|
||||
#include "esphome/components/audio/audio_transfer_buffer.h"
|
||||
#include "esphome/components/ring_buffer/ring_buffer.h"
|
||||
#include "esphome/components/speaker/speaker.h"
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
@@ -106,7 +107,7 @@ class SourceSpeaker : public speaker::Speaker, public Component {
|
||||
MixerSpeaker *parent_;
|
||||
|
||||
std::shared_ptr<audio::AudioSourceTransferBuffer> transfer_buffer_;
|
||||
std::weak_ptr<RingBuffer> ring_buffer_;
|
||||
std::weak_ptr<ring_buffer::RingBuffer> ring_buffer_;
|
||||
|
||||
uint32_t buffer_duration_ms_;
|
||||
uint32_t last_seen_data_ms_{0};
|
||||
|
||||
@@ -226,7 +226,7 @@ size_t ResamplerSpeaker::play(const uint8_t *data, size_t length, TickType_t tic
|
||||
if ((this->output_speaker_->is_running()) && (!this->requires_resampling_())) {
|
||||
bytes_written = this->output_speaker_->play(data, length, ticks_to_wait);
|
||||
} else {
|
||||
std::shared_ptr<RingBuffer> temp_ring_buffer = this->ring_buffer_.lock();
|
||||
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = this->ring_buffer_.lock();
|
||||
if (temp_ring_buffer) {
|
||||
// Only write to the ring buffer if the reference is valid
|
||||
bytes_written = temp_ring_buffer->write_without_replacement(data, length, ticks_to_wait);
|
||||
@@ -286,7 +286,7 @@ void ResamplerSpeaker::finish() { this->send_command_(ResamplingEventGroupBits::
|
||||
bool ResamplerSpeaker::has_buffered_data() const {
|
||||
bool has_ring_buffer_data = false;
|
||||
if (this->requires_resampling_()) {
|
||||
std::shared_ptr<RingBuffer> temp_ring_buffer = this->ring_buffer_.lock();
|
||||
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = this->ring_buffer_.lock();
|
||||
if (temp_ring_buffer) {
|
||||
has_ring_buffer_data = (temp_ring_buffer->available() > 0);
|
||||
}
|
||||
@@ -323,8 +323,8 @@ void ResamplerSpeaker::resample_task(void *params) {
|
||||
this_resampler->taps_, this_resampler->filters_);
|
||||
|
||||
if (err == ESP_OK) {
|
||||
std::shared_ptr<RingBuffer> temp_ring_buffer =
|
||||
RingBuffer::create(this_resampler->audio_stream_info_.ms_to_bytes(this_resampler->buffer_duration_ms_));
|
||||
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = ring_buffer::RingBuffer::create(
|
||||
this_resampler->audio_stream_info_.ms_to_bytes(this_resampler->buffer_duration_ms_));
|
||||
|
||||
if (!temp_ring_buffer) {
|
||||
err = ESP_ERR_NO_MEM;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "esphome/components/audio/audio.h"
|
||||
#include "esphome/components/audio/audio_transfer_buffer.h"
|
||||
#include "esphome/components/ring_buffer/ring_buffer.h"
|
||||
#include "esphome/components/speaker/speaker.h"
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
@@ -75,7 +76,7 @@ class ResamplerSpeaker : public Component, public speaker::Speaker {
|
||||
|
||||
EventGroupHandle_t event_group_{nullptr};
|
||||
|
||||
std::weak_ptr<RingBuffer> ring_buffer_;
|
||||
std::weak_ptr<ring_buffer::RingBuffer> ring_buffer_;
|
||||
|
||||
speaker::Speaker *output_speaker_{nullptr};
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
CODEOWNERS = ["@kahrendt"]
|
||||
DEPENDENCIES = ["esp32"]
|
||||
|
||||
ring_buffer_ns = cg.esphome_ns.namespace("ring_buffer")
|
||||
RingBuffer = ring_buffer_ns.class_("RingBuffer")
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace esphome::ring_buffer {
|
||||
|
||||
static const char *const TAG = "ring_buffer";
|
||||
|
||||
@@ -135,6 +135,6 @@ bool RingBuffer::discard_bytes_(size_t discard_bytes) {
|
||||
return (bytes_read == discard_bytes);
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ring_buffer
|
||||
|
||||
#endif
|
||||
#endif // USE_ESP32
|
||||
@@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/ringbuf.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
|
||||
namespace esphome::ring_buffer {
|
||||
|
||||
class RingBuffer {
|
||||
public:
|
||||
~RingBuffer();
|
||||
|
||||
/**
|
||||
* @brief Reads from the ring buffer, waiting up to a specified number of ticks if necessary.
|
||||
*
|
||||
* Available bytes are read into the provided data pointer. If not enough bytes are available,
|
||||
* the function will wait up to `ticks_to_wait` FreeRTOS ticks before reading what is available.
|
||||
*
|
||||
* @param data Pointer to copy read data into
|
||||
* @param len Number of bytes to read
|
||||
* @param ticks_to_wait Maximum number of FreeRTOS ticks to wait (default: 0)
|
||||
* @return Number of bytes read
|
||||
*/
|
||||
size_t read(void *data, size_t len, TickType_t ticks_to_wait = 0);
|
||||
|
||||
/**
|
||||
* @brief Acquires a pointer into the ring buffer's internal storage without copying.
|
||||
*
|
||||
* The returned pointer is valid until receive_release() is called. Only one item
|
||||
* may be checked out at a time.
|
||||
*
|
||||
* @param[out] length Set to the number of bytes actually acquired (may be less than max_length at wrap boundary)
|
||||
* @param max_length Maximum number of bytes to acquire
|
||||
* @param ticks_to_wait Maximum number of FreeRTOS ticks to wait (default: 0)
|
||||
* @return Pointer into the ring buffer's internal storage, or nullptr if no data is available
|
||||
*/
|
||||
void *receive_acquire(size_t &length, size_t max_length, TickType_t ticks_to_wait = 0);
|
||||
|
||||
/**
|
||||
* @brief Releases a previously acquired ring buffer item.
|
||||
*
|
||||
* Must be called exactly once for each successful receive_acquire().
|
||||
*
|
||||
* @param item Pointer returned by receive_acquire()
|
||||
*/
|
||||
void receive_release(void *item);
|
||||
|
||||
/**
|
||||
* @brief Writes to the ring buffer, overwriting oldest data if necessary.
|
||||
*
|
||||
* The provided data is written to the ring buffer. If not enough space is available,
|
||||
* the function will overwrite the oldest data in the ring buffer.
|
||||
*
|
||||
* @param data Pointer to data for writing
|
||||
* @param len Number of bytes to write
|
||||
* @return Number of bytes written
|
||||
*/
|
||||
size_t write(const void *data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Writes to the ring buffer without overwriting oldest data.
|
||||
*
|
||||
* The provided data is written to the ring buffer. If not enough space is available,
|
||||
* the function will wait up to `ticks_to_wait` FreeRTOS ticks before writing as much as possible.
|
||||
*
|
||||
* @param data Pointer to data for writing
|
||||
* @param len Number of bytes to write
|
||||
* @param ticks_to_wait Maximum number of FreeRTOS ticks to wait (default: 0)
|
||||
* @return Number of bytes written
|
||||
*/
|
||||
size_t write_without_replacement(const void *data, size_t len, TickType_t ticks_to_wait = 0,
|
||||
bool write_partial = true);
|
||||
|
||||
/**
|
||||
* @brief Returns the number of available bytes in the ring buffer.
|
||||
*
|
||||
* This function provides the number of bytes that can be read from the ring buffer
|
||||
* without blocking the calling FreeRTOS task.
|
||||
*
|
||||
* @return Number of available bytes
|
||||
*/
|
||||
size_t available() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the number of free bytes in the ring buffer.
|
||||
*
|
||||
* This function provides the number of bytes that can be written to the ring buffer
|
||||
* without overwriting data or blocking the calling FreeRTOS task.
|
||||
*
|
||||
* @return Number of free bytes
|
||||
*/
|
||||
size_t free() const;
|
||||
|
||||
/**
|
||||
* @brief Resets the ring buffer, discarding all stored data.
|
||||
*
|
||||
* @return pdPASS if successful, pdFAIL otherwise
|
||||
*/
|
||||
BaseType_t reset();
|
||||
|
||||
enum class MemoryPreference {
|
||||
EXTERNAL_FIRST, // External RAM preferred, fall back to internal (default)
|
||||
INTERNAL_FIRST, // Internal RAM preferred, fall back to external
|
||||
};
|
||||
|
||||
static std::unique_ptr<RingBuffer> create(size_t len, MemoryPreference preference = MemoryPreference::EXTERNAL_FIRST);
|
||||
|
||||
protected:
|
||||
/// @brief Discards data from the ring buffer.
|
||||
/// @param discard_bytes amount of bytes to discard
|
||||
/// @return True if all bytes were successfully discarded, false otherwise
|
||||
bool discard_bytes_(size_t discard_bytes);
|
||||
|
||||
RingbufHandle_t handle_{nullptr};
|
||||
StaticRingbuffer_t structure_;
|
||||
uint8_t *storage_{nullptr};
|
||||
size_t size_{0};
|
||||
};
|
||||
|
||||
} // namespace esphome::ring_buffer
|
||||
|
||||
#endif // USE_ESP32
|
||||
@@ -29,7 +29,7 @@ void SoundLevelComponent::dump_config() {
|
||||
|
||||
void SoundLevelComponent::setup() {
|
||||
this->microphone_source_->add_data_callback([this](const std::vector<uint8_t> &data) {
|
||||
std::shared_ptr<RingBuffer> temp_ring_buffer = this->ring_buffer_.lock();
|
||||
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = this->ring_buffer_.lock();
|
||||
if (this->ring_buffer_.use_count() == 2) {
|
||||
// ``audio_buffer_`` and ``temp_ring_buffer`` share ownership of a ring buffer, so its safe/useful to write
|
||||
temp_ring_buffer->write((void *) data.data(), data.size());
|
||||
@@ -172,8 +172,8 @@ bool SoundLevelComponent::start_() {
|
||||
|
||||
// Allocates a new ring buffer, adds it as a source for the transfer buffer, and points ring_buffer_ to it
|
||||
this->ring_buffer_.reset(); // Reset pointer to any previous ring buffer allocation
|
||||
std::shared_ptr<RingBuffer> temp_ring_buffer =
|
||||
RingBuffer::create(this->microphone_source_->get_audio_stream_info().ms_to_bytes(RING_BUFFER_DURATION_MS));
|
||||
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = ring_buffer::RingBuffer::create(
|
||||
this->microphone_source_->get_audio_stream_info().ms_to_bytes(RING_BUFFER_DURATION_MS));
|
||||
if (temp_ring_buffer.use_count() == 0) {
|
||||
this->status_momentary_error("ring_buffer", 15000);
|
||||
this->stop_();
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
#include "esphome/components/audio/audio_transfer_buffer.h"
|
||||
#include "esphome/components/microphone/microphone_source.h"
|
||||
#include "esphome/components/ring_buffer/ring_buffer.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/ring_buffer.h"
|
||||
|
||||
namespace esphome::sound_level {
|
||||
|
||||
@@ -49,7 +49,7 @@ class SoundLevelComponent : public Component {
|
||||
sensor::Sensor *rms_sensor_{nullptr};
|
||||
|
||||
std::unique_ptr<audio::AudioSourceTransferBuffer> audio_buffer_;
|
||||
std::weak_ptr<RingBuffer> ring_buffer_;
|
||||
std::weak_ptr<ring_buffer::RingBuffer> ring_buffer_;
|
||||
|
||||
int32_t squared_peak_{0};
|
||||
uint64_t squared_samples_sum_{0};
|
||||
|
||||
@@ -315,10 +315,10 @@ void AudioPipeline::read_task(void *params) {
|
||||
if (err == ESP_OK) {
|
||||
size_t file_ring_buffer_size = this_pipeline->buffer_size_;
|
||||
|
||||
std::shared_ptr<RingBuffer> temp_ring_buffer;
|
||||
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer;
|
||||
|
||||
if (!this_pipeline->raw_file_ring_buffer_.use_count()) {
|
||||
temp_ring_buffer = RingBuffer::create(file_ring_buffer_size);
|
||||
temp_ring_buffer = ring_buffer::RingBuffer::create(file_ring_buffer_size);
|
||||
this_pipeline->raw_file_ring_buffer_ = temp_ring_buffer;
|
||||
}
|
||||
|
||||
@@ -502,7 +502,7 @@ void AudioPipeline::decode_task(void *params) {
|
||||
|
||||
if (!started_playback && has_stream_info) {
|
||||
// Verify enough data is available before starting playback
|
||||
std::shared_ptr<RingBuffer> temp_ring_buffer = this_pipeline->raw_file_ring_buffer_.lock();
|
||||
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = this_pipeline->raw_file_ring_buffer_.lock();
|
||||
if (temp_ring_buffer != nullptr && temp_ring_buffer->available() >= initial_bytes_to_buffer) {
|
||||
started_playback = true;
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#include "esphome/components/audio/audio.h"
|
||||
#include "esphome/components/audio/audio_reader.h"
|
||||
#include "esphome/components/audio/audio_decoder.h"
|
||||
#include "esphome/components/ring_buffer/ring_buffer.h"
|
||||
#include "esphome/components/speaker/speaker.h"
|
||||
|
||||
#include "esphome/core/ring_buffer.h"
|
||||
#include "esphome/core/static_task.h"
|
||||
|
||||
#include "esp_err.h"
|
||||
@@ -129,7 +129,7 @@ class AudioPipeline {
|
||||
size_t buffer_size_; // Ring buffer between reader and decoder
|
||||
size_t transfer_buffer_size_; // Internal source/sink buffers for the audio reader and decoder
|
||||
|
||||
std::weak_ptr<RingBuffer> raw_file_ring_buffer_;
|
||||
std::weak_ptr<ring_buffer::RingBuffer> raw_file_ring_buffer_;
|
||||
|
||||
// Handles basic control/state of the three tasks
|
||||
EventGroupHandle_t event_group_{nullptr};
|
||||
|
||||
@@ -15,7 +15,7 @@ from esphome.const import (
|
||||
CONF_SPEAKER,
|
||||
)
|
||||
|
||||
AUTO_LOAD = ["socket"]
|
||||
AUTO_LOAD = ["ring_buffer", "socket"]
|
||||
DEPENDENCIES = ["api", "microphone"]
|
||||
|
||||
CODEOWNERS = ["@jesserockz", "@kahrendt"]
|
||||
|
||||
@@ -30,7 +30,7 @@ VoiceAssistant::VoiceAssistant() { global_voice_assistant = this; }
|
||||
|
||||
void VoiceAssistant::setup() {
|
||||
this->mic_source_->add_data_callback([this](const std::vector<uint8_t> &data) {
|
||||
std::shared_ptr<RingBuffer> temp_ring_buffer = this->ring_buffer_;
|
||||
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = this->ring_buffer_;
|
||||
if (this->ring_buffer_.use_count() > 1) {
|
||||
temp_ring_buffer->write((void *) data.data(), data.size());
|
||||
}
|
||||
@@ -116,7 +116,7 @@ bool VoiceAssistant::allocate_buffers_() {
|
||||
#endif
|
||||
|
||||
if (this->ring_buffer_.use_count() == 0) {
|
||||
this->ring_buffer_ = RingBuffer::create(RING_BUFFER_SIZE);
|
||||
this->ring_buffer_ = ring_buffer::RingBuffer::create(RING_BUFFER_SIZE);
|
||||
if (this->ring_buffer_.use_count() == 0) {
|
||||
ESP_LOGE(TAG, "Could not allocate ring buffer");
|
||||
return false;
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/ring_buffer.h"
|
||||
|
||||
#include "esphome/components/api/api_connection.h"
|
||||
#include "esphome/components/ring_buffer/ring_buffer.h"
|
||||
#include "esphome/components/api/api_pb2.h"
|
||||
#include "esphome/components/microphone/microphone_source.h"
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
@@ -300,7 +300,7 @@ class VoiceAssistant : public Component {
|
||||
|
||||
std::string wake_word_{""};
|
||||
|
||||
std::shared_ptr<RingBuffer> ring_buffer_;
|
||||
std::shared_ptr<ring_buffer::RingBuffer> ring_buffer_;
|
||||
|
||||
bool use_wake_word_;
|
||||
uint8_t noise_suppression_level_;
|
||||
|
||||
@@ -759,10 +759,6 @@ async def to_code(config: ConfigType) -> None:
|
||||
# Platform-specific source files for core
|
||||
FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
{
|
||||
"ring_buffer.cpp": {
|
||||
PlatformFramework.ESP32_ARDUINO,
|
||||
PlatformFramework.ESP32_IDF,
|
||||
},
|
||||
"static_task.cpp": {
|
||||
PlatformFramework.ESP32_ARDUINO,
|
||||
PlatformFramework.ESP32_IDF,
|
||||
|
||||
+11
-115
@@ -2,124 +2,20 @@
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/ringbuf.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
// Deprecated: include "esphome/components/ring_buffer/ring_buffer.h" and use
|
||||
// esphome::ring_buffer::RingBuffer. This shim will be removed in 2026.11.0.
|
||||
#if __has_include("esphome/components/ring_buffer/ring_buffer.h")
|
||||
#include "esphome/components/ring_buffer/ring_buffer.h"
|
||||
#else
|
||||
#error \
|
||||
"esphome/components/ring_buffer/ring_buffer.h not found. Add 'ring_buffer' to your component's AUTO_LOAD list to use esphome::ring_buffer::RingBuffer."
|
||||
#endif
|
||||
#include "esphome/core/helpers.h" // for ESPDEPRECATED
|
||||
|
||||
namespace esphome {
|
||||
|
||||
class RingBuffer {
|
||||
public:
|
||||
~RingBuffer();
|
||||
|
||||
/**
|
||||
* @brief Reads from the ring buffer, waiting up to a specified number of ticks if necessary.
|
||||
*
|
||||
* Available bytes are read into the provided data pointer. If not enough bytes are available,
|
||||
* the function will wait up to `ticks_to_wait` FreeRTOS ticks before reading what is available.
|
||||
*
|
||||
* @param data Pointer to copy read data into
|
||||
* @param len Number of bytes to read
|
||||
* @param ticks_to_wait Maximum number of FreeRTOS ticks to wait (default: 0)
|
||||
* @return Number of bytes read
|
||||
*/
|
||||
size_t read(void *data, size_t len, TickType_t ticks_to_wait = 0);
|
||||
|
||||
/**
|
||||
* @brief Acquires a pointer into the ring buffer's internal storage without copying.
|
||||
*
|
||||
* The returned pointer is valid until receive_release() is called. Only one item
|
||||
* may be checked out at a time.
|
||||
*
|
||||
* @param[out] length Set to the number of bytes actually acquired (may be less than max_length at wrap boundary)
|
||||
* @param max_length Maximum number of bytes to acquire
|
||||
* @param ticks_to_wait Maximum number of FreeRTOS ticks to wait (default: 0)
|
||||
* @return Pointer into the ring buffer's internal storage, or nullptr if no data is available
|
||||
*/
|
||||
void *receive_acquire(size_t &length, size_t max_length, TickType_t ticks_to_wait = 0);
|
||||
|
||||
/**
|
||||
* @brief Releases a previously acquired ring buffer item.
|
||||
*
|
||||
* Must be called exactly once for each successful receive_acquire().
|
||||
*
|
||||
* @param item Pointer returned by receive_acquire()
|
||||
*/
|
||||
void receive_release(void *item);
|
||||
|
||||
/**
|
||||
* @brief Writes to the ring buffer, overwriting oldest data if necessary.
|
||||
*
|
||||
* The provided data is written to the ring buffer. If not enough space is available,
|
||||
* the function will overwrite the oldest data in the ring buffer.
|
||||
*
|
||||
* @param data Pointer to data for writing
|
||||
* @param len Number of bytes to write
|
||||
* @return Number of bytes written
|
||||
*/
|
||||
size_t write(const void *data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Writes to the ring buffer without overwriting oldest data.
|
||||
*
|
||||
* The provided data is written to the ring buffer. If not enough space is available,
|
||||
* the function will wait up to `ticks_to_wait` FreeRTOS ticks before writing as much as possible.
|
||||
*
|
||||
* @param data Pointer to data for writing
|
||||
* @param len Number of bytes to write
|
||||
* @param ticks_to_wait Maximum number of FreeRTOS ticks to wait (default: 0)
|
||||
* @return Number of bytes written
|
||||
*/
|
||||
size_t write_without_replacement(const void *data, size_t len, TickType_t ticks_to_wait = 0,
|
||||
bool write_partial = true);
|
||||
|
||||
/**
|
||||
* @brief Returns the number of available bytes in the ring buffer.
|
||||
*
|
||||
* This function provides the number of bytes that can be read from the ring buffer
|
||||
* without blocking the calling FreeRTOS task.
|
||||
*
|
||||
* @return Number of available bytes
|
||||
*/
|
||||
size_t available() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the number of free bytes in the ring buffer.
|
||||
*
|
||||
* This function provides the number of bytes that can be written to the ring buffer
|
||||
* without overwriting data or blocking the calling FreeRTOS task.
|
||||
*
|
||||
* @return Number of free bytes
|
||||
*/
|
||||
size_t free() const;
|
||||
|
||||
/**
|
||||
* @brief Resets the ring buffer, discarding all stored data.
|
||||
*
|
||||
* @return pdPASS if successful, pdFAIL otherwise
|
||||
*/
|
||||
BaseType_t reset();
|
||||
|
||||
enum class MemoryPreference {
|
||||
EXTERNAL_FIRST, // External RAM preferred, fall back to internal (default)
|
||||
INTERNAL_FIRST, // Internal RAM preferred, fall back to external
|
||||
};
|
||||
|
||||
static std::unique_ptr<RingBuffer> create(size_t len, MemoryPreference preference = MemoryPreference::EXTERNAL_FIRST);
|
||||
|
||||
protected:
|
||||
/// @brief Discards data from the ring buffer.
|
||||
/// @param discard_bytes amount of bytes to discard
|
||||
/// @return True if all bytes were successfully discarded, false otherwise
|
||||
bool discard_bytes_(size_t discard_bytes);
|
||||
|
||||
RingbufHandle_t handle_{nullptr};
|
||||
StaticRingbuffer_t structure_;
|
||||
uint8_t *storage_{nullptr};
|
||||
size_t size_{0};
|
||||
};
|
||||
using RingBuffer ESPDEPRECATED("Use esphome::ring_buffer::RingBuffer instead. Removed in 2026.11.0.",
|
||||
"2026.5.0") = ring_buffer::RingBuffer;
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
|
||||
+9
-1
@@ -199,7 +199,15 @@ def copy_src_tree():
|
||||
# Build #include list for esphome.h
|
||||
# X-macro files are included multiple times with different macro definitions
|
||||
# and must not be included bare in esphome.h
|
||||
esphome_h_exclude = {Path(ENTITY_TYPES_H_TARGET)}
|
||||
# Deprecated headers that re-export from a relocated component must not be
|
||||
# auto-included, since their #include of the new path only resolves when the
|
||||
# new component is loaded by a consumer.
|
||||
esphome_h_exclude = {
|
||||
Path(ENTITY_TYPES_H_TARGET),
|
||||
Path(
|
||||
"esphome/core/ring_buffer.h"
|
||||
), # moved to components/ring_buffer/, removed in 2026.11.0
|
||||
}
|
||||
include_l = []
|
||||
for target, _ in source_files_l:
|
||||
if target.suffix in HEADER_FILE_EXTENSIONS and target not in esphome_h_exclude:
|
||||
|
||||
Reference in New Issue
Block a user