23static constexpr UBaseType_t UAC_TASK_PRIORITY = 19;
24static constexpr uint32_t UAC_TASK_STACK_SIZE = 4096;
26static constexpr UBaseType_t WORKER_TASK_PRIORITY = 5;
27static constexpr uint32_t WORKER_TASK_STACK_SIZE = 4096;
29static constexpr uint8_t MAX_CONTROL_ATTEMPTS = 5;
30static constexpr uint32_t CONTROL_RETRY_MS = 100;
32static constexpr uint8_t MAX_CLOSE_ATTEMPTS = 5;
33static constexpr uint32_t CLOSE_RETRY_MS = 500;
35static const LogString *endpoint_name(
Endpoint endpoint) {
36 return endpoint ==
Endpoint::SPEAKER ? LOG_STR(
"Speaker") : LOG_STR(
"Microphone");
39static uint8_t discrete_rate_count(
const uac_host_dev_alt_param_t &alt) {
40 return std::min<uint8_t>(alt.sample_freq_type, UAC_FREQ_NUM_MAX);
43static bool alt_supports_rate(
const uac_host_dev_alt_param_t &alt,
uint32_t sample_rate) {
44 if (alt.sample_freq_type == 0) {
45 return sample_rate >= alt.sample_freq_lower && sample_rate <= alt.sample_freq_upper;
47 const uint8_t
count = discrete_rate_count(alt);
48 return std::find(alt.sample_freq, alt.sample_freq +
count, sample_rate) != alt.sample_freq +
count;
53 &this->event_queue_struct_);
57 uac_host_driver_config_t uac_cfg = {
58 .create_background_task =
true,
59 .task_priority = UAC_TASK_PRIORITY,
60 .stack_size = UAC_TASK_STACK_SIZE,
66 esp_err_t err = uac_host_install(&uac_cfg);
68 ESP_LOGE(TAG,
"uac_host_install failed: %s", esp_err_to_name(err));
77 WORKER_TASK_PRIORITY,
false)) {
78 this->
mark_failed(LOG_STR(
"Failed to create worker task"));
89 ESP_LOGW(TAG,
"%" PRIu32
" USB transfer errors", errors);
93 if (std::any_of(this->
endpoints_.begin(), this->endpoints_.end(),
94 [](
const EndpointState &ep) { return ep.closing != nullptr; })) {
105 for (UBaseType_t queued = uxQueueMessagesWaiting(this->
event_queue_);
106 queued > 0 && xQueueReceive(this->
event_queue_, &event, 0) == pdTRUE; queued--) {
116 " Device connected: %s\n"
117 " Speaker registered: %s\n"
118 " Microphone registered: %s",
119 YESNO(speaker.handle.load() !=
nullptr || microphone.handle.load() !=
nullptr),
120 YESNO(speaker.platform !=
nullptr), YESNO(microphone.platform !=
nullptr));
143 esp_err_t err = uac_host_uninstall();
144 if (err == ESP_ERR_INVALID_STATE) {
148 ESP_LOGW(TAG,
"uac_host_uninstall returned %s", esp_err_to_name(err));
159 const uac_host_device_handle_t
handle = ep.handle;
161 if (ep.open_failed && !ep.open_pending) {
163 ep.open_failed =
false;
164 this->
open_stream_(endpoint, ep.failed_addr, ep.failed_iface);
167 ESP_LOGD(TAG,
"%s interface not connected yet", LOG_STR_ARG(endpoint_name(endpoint)));
170 if (ep.start_pending) {
174 if (!ep.stream_started) {
175 if (ep.start_rejected) {
189 op.
seq = ep.request_seq;
199 const uac_host_device_handle_t
handle = ep.handle;
201 if (!ep.stream_started ||
handle ==
nullptr) {
218 timeout_ms = waited < timeout_ms ? timeout_ms - waited : 0;
223 if (
millis() - start >= timeout_ms) {
233 return ESP_ERR_TIMEOUT;
236 if (handle ==
nullptr) {
238 return ESP_ERR_INVALID_STATE;
243 esp_err_t err = uac_host_device_read(
handle,
buffer,
static_cast<uint32_t>(
size), &actual, pdMS_TO_TICKS(timeout_ms));
245 *bytes_read = actual;
252 return ESP_ERR_TIMEOUT;
255 if (handle ==
nullptr) {
257 return ESP_ERR_INVALID_STATE;
259 esp_err_t err = uac_host_device_write(
handle,
const_cast<uint8_t *
>(data),
static_cast<uint32_t>(
length),
260 pdMS_TO_TICKS(timeout_ms));
262 if (err == ESP_FAIL || err == ESP_ERR_INVALID_STATE) {
264 err = ESP_ERR_TIMEOUT;
270 switch (event.
type) {
273 if (ep.closing !=
nullptr) {
276 ep.connect_deferred =
true;
277 ep.deferred_addr =
event.addr;
278 ep.deferred_iface =
event.iface_num;
304 const LogString *name = endpoint_name(endpoint);
305 if (ep.platform ==
nullptr) {
306 ESP_LOGD(TAG,
"%s interface connected but not configured", LOG_STR_ARG(name));
309 if (ep.open_pending) {
312 ep.connect_deferred =
true;
313 ep.deferred_addr = addr;
314 ep.deferred_iface = iface_num;
317 if (ep.handle.load() !=
nullptr) {
318 ESP_LOGW(TAG,
"%s interface already open, ignoring duplicate event", LOG_STR_ARG(name));
330 event.endpoint = endpoint;
332 event.iface_num = iface_num;
336 ep.open_pending =
true;
341 ep.open_pending =
false;
342 bool lost_meanwhile =
false;
344 if (event.
ok && lost == event.
handle) {
346 lost_meanwhile =
true;
349 if (std::none_of(this->
endpoints_.begin(), this->endpoints_.end(),
350 [](
const EndpointState &other) { return other.open_pending; })) {
353 this->lost_while_opening_.fill(
nullptr);
356 if (ep.connect_deferred) {
358 ep.connect_deferred =
false;
363 ep.open_failed =
true;
364 ep.failed_addr =
event.addr;
365 ep.failed_iface =
event.iface_num;
371 ep.open_failed =
false;
372 ep.handle =
event.handle;
373 ep.iface =
event.iface_num;
374 ESP_LOGI(TAG,
"%s interface %u connected (addr=%u)", LOG_STR_ARG(endpoint_name(event.
endpoint)), ep.iface,
376 if (lost_meanwhile) {
382 ep.connect_deferred =
false;
398 ep.start_pending =
true;
403 ep.start_pending =
false;
404 if (event.
handle != ep.handle.load()) {
409 ep.start_rejected =
event.rejected;
415 ep.stream_started =
true;
432 if (event.
handle != ep.handle.load() || event.
seq != ep.request_seq || !ep.active) {
451 this->
speaker_volume_ =
static_cast<uint8_t
>(std::lround(std::clamp(volume, 0.0f, 1.0f) * 100.0f));
472 if (xQueueSend(this->
op_queue_, &op, 0) != pdTRUE) {
479 for (
size_t i = 0; i < this->
endpoints_.size(); i++) {
482 const auto endpoint =
static_cast<Endpoint>(i);
483 ESP_LOGI(TAG,
"%s interface %u disconnected", LOG_STR_ARG(endpoint_name(endpoint)), ep.iface);
488 if (std::any_of(this->
endpoints_.begin(), this->endpoints_.end(),
492 if (lost ==
nullptr || lost ==
handle) {
498 this->lost_while_opening_[0] =
handle;
501 ESP_LOGD(TAG,
"Disconnect event for an interface with no active stream");
507 const uac_host_device_handle_t
handle = ep.handle.exchange(
nullptr);
512 ep.close_queued =
false;
519 for (
size_t i = 0; i < this->
endpoints_.size(); i++) {
521 if (ep.closing ==
nullptr) {
526 if (ep.close_queued || !ep.platform->stream_idle()) {
529 if (ep.close_attempts > 0 &&
static_cast<int32_t
>(now - ep.close_retry_ms) < 0) {
537 op.
stop = ep.stream_started;
545 ep.close_queued =
false;
546 if (!event.
ok && ++ep.close_attempts < MAX_CLOSE_ATTEMPTS) {
548 ep.close_retry_ms = now + CLOSE_RETRY_MS;
552 ESP_LOGE(TAG,
"Giving up closing the %s interface after %u attempts", LOG_STR_ARG(endpoint_name(event.
endpoint)),
555 ep.close_attempts = 0;
556 ep.closing =
nullptr;
557 ep.stream_started =
false;
558 ep.start_rejected =
false;
559 ep.start_pending =
false;
560 if (ep.connect_deferred) {
562 ep.connect_deferred =
false;
568 if (xQueueSend(this->
op_queue_, &op, 0) != pdTRUE) {
569 ESP_LOGW(TAG,
"USB audio work queue full, dropping %s request", LOG_STR_ARG(endpoint_name(op.
endpoint)));
576 if (xQueueSend(this->
event_queue_, &event, 0) != pdTRUE) {
577 ESP_LOGW(TAG,
"USB audio event queue full, dropping event");
586 if (xQueueReceive(self->op_queue_, &op, portMAX_DELAY) != pdTRUE) {
589 self->lock_driver_();
591 self->unlock_driver_();
606 const LogString *name = endpoint_name(op.
endpoint);
614 uint32_t threshold = std::max<uint32_t>(buffer_size / 4, 1U);
619 threshold = buffer_size;
622 uac_host_device_config_t dev_cfg = {
625 .buffer_size = buffer_size,
626 .buffer_threshold = threshold,
628 .callback_arg =
this,
630 uac_host_device_handle_t
handle{
nullptr};
631 esp_err_t err = uac_host_device_open(&dev_cfg, &
handle);
633 ESP_LOGE(TAG,
"Failed to open %s interface: %s", LOG_STR_ARG(name), esp_err_to_name(err));
636 result.
ok = err == ESP_OK;
645 uac_host_stream_config_t stm_cfg{};
647 esp_err_t err = uac_host_device_start(op.
handle, &stm_cfg);
649 result.
ok = err == ESP_OK;
651 result.
rejected = err == ESP_ERR_NOT_SUPPORTED;
653 ESP_LOGW(TAG,
"Failed to start %s stream: %s", LOG_STR_ARG(name), esp_err_to_name(err));
660 esp_err_t err = uac_host_device_resume(op.
handle);
664 result.
ok = err == ESP_OK;
666 ESP_LOGW(TAG,
"Failed to resume %s stream: %s", LOG_STR_ARG(name), esp_err_to_name(err));
672 esp_err_t err = uac_host_device_suspend(op.
handle);
673 if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
674 ESP_LOGW(TAG,
"Failed to suspend %s stream: %s", LOG_STR_ARG(name), esp_err_to_name(err));
680 esp_err_t err = uac_host_device_stop(op.
handle);
681 if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
682 ESP_LOGW(TAG,
"Failed to stop %s stream: %s", LOG_STR_ARG(name), esp_err_to_name(err));
685 esp_err_t err = uac_host_device_close(op.
handle);
687 result.
ok = err == ESP_OK;
690 ESP_LOGW(TAG,
"uac_host_device_close failed: %s", esp_err_to_name(err));
697 control->supported =
true;
698 control->attempts = 0;
710 vTaskDelay(pdMS_TO_TICKS(CONTROL_RETRY_MS));
720 uac_host_stream_config_t &stream_config,
bool &rejected) {
721 const LogString *name = endpoint_name(endpoint);
727 uac_host_dev_info_t dev_info{};
728 esp_err_t err = uac_host_get_device_info(
handle, &dev_info);
730 ESP_LOGW(TAG,
"Failed to query %s interface info: %s", LOG_STR_ARG(name), esp_err_to_name(err));
736 uint8_t selected_alt = 0;
737 uint8_t selected_subframe = 0;
738 for (uint8_t alt = 1; alt <= dev_info.iface_alt_num; alt++) {
739 uac_host_dev_alt_param_t alt_param{};
740 if (uac_host_get_device_alt_param(
handle, alt, &alt_param) != ESP_OK || alt_param.channels != channels ||
741 alt_param.bit_resolution != bits || !alt_supports_rate(alt_param, sample_rate)) {
745 selected_subframe = alt_param.subframe_size;
746 if (alt_param.sample_freq_type == 0) {
753 if (selected_alt != 0 && selected_subframe * 8 == bits) {
754 stream_config.channels = channels;
755 stream_config.bit_resolution = bits;
756 stream_config.sample_freq = sample_rate;
757 stream_config.flags = 0;
762 if (selected_alt != 0) {
763 ESP_LOGW(TAG,
"%s interface %u would stream alt %u with %u-bit samples padded to %u bytes, which is not supported",
764 LOG_STR_ARG(name), dev_info.iface_num, selected_alt, bits, selected_subframe);
766 ESP_LOGW(TAG,
"%s interface %u does not support requested format (channels=%u bits=%u sample_rate=%" PRIu32
" Hz)",
767 LOG_STR_ARG(name), dev_info.iface_num, channels, bits, sample_rate);
768 for (uint8_t alt = 1; alt <= dev_info.iface_alt_num; alt++) {
769 uac_host_dev_alt_param_t alt_param{};
770 if (uac_host_get_device_alt_param(
handle, alt, &alt_param) != ESP_OK) {
773 if (alt_param.sample_freq_type == 0) {
774 ESP_LOGI(TAG,
" alt %u: channels=%u bits=%u subframe=%u bytes sample rate range=%" PRIu32
"-%" PRIu32
" Hz", alt,
775 alt_param.channels, alt_param.bit_resolution, alt_param.subframe_size, alt_param.sample_freq_lower,
776 alt_param.sample_freq_upper);
779 ESP_LOGI(TAG,
" alt %u: channels=%u bits=%u subframe=%u bytes sample rates:", alt, alt_param.channels,
780 alt_param.bit_resolution, alt_param.subframe_size);
781 const uint8_t
count = discrete_rate_count(alt_param);
782 for (uint8_t i = 0; i <
count; i++) {
784 if (std::find(alt_param.sample_freq, alt_param.sample_freq + i, alt_param.sample_freq[i]) ==
785 alt_param.sample_freq + i) {
786 ESP_LOGI(TAG,
" %" PRIu32
" Hz", alt_param.sample_freq[i]);
795 if (handle ==
nullptr) {
800 if (this->
volume_.
pending.exchange(
false) && this->volume_.supported) {
804 if (this->
mute_.
pending.exchange(
false) && this->mute_.supported) {
815 if (err == ESP_ERR_NOT_SUPPORTED) {
816 ESP_LOGW(TAG,
"Speaker %s control not supported by device", LOG_STR_ARG(name));
820 if (err == ESP_ERR_INVALID_STATE) {
825 if (++control.
attempts >= MAX_CONTROL_ATTEMPTS) {
826 ESP_LOGW(TAG,
"Failed to set speaker %s: %s", LOG_STR_ARG(name), esp_err_to_name(err));
839 case UAC_HOST_DRIVER_EVENT_TX_CONNECTED:
842 case UAC_HOST_DRIVER_EVENT_RX_CONNECTED:
851 self->post_event_(evt);
857 if (event == UAC_HOST_DEVICE_EVENT_TX_DONE) {
860 if (speaker.platform !=
nullptr &&
handle == speaker.handle.load()) {
861 speaker.platform->on_transfer_taken();
863 }
else if (event == UAC_HOST_DEVICE_EVENT_TRANSFER_ERROR) {
864 self->transfer_errors_.fetch_add(1, std::memory_order_relaxed);
865 self->enable_loop_soon_any_context();
866 }
else if (event == UAC_HOST_DRIVER_EVENT_DISCONNECTED) {
870 self->post_event_(evt);
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void mark_failed()
Mark this component as failed.
void enable_loop_soon_any_context()
Thread and ISR-safe version of enable_loop() that can be called from any context.
void disable_loop()
Disable this component's loop.
bool create(TaskFunction_t fn, const char *name, uint32_t stack_size, void *param, UBaseType_t priority, bool use_psram, BaseType_t core_id=tskNO_AFFINITY)
Allocate the TCB (always internal RAM) and the stack, then create the task.
uint8_t get_bits_per_sample() const
uint8_t get_channels() const
uint32_t get_sample_rate() const
void lock_driver_()
Keep platform reads and writes out of the driver while the worker uses it.
static void worker_task(void *param)
void on_started_(const USBAudioEvent &event)
void dump_config() override
void run_op_(const Op &op)
bool finish_closes_(uint32_t now)
void on_closed_(const USBAudioEvent &event, uint32_t now)
void process_events_(uint32_t now)
bool populate_stream_config_(Endpoint endpoint, uac_host_device_handle_t handle, uac_host_stream_config_t &stream_config, bool &rejected)
void begin_close_(Endpoint endpoint)
void set_speaker_mute_state(bool mute_state)
bool apply_control_(ControlState &control, esp_err_t err, const LogString *name)
void stream_failed_(Endpoint endpoint)
std::atomic< bool > driver_busy_
void open_stream_(Endpoint endpoint, uint8_t addr, uint8_t iface_num)
static constexpr size_t EVENT_QUEUE_LENGTH
std::atomic< bool > speaker_muted_
bool apply_pending_speaker_controls_()
Send pending volume and mute; returns true when one should be retried later.
static void device_event_stub(uac_host_device_handle_t handle, uac_host_device_event_t event, void *user_data)
std::array< EndpointState, 2 > endpoints_
std::array< uac_host_device_handle_t, 2 > lost_while_opening_
void start_stream_(Endpoint endpoint)
void handle_disconnect_(uac_host_device_handle_t handle)
std::atomic< bool > controls_queued_
static constexpr uint32_t SPEAKER_RING_SLACK
QueueHandle_t event_queue_
EndpointState & endpoint_(Endpoint endpoint)
std::atomic< uint32_t > transfer_errors_
void process_event_(const USBAudioEvent &event, uint32_t now)
std::array< uint8_t, EVENT_QUEUE_LENGTH *sizeof(USBAudioEvent)> event_queue_storage_
std::array< uint8_t, OP_QUEUE_LENGTH *sizeof(Op)> op_queue_storage_
void post_event_(const USBAudioEvent &event)
bool queue_op_(const Op &op)
void suspend(Endpoint endpoint)
void on_resumed_(const USBAudioEvent &event)
esp_err_t read_microphone(uint8_t *buffer, size_t size, size_t *bytes_read, uint32_t timeout_ms)
void on_opened_(const USBAudioEvent &event)
void resume(Endpoint endpoint)
Start streaming as soon as the device is connected; the platform hears back through on_stream_state()...
static constexpr size_t OP_QUEUE_LENGTH
static void driver_event_stub(uint8_t addr, uint8_t iface_num, uac_host_driver_event_t event, void *user_data)
esp_err_t write_speaker(const uint8_t *data, size_t length, uint32_t timeout_ms)
bool enter_driver_(uint32_t &timeout_ms)
Platform side: wait, within timeout_ms, until the worker is not using the driver, and count the calle...
std::atomic< uint8_t > driver_users_
void set_speaker_volume_level(float volume)
Safe from any task; the worker sends the values to the device.
std::atomic< uint8_t > speaker_volume_
virtual audio::AudioStreamInfo get_usb_stream_info()=0
The fixed format the hub opens the stream with.
uint32_t get_buffer_size() const
ESPHOME_LOG_TAG(TAG, "usb_audio.mic")
uint32_t IRAM_ATTR HOT millis()
Application App
Global storage of Application pointer - only one Application can exist.
std::atomic< bool > pending
USBAudioStream * platform
std::atomic< uac_host_device_handle_t > handle
A blocking driver call, run on the worker.
uac_host_device_handle_t handle
Posted to the main loop, by the USB driver task (CONNECTED, DISCONNECTED) or the hub's worker (the re...
uac_host_device_handle_t handle
spi_device_handle_t handle