ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
usb_audio_speaker.h
Go to the documentation of this file.
1#pragma once
2
4
5#ifdef USE_USB_AUDIO
6
8
13
14#include <atomic>
15
16namespace esphome::usb_audio {
17
19 public Component,
20 public Parented<USBAudioComponent>,
21 public USBAudioStream {
22 public:
23 USBAudioSpeaker(const audio::AudioStreamInfo &stream_info, uint32_t buffer_size, uint32_t write_timeout_ms)
24 : USBAudioStream(buffer_size), usb_stream_info_(stream_info), write_timeout_ms_(write_timeout_ms) {
25 this->audio_stream_info_ = stream_info;
26 }
27
28 void setup() override;
29 void dump_config() override;
30 void loop() override;
31
32 // Safe from any task; loop() acts on the request.
33 void start() override;
34 void stop() override;
35 void finish() override;
36
37 size_t play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) override;
38 size_t play(const uint8_t *data, size_t length) override;
39 bool has_buffered_data() const override;
40
41 void set_volume(float volume) override;
42 void set_mute_state(bool mute_state) override;
43
45 void on_stream_state(StreamEvent event) override;
46 bool stream_idle() const override { return this->writes_in_flight_ == 0; }
47 void on_transfer_taken() override;
48
49 protected:
50 static constexpr uint8_t COMMAND_START = 1 << 0;
51 static constexpr uint8_t COMMAND_STOP = 1 << 1;
52 static constexpr uint8_t COMMAND_FINISH = 1 << 2;
53 void request_(uint8_t set, uint8_t clear);
54 void deliver_reports_();
55 void begin_finishing_();
56 void stop_();
57 size_t play_internal_(const uint8_t *data, size_t length, uint32_t budget_ms);
59 void pad_with_silence_();
61 esp_err_t write_(const uint8_t *data, size_t length, uint32_t timeout_ms, bool padding);
63 size_t ring_room_() const;
64 bool has_buffered_data_(int64_t now_us) const;
65
66 // The configured format the USB stream runs with. Callers such as the media pipeline set audio_stream_info_ for
67 // each item, so play() checks it against this instead of reopening the stream with another format.
70 // Frame-aligned size of each driver write, set in setup().
72 // The driver's packet sizing, mirrored so on_transfer_taken() knows how much each transfer takes: whole frames per
73 // 1 ms packet, plus a frame whenever the remainder of sample_rate / 1000 adds up. Set in setup().
77 // Bytes in the largest USB transfer, how long one transfer plays and how long all the driver keeps in flight play,
78 // and the batch of frames reported at a time; set in setup().
83 // Silence still to write while finishing.
85 // Bytes in the driver's ring buffer: added before each write, since the driver can take a transfer inside it, and
86 // taken off for each transfer it takes. ring_padding_ is the silence padding at its end.
87 std::atomic<uint32_t> ring_bytes_{0};
88 std::atomic<uint32_t> ring_padding_{0};
89 // Low 32 bits of esp_timer_get_time() when the driver last took a transfer.
90 std::atomic<uint32_t> last_taken_us_{0};
91 // on_transfer_taken() runs on the USB driver's task, which must not block, and inside writes, so its state is
92 // guarded by a spinlock and the reports it collects are delivered from loop(). Reset while the stream is stopped.
93 portMUX_TYPE taken_lock_ = portMUX_INITIALIZER_UNLOCKED;
98 // When the last transfer taken finishes playing; reset on a start, so the next one starts the chain from now.
99 int64_t last_finish_us_{0};
100 std::atomic<uint8_t> commands_{0};
101 // play() calls and silence padding that may be using the device handle; the hub closes a lost device only at zero.
102 std::atomic<uint8_t> writes_in_flight_{0};
103 // Set while play() rejects audio in a format other than usb_stream_info_, so the error is logged once.
104 std::atomic<bool> format_mismatch_{false};
105 bool finishing_{false};
106 // finish() arrived while the stream was still starting.
108};
109
110} // namespace esphome::usb_audio
111
112#endif
Helper class to easily give an object a parent of type T.
Definition helpers.h:2073
audio::AudioStreamInfo audio_stream_info_
Definition speaker.h:131
USBAudioSpeaker(const audio::AudioStreamInfo &stream_info, uint32_t buffer_size, uint32_t write_timeout_ms)
void request_(uint8_t set, uint8_t clear)
void set_mute_state(bool mute_state) override
static constexpr uint8_t COMMAND_START
static constexpr uint8_t COMMAND_STOP
std::atomic< uint32_t > ring_padding_
size_t play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) override
void pad_with_silence_()
Write silence until a whole USB transfer is padded, so the driver sends a final partial transfer.
audio::AudioStreamInfo get_usb_stream_info() override
size_t play_internal_(const uint8_t *data, size_t length, uint32_t budget_ms)
static constexpr uint8_t COMMAND_FINISH
void set_volume(float volume) override
const audio::AudioStreamInfo usb_stream_info_
void on_stream_state(StreamEvent event) override
bool has_buffered_data_(int64_t now_us) const
esp_err_t write_(const uint8_t *data, size_t length, uint32_t timeout_ms, bool padding)
Write to the driver, counting the bytes in ring_bytes_ (and in ring_padding_ for silence padding).
std::atomic< uint32_t > last_taken_us_
size_t ring_room_() const
Bytes that can be written without filling the driver's ring buffer past buffer_size_.
A speaker or microphone platform that streams through the hub. The hub calls it on the main loop only...
Definition usb_audio.h:39
StreamEvent
What the hub reports to a platform about its stream.
Definition usb_audio.h:29
static void uint32_t