18static constexpr size_t MAX_TIMEOUT_RETRIES = 4;
20static constexpr uint8_t SILENCE[240] = {};
27 this->
packet_bytes_ = info.frames_to_bytes(info.get_sample_rate() / 1000);
29 this->
transfer_bytes_ = info.frames_to_bytes((info.get_sample_rate() + 999) / 1000 * CONFIG_UAC_NUM_PACKETS_PER_URB);
30 this->
transfer_us_ = CONFIG_UAC_NUM_PACKETS_PER_URB * 1000;
33 this->
report_frames_ = std::max<uint32_t>(info.get_sample_rate() / 100, 1);
41 " Sample rate: %" PRIu32
" Hz\n"
42 " Bits per sample: %u\n"
44 " Buffer size: %" PRIu32
"\n"
45 " Write timeout: %" PRIu32
" ms",
47 this->usb_stream_info_.get_channels(), this->buffer_size_, this->write_timeout_ms_);
52 const uint8_t commands = this->
commands_.exchange(0);
93 this->
commands_.fetch_and(
static_cast<uint8_t
>(~clear));
143 ESP_LOGW(TAG,
"USB device disconnected, stopping playback");
163 "Audio format (%" PRIu32
" Hz, %u bits, %u channels) does not match the USB stream; use a resampler "
165 info.get_sample_rate(), info.get_bits_per_sample(), info.get_channels());
168 vTaskDelay(pdMS_TO_TICKS(budget_ms));
178 vTaskDelay(pdMS_TO_TICKS(budget_ms));
190 size_t timeout_retries = 0;
192 const uint32_t elapsed = now - start_ms;
193 if (budget_ms > 0 && elapsed >= budget_ms) {
199 if (budget_ms == 0) {
206 const uint32_t timeout_ms = budget_ms > 0 ? budget_ms - elapsed : 0;
207 const esp_err_t err = this->
write_(data +
written, chunk, timeout_ms,
false);
212 }
else if (err == ESP_ERR_INVALID_STATE) {
215 }
else if (err != ESP_ERR_TIMEOUT) {
216 ESP_LOGW(TAG,
"Write error: %s", esp_err_to_name(err));
218 }
else if (budget_ms == 0 || ++timeout_retries >= MAX_TIMEOUT_RETRIES) {
241 const esp_err_t err = this->
parent_->write_speaker(data,
length, timeout_ms);
261 if (chunk == 0 || this->
write_(SILENCE, chunk, 0,
true) != ESP_OK) {
277 for (
int i = 0; i < CONFIG_UAC_NUM_PACKETS_PER_URB; i++) {
349 this->
parent_->set_speaker_volume_level(volume);
354 this->
parent_->set_speaker_mute_state(mute_state);
void enable_loop_soon_any_context()
Thread and ISR-safe version of enable_loop() that can be called from any context.
void enable_loop()
Enable this component's loop.
void disable_loop()
Disable this component's loop.
USBAudioComponent * parent_
size_t frames_to_bytes(uint32_t frames) const
Converts frames to bytes.
uint32_t bytes_to_frames(size_t bytes) const
Convert bytes to frames.
uint32_t get_sample_rate() const
virtual void set_volume(float volume)
CallbackManager< void(uint32_t, int64_t)> audio_output_callback_
virtual void set_mute_state(bool mute_state)
audio::AudioStreamInfo audio_stream_info_
void request_(uint8_t set, uint8_t clear)
std::atomic< uint8_t > writes_in_flight_
void on_transfer_taken() override
void set_mute_state(bool mute_state) override
static constexpr uint8_t COMMAND_START
static constexpr uint8_t COMMAND_STOP
uint32_t write_timeout_ms_
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.
size_t play_internal_(const uint8_t *data, size_t length, uint32_t budget_ms)
static constexpr uint8_t COMMAND_FINISH
bool has_buffered_data() const override
void set_volume(float volume) override
std::atomic< bool > format_mismatch_
const audio::AudioStreamInfo usb_stream_info_
void on_stream_state(StreamEvent event) override
uint32_t packet_frac_accum_
std::atomic< uint8_t > commands_
std::atomic< uint32_t > ring_bytes_
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).
uint32_t report_frames_pending_
std::atomic< uint32_t > last_taken_us_
void dump_config() override
int64_t report_finish_us_
size_t ring_room_() const
Bytes that can be written without filling the driver's ring buffer past buffer_size_.
uint32_t unreported_frames_
ESPHOME_LOG_TAG(TAG, "usb_audio.mic")
StreamEvent
What the hub reports to a platform about its stream.
uint32_t IRAM_ATTR HOT millis()
int64_t esp_timer_get_time(void)