esphome 2024.10.3__py3-none-any.whl → 2024.11.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. esphome/__main__.py +22 -4
  2. esphome/automation.py +29 -2
  3. esphome/components/animation/__init__.py +5 -8
  4. esphome/components/animation/animation.cpp +1 -1
  5. esphome/components/audio/__init__.py +9 -0
  6. esphome/components/audio/audio.h +21 -0
  7. esphome/components/axs15231/__init__.py +6 -0
  8. esphome/components/axs15231/touchscreen/__init__.py +36 -0
  9. esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +64 -0
  10. esphome/components/axs15231/touchscreen/axs15231_touchscreen.h +27 -0
  11. esphome/components/bme68x_bsec2/__init__.py +1 -1
  12. esphome/components/bytebuffer/__init__.py +5 -0
  13. esphome/components/bytebuffer/bytebuffer.h +421 -0
  14. esphome/components/climate/__init__.py +14 -13
  15. esphome/components/datetime/__init__.py +3 -3
  16. esphome/components/debug/debug_esp32.cpp +16 -8
  17. esphome/components/dfplayer/dfplayer.cpp +132 -6
  18. esphome/components/dfplayer/dfplayer.h +19 -53
  19. esphome/components/display/display.cpp +142 -0
  20. esphome/components/display/display.h +7 -0
  21. esphome/components/es8311/__init__.py +0 -0
  22. esphome/components/es8311/audio_dac.py +70 -0
  23. esphome/components/es8311/es8311.cpp +227 -0
  24. esphome/components/es8311/es8311.h +135 -0
  25. esphome/components/es8311/es8311_const.h +195 -0
  26. esphome/components/esp32/boards.py +199 -1
  27. esphome/components/esp32/gpio.py +3 -1
  28. esphome/components/esp32_ble/const_esp32c6.h +7 -0
  29. esphome/components/esp32_ble_client/ble_client_base.h +1 -1
  30. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +2 -1
  31. esphome/components/esp32_rmt_led_strip/led_strip.cpp +2 -2
  32. esphome/components/esp32_rmt_led_strip/led_strip.h +2 -0
  33. esphome/components/esp32_rmt_led_strip/light.py +3 -1
  34. esphome/components/esp8266/gpio.py +7 -5
  35. esphome/components/ethernet/__init__.py +55 -1
  36. esphome/components/ethernet/ethernet_component.cpp +14 -1
  37. esphome/components/ethernet/ethernet_component.h +7 -1
  38. esphome/components/font/__init__.py +213 -108
  39. esphome/components/gp8403/output/__init__.py +1 -1
  40. esphome/components/host/gpio.py +6 -4
  41. esphome/components/http_request/__init__.py +12 -0
  42. esphome/components/http_request/http_request.h +65 -3
  43. esphome/components/http_request/http_request_arduino.cpp +2 -3
  44. esphome/components/http_request/http_request_idf.cpp +6 -14
  45. esphome/components/http_request/ota/ota_http_request.cpp +1 -1
  46. esphome/components/http_request/update/http_request_update.cpp +1 -1
  47. esphome/components/i2c_device/__init__.py +26 -0
  48. esphome/components/i2c_device/i2c_device.cpp +17 -0
  49. esphome/components/i2c_device/i2c_device.h +18 -0
  50. esphome/components/i2s_audio/__init__.py +1 -3
  51. esphome/components/i2s_audio/speaker/__init__.py +12 -4
  52. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +432 -197
  53. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +91 -32
  54. esphome/components/ili9xxx/display.py +5 -1
  55. esphome/components/image/__init__.py +5 -8
  56. esphome/components/image/image.cpp +14 -14
  57. esphome/components/image/image.h +20 -24
  58. esphome/components/internal_temperature/internal_temperature.cpp +51 -2
  59. esphome/components/internal_temperature/internal_temperature.h +1 -0
  60. esphome/components/libretiny/gpio.py +4 -2
  61. esphome/components/light/__init__.py +32 -1
  62. esphome/components/light/automation.py +39 -32
  63. esphome/components/light/effects.py +36 -36
  64. esphome/components/light/light_state.cpp +6 -16
  65. esphome/components/light/light_state.h +34 -0
  66. esphome/components/light/types.py +3 -1
  67. esphome/components/logger/logger_esp32.cpp +15 -0
  68. esphome/components/lvgl/__init__.py +202 -95
  69. esphome/components/lvgl/automation.py +42 -40
  70. esphome/components/lvgl/binary_sensor/__init__.py +8 -15
  71. esphome/components/lvgl/defines.py +14 -8
  72. esphome/components/lvgl/encoders.py +11 -8
  73. esphome/components/lvgl/keypads.py +77 -0
  74. esphome/components/lvgl/light/__init__.py +6 -8
  75. esphome/components/lvgl/lv_validation.py +2 -4
  76. esphome/components/lvgl/lvcode.py +3 -9
  77. esphome/components/lvgl/lvgl_esphome.cpp +210 -89
  78. esphome/components/lvgl/lvgl_esphome.h +113 -30
  79. esphome/components/lvgl/lvgl_proxy.h +17 -0
  80. esphome/components/lvgl/number/__init__.py +10 -15
  81. esphome/components/lvgl/schemas.py +4 -2
  82. esphome/components/lvgl/select/__init__.py +12 -37
  83. esphome/components/lvgl/select/lvgl_select.h +27 -33
  84. esphome/components/lvgl/sensor/__init__.py +8 -14
  85. esphome/components/lvgl/styles.py +3 -4
  86. esphome/components/lvgl/switch/__init__.py +8 -13
  87. esphome/components/lvgl/text/__init__.py +5 -6
  88. esphome/components/lvgl/text_sensor/__init__.py +15 -15
  89. esphome/components/lvgl/touchscreens.py +2 -3
  90. esphome/components/lvgl/trigger.py +7 -9
  91. esphome/components/lvgl/types.py +9 -3
  92. esphome/components/lvgl/widgets/__init__.py +32 -21
  93. esphome/components/lvgl/widgets/dropdown.py +22 -10
  94. esphome/components/lvgl/widgets/msgbox.py +6 -5
  95. esphome/components/lvgl/widgets/obj.py +4 -2
  96. esphome/components/lvgl/widgets/page.py +3 -2
  97. esphome/components/lvgl/widgets/qrcode.py +54 -0
  98. esphome/components/lvgl/widgets/roller.py +21 -14
  99. esphome/components/lvgl/widgets/tileview.py +2 -1
  100. esphome/components/max17043/__init__.py +1 -0
  101. esphome/components/max17043/automation.h +20 -0
  102. esphome/components/max17043/max17043.cpp +98 -0
  103. esphome/components/max17043/max17043.h +29 -0
  104. esphome/components/max17043/sensor.py +77 -0
  105. esphome/components/media_player/__init__.py +11 -0
  106. esphome/components/media_player/automation.h +10 -0
  107. esphome/components/media_player/media_player.cpp +4 -0
  108. esphome/components/midea/air_conditioner.cpp +17 -1
  109. esphome/components/mlx90393/sensor.py +1 -1
  110. esphome/components/modbus_controller/__init__.py +31 -1
  111. esphome/components/modbus_controller/automation.h +16 -0
  112. esphome/components/modbus_controller/const.py +2 -0
  113. esphome/components/modbus_controller/modbus_controller.cpp +14 -2
  114. esphome/components/modbus_controller/modbus_controller.h +9 -0
  115. esphome/components/mopeka_pro_check/mopeka_pro_check.cpp +40 -21
  116. esphome/components/mopeka_pro_check/mopeka_pro_check.h +9 -2
  117. esphome/components/mopeka_pro_check/sensor.py +41 -0
  118. esphome/components/mqtt/__init__.py +36 -0
  119. esphome/components/mqtt/mqtt_client.cpp +27 -3
  120. esphome/components/mqtt/mqtt_client.h +27 -2
  121. esphome/components/mqtt/mqtt_climate.cpp +4 -2
  122. esphome/components/mqtt/mqtt_component.cpp +6 -0
  123. esphome/components/mqtt/mqtt_component.h +4 -0
  124. esphome/components/mqtt/mqtt_const.h +6 -0
  125. esphome/components/online_image/online_image.cpp +2 -8
  126. esphome/components/online_image/online_image.h +2 -6
  127. esphome/components/opentherm/__init__.py +35 -9
  128. esphome/components/opentherm/binary_sensor/__init__.py +33 -0
  129. esphome/components/opentherm/const.py +11 -0
  130. esphome/components/opentherm/generate.py +142 -0
  131. esphome/components/opentherm/hub.cpp +130 -24
  132. esphome/components/opentherm/hub.h +62 -9
  133. esphome/components/opentherm/input.h +18 -0
  134. esphome/components/opentherm/input.py +51 -0
  135. esphome/components/opentherm/number/__init__.py +74 -0
  136. esphome/components/opentherm/number/number.cpp +40 -0
  137. esphome/components/opentherm/number/number.h +31 -0
  138. esphome/components/opentherm/opentherm.cpp +30 -0
  139. esphome/components/opentherm/opentherm.h +34 -2
  140. esphome/components/opentherm/opentherm_macros.h +151 -0
  141. esphome/components/opentherm/output/__init__.py +47 -0
  142. esphome/components/opentherm/output/output.cpp +18 -0
  143. esphome/components/opentherm/output/output.h +33 -0
  144. esphome/components/opentherm/schema.py +814 -0
  145. esphome/components/opentherm/sensor/__init__.py +51 -0
  146. esphome/components/opentherm/switch/__init__.py +43 -0
  147. esphome/components/opentherm/switch/switch.cpp +28 -0
  148. esphome/components/opentherm/switch/switch.h +20 -0
  149. esphome/components/opentherm/validate.py +31 -0
  150. esphome/components/pcd8544/display.py +8 -4
  151. esphome/components/prometheus/prometheus_handler.cpp +176 -14
  152. esphome/components/prometheus/prometheus_handler.h +25 -7
  153. esphome/components/qspi_amoled/display.py +1 -141
  154. esphome/components/qspi_dbi/display.py +185 -0
  155. esphome/components/qspi_dbi/models.py +64 -0
  156. esphome/components/{qspi_amoled/qspi_amoled.cpp → qspi_dbi/qspi_dbi.cpp} +95 -46
  157. esphome/components/{qspi_amoled/qspi_amoled.h → qspi_dbi/qspi_dbi.h} +26 -15
  158. esphome/components/rp2040/__init__.py +6 -3
  159. esphome/components/rp2040/gpio.py +5 -3
  160. esphome/components/rtttl/rtttl.cpp +4 -1
  161. esphome/components/rtttl/rtttl.h +1 -0
  162. esphome/components/sdl/sdl_esphome.cpp +22 -5
  163. esphome/components/sdl/sdl_esphome.h +1 -0
  164. esphome/components/sensor/__init__.py +18 -8
  165. esphome/components/sensor/filter.cpp +19 -18
  166. esphome/components/sensor/filter.h +9 -10
  167. esphome/components/sgp4x/sgp4x.cpp +40 -74
  168. esphome/components/sgp4x/sgp4x.h +5 -3
  169. esphome/components/speaker/__init__.py +51 -5
  170. esphome/components/speaker/automation.h +25 -0
  171. esphome/components/speaker/speaker.h +72 -1
  172. esphome/components/spi/__init__.py +15 -14
  173. esphome/components/spi_device/__init__.py +4 -15
  174. esphome/components/ssd1306_spi/display.py +6 -2
  175. esphome/components/ssd1322_spi/display.py +6 -2
  176. esphome/components/ssd1325_spi/display.py +6 -2
  177. esphome/components/ssd1327_spi/display.py +6 -2
  178. esphome/components/ssd1331_spi/display.py +6 -2
  179. esphome/components/ssd1351_spi/display.py +6 -2
  180. esphome/components/st7567_spi/display.py +6 -2
  181. esphome/components/st7701s/display.py +5 -1
  182. esphome/components/st7735/display.py +10 -5
  183. esphome/components/st7789v/display.py +12 -7
  184. esphome/components/statsd/statsd.cpp +2 -0
  185. esphome/components/statsd/statsd.h +2 -0
  186. esphome/components/sun/sun.h +3 -0
  187. esphome/components/tc74/__init__.py +1 -0
  188. esphome/components/tc74/sensor.py +32 -0
  189. esphome/components/tc74/tc74.cpp +68 -0
  190. esphome/components/tc74/tc74.h +28 -0
  191. esphome/components/touchscreen/__init__.py +41 -50
  192. esphome/components/touchscreen/touchscreen.h +4 -8
  193. esphome/components/udp/udp_component.cpp +6 -3
  194. esphome/components/udp/udp_component.h +4 -2
  195. esphome/components/waveshare_epaper/display.py +6 -2
  196. esphome/components/web_server/web_server.cpp +22 -0
  197. esphome/components/web_server/web_server.h +3 -0
  198. esphome/components/weikai/weikai.h +2 -2
  199. esphome/components/wifi/wifi_component.cpp +2 -2
  200. esphome/components/wifi/wifi_component_esp32_arduino.cpp +4 -4
  201. esphome/components/wifi/wifi_component_esp8266.cpp +4 -4
  202. esphome/components/wifi/wifi_component_esp_idf.cpp +2 -2
  203. esphome/components/xpt2046/touchscreen/__init__.py +7 -32
  204. esphome/config_validation.py +3 -1
  205. esphome/const.py +8 -1
  206. esphome/core/defines.h +8 -2
  207. esphome/core/helpers.cpp +32 -17
  208. esphome/core/helpers.h +32 -16
  209. esphome/core/ring_buffer.cpp +2 -2
  210. esphome/core/ring_buffer.h +2 -2
  211. esphome/dashboard/core.py +25 -0
  212. esphome/dashboard/status/mdns.py +3 -4
  213. esphome/dashboard/web_server.py +54 -19
  214. esphome/espota2.py +36 -35
  215. esphome/helpers.py +68 -16
  216. esphome/mqtt.py +9 -2
  217. esphome/storage_json.py +4 -0
  218. esphome/writer.py +7 -18
  219. esphome/zeroconf.py +8 -6
  220. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/METADATA +7 -5
  221. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/RECORD +226 -180
  222. esphome/core/bytebuffer.cpp +0 -167
  223. esphome/core/bytebuffer.h +0 -144
  224. /esphome/components/{qspi_amoled → qspi_dbi}/__init__.py +0 -0
  225. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/LICENSE +0 -0
  226. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/WHEEL +0 -0
  227. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/entry_points.txt +0 -0
  228. {esphome-2024.10.3.dist-info → esphome-2024.11.0b1.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,8 @@
4
4
 
5
5
  #include <driver/i2s.h>
6
6
 
7
+ #include "esphome/components/audio/audio.h"
8
+
7
9
  #include "esphome/core/application.h"
8
10
  #include "esphome/core/hal.h"
9
11
  #include "esphome/core/log.h"
@@ -11,186 +13,347 @@
11
13
  namespace esphome {
12
14
  namespace i2s_audio {
13
15
 
14
- static const size_t BUFFER_COUNT = 20;
16
+ static const uint8_t DMA_BUFFER_DURATION_MS = 15;
17
+ static const size_t DMA_BUFFERS_COUNT = 4;
18
+
19
+ static const size_t TASK_DELAY_MS = DMA_BUFFER_DURATION_MS * DMA_BUFFERS_COUNT / 2;
20
+
21
+ static const size_t TASK_STACK_SIZE = 4096;
22
+ static const ssize_t TASK_PRIORITY = 23;
23
+
24
+ static const size_t I2S_EVENT_QUEUE_COUNT = DMA_BUFFERS_COUNT + 1;
15
25
 
16
26
  static const char *const TAG = "i2s_audio.speaker";
17
27
 
28
+ enum SpeakerEventGroupBits : uint32_t {
29
+ COMMAND_START = (1 << 0), // starts the speaker task
30
+ COMMAND_STOP = (1 << 1), // stops the speaker task
31
+ COMMAND_STOP_GRACEFULLY = (1 << 2), // Stops the speaker task once all data has been written
32
+ STATE_STARTING = (1 << 10),
33
+ STATE_RUNNING = (1 << 11),
34
+ STATE_STOPPING = (1 << 12),
35
+ STATE_STOPPED = (1 << 13),
36
+ ERR_INVALID_FORMAT = (1 << 14),
37
+ ERR_TASK_FAILED_TO_START = (1 << 15),
38
+ ERR_ESP_INVALID_STATE = (1 << 16),
39
+ ERR_ESP_INVALID_ARG = (1 << 17),
40
+ ERR_ESP_INVALID_SIZE = (1 << 18),
41
+ ERR_ESP_NO_MEM = (1 << 19),
42
+ ERR_ESP_FAIL = (1 << 20),
43
+ ALL_ERR_ESP_BITS = ERR_ESP_INVALID_STATE | ERR_ESP_INVALID_ARG | ERR_ESP_INVALID_SIZE | ERR_ESP_NO_MEM | ERR_ESP_FAIL,
44
+ ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits
45
+ };
46
+
47
+ // Translates a SpeakerEventGroupBits ERR_ESP bit to the coressponding esp_err_t
48
+ static esp_err_t err_bit_to_esp_err(uint32_t bit) {
49
+ switch (bit) {
50
+ case SpeakerEventGroupBits::ERR_ESP_INVALID_STATE:
51
+ return ESP_ERR_INVALID_STATE;
52
+ case SpeakerEventGroupBits::ERR_ESP_INVALID_ARG:
53
+ return ESP_ERR_INVALID_ARG;
54
+ case SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE:
55
+ return ESP_ERR_INVALID_SIZE;
56
+ case SpeakerEventGroupBits::ERR_ESP_NO_MEM:
57
+ return ESP_ERR_NO_MEM;
58
+ default:
59
+ return ESP_FAIL;
60
+ }
61
+ }
62
+
63
+ /// @brief Multiplies the input array of Q15 numbers by a Q15 constant factor
64
+ ///
65
+ /// Based on `dsps_mulc_s16_ansi` from the esp-dsp library:
66
+ /// https://github.com/espressif/esp-dsp/blob/master/modules/math/mulc/fixed/dsps_mulc_s16_ansi.c
67
+ /// (accessed on 2024-09-30).
68
+ /// @param input Array of Q15 numbers
69
+ /// @param output Array of Q15 numbers
70
+ /// @param len Length of array
71
+ /// @param c Q15 constant factor
72
+ static void q15_multiplication(const int16_t *input, int16_t *output, size_t len, int16_t c) {
73
+ for (int i = 0; i < len; i++) {
74
+ int32_t acc = (int32_t) input[i] * (int32_t) c;
75
+ output[i] = (int16_t) (acc >> 15);
76
+ }
77
+ }
78
+
79
+ // Lists the Q15 fixed point scaling factor for volume reduction.
80
+ // Has 100 values representing silence and a reduction [49, 48.5, ... 0.5, 0] dB.
81
+ // dB to PCM scaling factor formula: floating_point_scale_factor = 2^(-db/6.014)
82
+ // float to Q15 fixed point formula: q15_scale_factor = floating_point_scale_factor * 2^(15)
83
+ static const std::vector<int16_t> Q15_VOLUME_SCALING_FACTORS = {
84
+ 0, 116, 122, 130, 137, 146, 154, 163, 173, 183, 194, 206, 218, 231, 244,
85
+ 259, 274, 291, 308, 326, 345, 366, 388, 411, 435, 461, 488, 517, 548, 580,
86
+ 615, 651, 690, 731, 774, 820, 868, 920, 974, 1032, 1094, 1158, 1227, 1300, 1377,
87
+ 1459, 1545, 1637, 1734, 1837, 1946, 2061, 2184, 2313, 2450, 2596, 2750, 2913, 3085, 3269,
88
+ 3462, 3668, 3885, 4116, 4360, 4619, 4893, 5183, 5490, 5816, 6161, 6527, 6914, 7324, 7758,
89
+ 8218, 8706, 9222, 9770, 10349, 10963, 11613, 12302, 13032, 13805, 14624, 15491, 16410, 17384, 18415,
90
+ 19508, 20665, 21891, 23189, 24565, 26022, 27566, 29201, 30933, 32767};
91
+
18
92
  void I2SAudioSpeaker::setup() {
19
93
  ESP_LOGCONFIG(TAG, "Setting up I2S Audio Speaker...");
20
94
 
21
- this->buffer_queue_ = xQueueCreate(BUFFER_COUNT, sizeof(DataEvent));
22
- if (this->buffer_queue_ == nullptr) {
23
- ESP_LOGE(TAG, "Failed to create buffer queue");
95
+ this->event_group_ = xEventGroupCreate();
96
+
97
+ if (this->event_group_ == nullptr) {
98
+ ESP_LOGE(TAG, "Failed to create event group");
24
99
  this->mark_failed();
25
100
  return;
26
101
  }
27
102
 
28
- this->event_queue_ = xQueueCreate(BUFFER_COUNT, sizeof(TaskEvent));
29
- if (this->event_queue_ == nullptr) {
30
- ESP_LOGE(TAG, "Failed to create event queue");
103
+ this->i2s_event_queue_ = xQueueCreate(I2S_EVENT_QUEUE_COUNT, sizeof(i2s_event_t));
104
+
105
+ if (this->i2s_event_queue_ == nullptr) {
106
+ ESP_LOGE(TAG, "Failed to create I2S event queue");
31
107
  this->mark_failed();
32
108
  return;
33
109
  }
34
110
  }
35
111
 
36
- void I2SAudioSpeaker::start() {
37
- if (this->is_failed()) {
38
- ESP_LOGE(TAG, "Cannot start audio, speaker failed to setup");
39
- return;
112
+ void I2SAudioSpeaker::loop() {
113
+ uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
114
+
115
+ if (event_group_bits & SpeakerEventGroupBits::STATE_STARTING) {
116
+ ESP_LOGD(TAG, "Starting Speaker");
117
+ this->state_ = speaker::STATE_STARTING;
118
+ xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STARTING);
40
119
  }
41
- if (this->task_created_) {
42
- ESP_LOGW(TAG, "Called start while task has been already created.");
43
- return;
120
+ if (event_group_bits & SpeakerEventGroupBits::STATE_RUNNING) {
121
+ ESP_LOGD(TAG, "Started Speaker");
122
+ this->state_ = speaker::STATE_RUNNING;
123
+ xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_RUNNING);
124
+ this->status_clear_warning();
125
+ this->status_clear_error();
44
126
  }
45
- this->state_ = speaker::STATE_STARTING;
46
- }
47
- void I2SAudioSpeaker::start_() {
48
- if (this->task_created_) {
49
- return;
127
+ if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPING) {
128
+ ESP_LOGD(TAG, "Stopping Speaker");
129
+ this->state_ = speaker::STATE_STOPPING;
130
+ xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPING);
50
131
  }
51
- if (!this->parent_->try_lock()) {
52
- return; // Waiting for another i2s component to return lock
132
+ if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPED) {
133
+ if (!this->task_created_) {
134
+ ESP_LOGD(TAG, "Stopped Speaker");
135
+ this->state_ = speaker::STATE_STOPPED;
136
+ xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ALL_BITS);
137
+ this->speaker_task_handle_ = nullptr;
138
+ }
139
+ }
140
+
141
+ if (event_group_bits & SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START) {
142
+ this->status_set_error("Failed to start speaker task");
143
+ xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START);
144
+ }
145
+
146
+ if (event_group_bits & SpeakerEventGroupBits::ERR_INVALID_FORMAT) {
147
+ this->status_set_error("Failed to adjust I2S bus to match the incoming audio");
148
+ ESP_LOGE(TAG,
149
+ "Incompatible audio format: sample rate = %" PRIu32 ", channels = %" PRIu8 ", bits per sample = %" PRIu8,
150
+ this->audio_stream_info_.sample_rate, this->audio_stream_info_.channels,
151
+ this->audio_stream_info_.bits_per_sample);
53
152
  }
54
153
 
55
- xTaskCreate(I2SAudioSpeaker::player_task, "speaker_task", 8192, (void *) this, 1, &this->player_task_handle_);
56
- this->task_created_ = true;
154
+ if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) {
155
+ uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS;
156
+ ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(err_bit_to_esp_err(error_bits)));
157
+ this->status_set_warning();
158
+ }
57
159
  }
58
160
 
59
- template<typename a, typename b> const uint8_t *convert_data_format(const a *from, b *to, size_t &bytes, bool repeat) {
60
- if (sizeof(a) == sizeof(b) && !repeat) {
61
- return reinterpret_cast<const uint8_t *>(from);
161
+ void I2SAudioSpeaker::set_volume(float volume) {
162
+ this->volume_ = volume;
163
+ #ifdef USE_AUDIO_DAC
164
+ if (this->audio_dac_ != nullptr) {
165
+ if (volume > 0.0) {
166
+ this->audio_dac_->set_mute_off();
167
+ }
168
+ this->audio_dac_->set_volume(volume);
169
+ } else
170
+ #endif
171
+ {
172
+ // Fallback to software volume control by using a Q15 fixed point scaling factor
173
+ ssize_t decibel_index = remap<ssize_t, float>(volume, 0.0f, 1.0f, 0, Q15_VOLUME_SCALING_FACTORS.size() - 1);
174
+ this->q15_volume_factor_ = Q15_VOLUME_SCALING_FACTORS[decibel_index];
62
175
  }
63
- const b *result = to;
64
- for (size_t i = 0; i < bytes; i += sizeof(a)) {
65
- b value = static_cast<b>(*from++) << (sizeof(b) - sizeof(a)) * 8;
66
- *to++ = value;
67
- if (repeat)
68
- *to++ = value;
176
+ }
177
+
178
+ void I2SAudioSpeaker::set_mute_state(bool mute_state) {
179
+ this->mute_state_ = mute_state;
180
+ #ifdef USE_AUDIO_DAC
181
+ if (this->audio_dac_) {
182
+ if (mute_state) {
183
+ this->audio_dac_->set_mute_on();
184
+ } else {
185
+ this->audio_dac_->set_mute_off();
186
+ }
187
+ } else
188
+ #endif
189
+ {
190
+ if (mute_state) {
191
+ // Fallback to software volume control and scale by 0
192
+ this->q15_volume_factor_ = 0;
193
+ } else {
194
+ // Revert to previous volume when unmuting
195
+ this->set_volume(this->volume_);
196
+ }
69
197
  }
70
- bytes *= (sizeof(b) / sizeof(a)) * (repeat ? 2 : 1); // NOLINT
71
- return reinterpret_cast<const uint8_t *>(result);
72
198
  }
73
199
 
74
- void I2SAudioSpeaker::player_task(void *params) {
75
- I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) params;
200
+ size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) {
201
+ if (this->is_failed()) {
202
+ ESP_LOGE(TAG, "Cannot play audio, speaker failed to setup");
203
+ return 0;
204
+ }
205
+ if (this->state_ != speaker::STATE_RUNNING && this->state_ != speaker::STATE_STARTING) {
206
+ this->start();
207
+ }
76
208
 
77
- TaskEvent event;
78
- event.type = TaskEventType::STARTING;
79
- xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY);
209
+ size_t bytes_written = 0;
210
+ if ((this->state_ == speaker::STATE_RUNNING) && (this->audio_ring_buffer_.use_count() == 1)) {
211
+ // Only one owner of the ring buffer (the speaker task), so the ring buffer is allocated and no other components are
212
+ // attempting to write to it.
80
213
 
81
- i2s_driver_config_t config = {
82
- .mode = (i2s_mode_t) (this_speaker->i2s_mode_ | I2S_MODE_TX),
83
- .sample_rate = this_speaker->sample_rate_,
84
- .bits_per_sample = this_speaker->bits_per_sample_,
85
- .channel_format = this_speaker->channel_,
86
- .communication_format = this_speaker->i2s_comm_fmt_,
87
- .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
88
- .dma_buf_count = 8,
89
- .dma_buf_len = 256,
90
- .use_apll = this_speaker->use_apll_,
91
- .tx_desc_auto_clear = true,
92
- .fixed_mclk = 0,
93
- .mclk_multiple = I2S_MCLK_MULTIPLE_256,
94
- .bits_per_chan = this_speaker->bits_per_channel_,
95
- };
96
- #if SOC_I2S_SUPPORTS_DAC
97
- if (this_speaker->internal_dac_mode_ != I2S_DAC_CHANNEL_DISABLE) {
98
- config.mode = (i2s_mode_t) (config.mode | I2S_MODE_DAC_BUILT_IN);
214
+ // Temporarily share ownership of the ring buffer so it won't be deallocated while writing
215
+ std::shared_ptr<RingBuffer> temp_ring_buffer = this->audio_ring_buffer_;
216
+ bytes_written = temp_ring_buffer->write_without_replacement((void *) data, length, ticks_to_wait);
99
217
  }
100
- #endif
101
218
 
102
- esp_err_t err = i2s_driver_install(this_speaker->parent_->get_port(), &config, 0, nullptr);
103
- if (err != ESP_OK) {
104
- event.type = TaskEventType::WARNING;
105
- event.err = err;
106
- xQueueSend(this_speaker->event_queue_, &event, 0);
107
- event.type = TaskEventType::STOPPED;
108
- xQueueSend(this_speaker->event_queue_, &event, 0);
109
- while (true) {
110
- delay(10);
111
- }
219
+ return bytes_written;
220
+ }
221
+
222
+ bool I2SAudioSpeaker::has_buffered_data() const {
223
+ if (this->audio_ring_buffer_ != nullptr) {
224
+ return this->audio_ring_buffer_->available() > 0;
112
225
  }
226
+ return false;
227
+ }
113
228
 
114
- #if SOC_I2S_SUPPORTS_DAC
115
- if (this_speaker->internal_dac_mode_ == I2S_DAC_CHANNEL_DISABLE) {
116
- #endif
117
- i2s_pin_config_t pin_config = this_speaker->parent_->get_pin_config();
118
- pin_config.data_out_num = this_speaker->dout_pin_;
229
+ void I2SAudioSpeaker::speaker_task(void *params) {
230
+ I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) params;
231
+ uint32_t event_group_bits =
232
+ xEventGroupWaitBits(this_speaker->event_group_,
233
+ SpeakerEventGroupBits::COMMAND_START | SpeakerEventGroupBits::COMMAND_STOP |
234
+ SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY, // Bit message to read
235
+ pdTRUE, // Clear the bits on exit
236
+ pdFALSE, // Don't wait for all the bits,
237
+ portMAX_DELAY); // Block indefinitely until a bit is set
238
+
239
+ if (event_group_bits & (SpeakerEventGroupBits::COMMAND_STOP | SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY)) {
240
+ // Received a stop signal before the task was requested to start
241
+ this_speaker->delete_task_(0);
242
+ }
119
243
 
120
- i2s_set_pin(this_speaker->parent_->get_port(), &pin_config);
121
- #if SOC_I2S_SUPPORTS_DAC
122
- } else {
123
- i2s_set_dac_mode(this_speaker->internal_dac_mode_);
244
+ xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STARTING);
245
+
246
+ audio::AudioStreamInfo audio_stream_info = this_speaker->audio_stream_info_;
247
+ const ssize_t bytes_per_sample = audio_stream_info.get_bytes_per_sample();
248
+ const uint8_t number_of_channels = audio_stream_info.channels;
249
+
250
+ const size_t dma_buffers_size = DMA_BUFFERS_COUNT * DMA_BUFFER_DURATION_MS * this_speaker->sample_rate_ / 1000 *
251
+ bytes_per_sample * number_of_channels;
252
+ const size_t ring_buffer_size =
253
+ this_speaker->buffer_duration_ms_ * this_speaker->sample_rate_ / 1000 * bytes_per_sample * number_of_channels;
254
+
255
+ if (this_speaker->send_esp_err_to_event_group_(this_speaker->allocate_buffers_(dma_buffers_size, ring_buffer_size))) {
256
+ // Failed to allocate buffers
257
+ xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM);
258
+ this_speaker->delete_task_(dma_buffers_size);
124
259
  }
125
- #endif
126
260
 
127
- DataEvent data_event;
261
+ if (this_speaker->send_esp_err_to_event_group_(this_speaker->start_i2s_driver_())) {
262
+ // Failed to start I2S driver
263
+ this_speaker->delete_task_(dma_buffers_size);
264
+ }
128
265
 
129
- event.type = TaskEventType::STARTED;
130
- xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY);
266
+ if (!this_speaker->send_esp_err_to_event_group_(this_speaker->reconfigure_i2s_stream_info_(audio_stream_info))) {
267
+ // Successfully set the I2S stream info, ready to write audio data to the I2S port
131
268
 
132
- int32_t buffer[BUFFER_SIZE];
269
+ xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_RUNNING);
133
270
 
134
- while (true) {
135
- if (xQueueReceive(this_speaker->buffer_queue_, &data_event, this_speaker->timeout_ / portTICK_PERIOD_MS) !=
136
- pdTRUE) {
137
- break; // End of audio from main thread
138
- }
139
- if (data_event.stop) {
140
- // Stop signal from main thread
141
- xQueueReset(this_speaker->buffer_queue_); // Flush queue
142
- break;
143
- }
271
+ bool stop_gracefully = false;
272
+ uint32_t last_data_received_time = millis();
273
+ bool tx_dma_underflow = false;
144
274
 
145
- const uint8_t *data = data_event.data;
146
- size_t remaining = data_event.len;
147
- switch (this_speaker->bits_per_sample_) {
148
- case I2S_BITS_PER_SAMPLE_8BIT:
149
- case I2S_BITS_PER_SAMPLE_16BIT: {
150
- data = convert_data_format(reinterpret_cast<const int16_t *>(data), reinterpret_cast<int16_t *>(buffer),
151
- remaining, this_speaker->channel_ == I2S_CHANNEL_FMT_ALL_LEFT);
275
+ while (!this_speaker->timeout_.has_value() ||
276
+ (millis() - last_data_received_time) <= this_speaker->timeout_.value()) {
277
+ event_group_bits = xEventGroupGetBits(this_speaker->event_group_);
278
+
279
+ if (event_group_bits & SpeakerEventGroupBits::COMMAND_STOP) {
152
280
  break;
153
281
  }
154
- case I2S_BITS_PER_SAMPLE_24BIT:
155
- case I2S_BITS_PER_SAMPLE_32BIT: {
156
- data = convert_data_format(reinterpret_cast<const int16_t *>(data), reinterpret_cast<int32_t *>(buffer),
157
- remaining, this_speaker->channel_ == I2S_CHANNEL_FMT_ALL_LEFT);
158
- break;
282
+ if (event_group_bits & SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY) {
283
+ stop_gracefully = true;
159
284
  }
160
- }
161
285
 
162
- while (remaining != 0) {
163
- size_t bytes_written;
164
- esp_err_t err =
165
- i2s_write(this_speaker->parent_->get_port(), data, remaining, &bytes_written, (32 / portTICK_PERIOD_MS));
166
- if (err != ESP_OK) {
167
- event = {.type = TaskEventType::WARNING, .err = err};
168
- if (xQueueSend(this_speaker->event_queue_, &event, 10 / portTICK_PERIOD_MS) != pdTRUE) {
169
- ESP_LOGW(TAG, "Failed to send WARNING event");
286
+ i2s_event_t i2s_event;
287
+ while (xQueueReceive(this_speaker->i2s_event_queue_, &i2s_event, 0)) {
288
+ if (i2s_event.type == I2S_EVENT_TX_Q_OVF) {
289
+ tx_dma_underflow = true;
170
290
  }
171
- continue;
172
291
  }
173
- data += bytes_written;
174
- remaining -= bytes_written;
175
- }
176
- }
177
292
 
178
- event.type = TaskEventType::STOPPING;
179
- if (xQueueSend(this_speaker->event_queue_, &event, 10 / portTICK_PERIOD_MS) != pdTRUE) {
180
- ESP_LOGW(TAG, "Failed to send STOPPING event");
181
- }
293
+ size_t bytes_to_read = dma_buffers_size;
294
+ size_t bytes_read = this_speaker->audio_ring_buffer_->read((void *) this_speaker->data_buffer_, bytes_to_read,
295
+ pdMS_TO_TICKS(TASK_DELAY_MS));
296
+
297
+ if (bytes_read > 0) {
298
+ size_t bytes_written = 0;
299
+
300
+ if ((audio_stream_info.bits_per_sample == 16) && (this_speaker->q15_volume_factor_ < INT16_MAX)) {
301
+ // Scale samples by the volume factor in place
302
+ q15_multiplication((int16_t *) this_speaker->data_buffer_, (int16_t *) this_speaker->data_buffer_,
303
+ bytes_read / sizeof(int16_t), this_speaker->q15_volume_factor_);
304
+ }
182
305
 
306
+ if (audio_stream_info.bits_per_sample == (uint8_t) this_speaker->bits_per_sample_) {
307
+ i2s_write(this_speaker->parent_->get_port(), this_speaker->data_buffer_, bytes_read, &bytes_written,
308
+ portMAX_DELAY);
309
+ } else if (audio_stream_info.bits_per_sample < (uint8_t) this_speaker->bits_per_sample_) {
310
+ i2s_write_expand(this_speaker->parent_->get_port(), this_speaker->data_buffer_, bytes_read,
311
+ audio_stream_info.bits_per_sample, this_speaker->bits_per_sample_, &bytes_written,
312
+ portMAX_DELAY);
313
+ }
314
+
315
+ if (bytes_written != bytes_read) {
316
+ xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE);
317
+ }
318
+ tx_dma_underflow = false;
319
+ last_data_received_time = millis();
320
+ } else {
321
+ // No data received
322
+ if (stop_gracefully && tx_dma_underflow) {
323
+ break;
324
+ }
325
+ }
326
+ }
327
+ } else {
328
+ // Couldn't configure the I2S port to be compatible with the incoming audio
329
+ xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_INVALID_FORMAT);
330
+ }
183
331
  i2s_zero_dma_buffer(this_speaker->parent_->get_port());
184
332
 
333
+ xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STOPPING);
334
+
185
335
  i2s_driver_uninstall(this_speaker->parent_->get_port());
186
336
 
187
- event.type = TaskEventType::STOPPED;
188
- if (xQueueSend(this_speaker->event_queue_, &event, 10 / portTICK_PERIOD_MS) != pdTRUE) {
189
- ESP_LOGW(TAG, "Failed to send STOPPED event");
337
+ this_speaker->parent_->unlock();
338
+ this_speaker->delete_task_(dma_buffers_size);
339
+ }
340
+
341
+ void I2SAudioSpeaker::start() {
342
+ if (this->is_failed() || this->status_has_error())
343
+ return;
344
+ if ((this->state_ == speaker::STATE_STARTING) || (this->state_ == speaker::STATE_RUNNING))
345
+ return;
346
+
347
+ if (this->speaker_task_handle_ == nullptr) {
348
+ xTaskCreate(I2SAudioSpeaker::speaker_task, "speaker_task", TASK_STACK_SIZE, (void *) this, TASK_PRIORITY,
349
+ &this->speaker_task_handle_);
190
350
  }
191
351
 
192
- while (true) {
193
- delay(10);
352
+ if (this->speaker_task_handle_ != nullptr) {
353
+ xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_START);
354
+ this->task_created_ = true;
355
+ } else {
356
+ xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START);
194
357
  }
195
358
  }
196
359
 
@@ -203,92 +366,164 @@ void I2SAudioSpeaker::stop_(bool wait_on_empty) {
203
366
  return;
204
367
  if (this->state_ == speaker::STATE_STOPPED)
205
368
  return;
206
- if (this->state_ == speaker::STATE_STARTING) {
207
- this->state_ = speaker::STATE_STOPPED;
208
- return;
209
- }
210
- this->state_ = speaker::STATE_STOPPING;
211
- DataEvent data;
212
- data.stop = true;
369
+
213
370
  if (wait_on_empty) {
214
- xQueueSend(this->buffer_queue_, &data, portMAX_DELAY);
371
+ xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY);
215
372
  } else {
216
- xQueueSendToFront(this->buffer_queue_, &data, portMAX_DELAY);
373
+ xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_STOP);
217
374
  }
218
375
  }
219
376
 
220
- void I2SAudioSpeaker::watch_() {
221
- TaskEvent event;
222
- if (xQueueReceive(this->event_queue_, &event, 0) == pdTRUE) {
223
- switch (event.type) {
224
- case TaskEventType::STARTING:
225
- ESP_LOGD(TAG, "Starting I2S Audio Speaker");
226
- break;
227
- case TaskEventType::STARTED:
228
- ESP_LOGD(TAG, "Started I2S Audio Speaker");
229
- this->state_ = speaker::STATE_RUNNING;
230
- this->status_clear_warning();
231
- break;
232
- case TaskEventType::STOPPING:
233
- ESP_LOGD(TAG, "Stopping I2S Audio Speaker");
234
- break;
235
- case TaskEventType::STOPPED:
236
- this->state_ = speaker::STATE_STOPPED;
237
- vTaskDelete(this->player_task_handle_);
238
- this->task_created_ = false;
239
- this->player_task_handle_ = nullptr;
240
- this->parent_->unlock();
241
- xQueueReset(this->buffer_queue_);
242
- ESP_LOGD(TAG, "Stopped I2S Audio Speaker");
243
- break;
244
- case TaskEventType::WARNING:
245
- ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(event.err));
246
- this->status_set_warning();
247
- break;
248
- }
377
+ bool I2SAudioSpeaker::send_esp_err_to_event_group_(esp_err_t err) {
378
+ switch (err) {
379
+ case ESP_OK:
380
+ return false;
381
+ case ESP_ERR_INVALID_STATE:
382
+ xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_STATE);
383
+ return true;
384
+ case ESP_ERR_INVALID_ARG:
385
+ xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_ARG);
386
+ return true;
387
+ case ESP_ERR_INVALID_SIZE:
388
+ xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE);
389
+ return true;
390
+ case ESP_ERR_NO_MEM:
391
+ xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM);
392
+ return true;
393
+ default:
394
+ xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_FAIL);
395
+ return true;
249
396
  }
250
397
  }
251
398
 
252
- void I2SAudioSpeaker::loop() {
253
- switch (this->state_) {
254
- case speaker::STATE_STARTING:
255
- this->start_();
256
- [[fallthrough]];
257
- case speaker::STATE_RUNNING:
258
- case speaker::STATE_STOPPING:
259
- this->watch_();
260
- break;
261
- case speaker::STATE_STOPPED:
262
- break;
399
+ esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size) {
400
+ if (this->data_buffer_ == nullptr) {
401
+ // Allocate data buffer for temporarily storing audio from the ring buffer before writing to the I2S bus
402
+ ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
403
+ this->data_buffer_ = allocator.allocate(data_buffer_size);
263
404
  }
405
+
406
+ if (this->data_buffer_ == nullptr) {
407
+ return ESP_ERR_NO_MEM;
408
+ }
409
+
410
+ if (this->audio_ring_buffer_.use_count() == 0) {
411
+ // Allocate ring buffer. Uses a shared_ptr to ensure it isn't improperly deallocated.
412
+ this->audio_ring_buffer_ = RingBuffer::create(ring_buffer_size);
413
+ }
414
+
415
+ if (this->audio_ring_buffer_ == nullptr) {
416
+ return ESP_ERR_NO_MEM;
417
+ }
418
+
419
+ return ESP_OK;
264
420
  }
265
421
 
266
- size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length) {
267
- if (this->is_failed()) {
268
- ESP_LOGE(TAG, "Cannot play audio, speaker failed to setup");
269
- return 0;
422
+ esp_err_t I2SAudioSpeaker::start_i2s_driver_() {
423
+ if (!this->parent_->try_lock()) {
424
+ return ESP_ERR_INVALID_STATE;
270
425
  }
271
- if (this->state_ != speaker::STATE_RUNNING && this->state_ != speaker::STATE_STARTING) {
272
- this->start();
426
+
427
+ int dma_buffer_length = DMA_BUFFER_DURATION_MS * this->sample_rate_ / 1000;
428
+
429
+ i2s_driver_config_t config = {
430
+ .mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_TX),
431
+ .sample_rate = this->sample_rate_,
432
+ .bits_per_sample = this->bits_per_sample_,
433
+ .channel_format = this->channel_,
434
+ .communication_format = this->i2s_comm_fmt_,
435
+ .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
436
+ .dma_buf_count = DMA_BUFFERS_COUNT,
437
+ .dma_buf_len = dma_buffer_length,
438
+ .use_apll = this->use_apll_,
439
+ .tx_desc_auto_clear = true,
440
+ .fixed_mclk = I2S_PIN_NO_CHANGE,
441
+ .mclk_multiple = I2S_MCLK_MULTIPLE_256,
442
+ .bits_per_chan = this->bits_per_channel_,
443
+ #if SOC_I2S_SUPPORTS_TDM
444
+ .chan_mask = (i2s_channel_t) (I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1),
445
+ .total_chan = 2,
446
+ .left_align = false,
447
+ .big_edin = false,
448
+ .bit_order_msb = false,
449
+ .skip_msk = false,
450
+ #endif
451
+ };
452
+ #if SOC_I2S_SUPPORTS_DAC
453
+ if (this->internal_dac_mode_ != I2S_DAC_CHANNEL_DISABLE) {
454
+ config.mode = (i2s_mode_t) (config.mode | I2S_MODE_DAC_BUILT_IN);
273
455
  }
274
- size_t remaining = length;
275
- size_t index = 0;
276
- while (remaining > 0) {
277
- DataEvent event;
278
- event.stop = false;
279
- size_t to_send_length = std::min(remaining, BUFFER_SIZE);
280
- event.len = to_send_length;
281
- memcpy(event.data, data + index, to_send_length);
282
- if (xQueueSend(this->buffer_queue_, &event, 0) != pdTRUE) {
283
- return index;
284
- }
285
- remaining -= to_send_length;
286
- index += to_send_length;
456
+ #endif
457
+
458
+ esp_err_t err =
459
+ i2s_driver_install(this->parent_->get_port(), &config, I2S_EVENT_QUEUE_COUNT, &this->i2s_event_queue_);
460
+ if (err != ESP_OK) {
461
+ // Failed to install the driver, so unlock the I2S port
462
+ this->parent_->unlock();
463
+ return err;
464
+ }
465
+
466
+ #if SOC_I2S_SUPPORTS_DAC
467
+ if (this->internal_dac_mode_ == I2S_DAC_CHANNEL_DISABLE) {
468
+ #endif
469
+ i2s_pin_config_t pin_config = this->parent_->get_pin_config();
470
+ pin_config.data_out_num = this->dout_pin_;
471
+
472
+ err = i2s_set_pin(this->parent_->get_port(), &pin_config);
473
+ #if SOC_I2S_SUPPORTS_DAC
474
+ } else {
475
+ i2s_set_dac_mode(this->internal_dac_mode_);
287
476
  }
288
- return index;
477
+ #endif
478
+
479
+ if (err != ESP_OK) {
480
+ // Failed to set the data out pin, so uninstall the driver and unlock the I2S port
481
+ i2s_driver_uninstall(this->parent_->get_port());
482
+ this->parent_->unlock();
483
+ }
484
+
485
+ return err;
486
+ }
487
+
488
+ esp_err_t I2SAudioSpeaker::reconfigure_i2s_stream_info_(audio::AudioStreamInfo &audio_stream_info) {
489
+ if (this->i2s_mode_ & I2S_MODE_MASTER) {
490
+ // ESP controls for the the I2S bus, so adjust the sample rate and bits per sample to match the incoming audio
491
+ this->sample_rate_ = audio_stream_info.sample_rate;
492
+ this->bits_per_sample_ = (i2s_bits_per_sample_t) audio_stream_info.bits_per_sample;
493
+ } else if (this->sample_rate_ != audio_stream_info.sample_rate) {
494
+ // Can't reconfigure I2S bus, so the sample rate must match the configured value
495
+ return ESP_ERR_INVALID_ARG;
496
+ }
497
+
498
+ if ((i2s_bits_per_sample_t) audio_stream_info.bits_per_sample > this->bits_per_sample_) {
499
+ // Currently can't handle the case when the incoming audio has more bits per sample than the configured value
500
+ return ESP_ERR_INVALID_ARG;
501
+ }
502
+
503
+ if (audio_stream_info.channels == 1) {
504
+ return i2s_set_clk(this->parent_->get_port(), this->sample_rate_, this->bits_per_sample_, I2S_CHANNEL_MONO);
505
+ } else if (audio_stream_info.channels == 2) {
506
+ return i2s_set_clk(this->parent_->get_port(), this->sample_rate_, this->bits_per_sample_, I2S_CHANNEL_STEREO);
507
+ }
508
+
509
+ return ESP_ERR_INVALID_ARG;
289
510
  }
290
511
 
291
- bool I2SAudioSpeaker::has_buffered_data() const { return uxQueueMessagesWaiting(this->buffer_queue_) > 0; }
512
+ void I2SAudioSpeaker::delete_task_(size_t buffer_size) {
513
+ this->audio_ring_buffer_.reset(); // Releases onwership of the shared_ptr
514
+
515
+ if (this->data_buffer_ != nullptr) {
516
+ ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
517
+ allocator.deallocate(this->data_buffer_, buffer_size);
518
+ this->data_buffer_ = nullptr;
519
+ }
520
+
521
+ xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPED);
522
+ xQueueReset(this->i2s_event_queue_);
523
+
524
+ this->task_created_ = false;
525
+ vTaskDelete(nullptr);
526
+ }
292
527
 
293
528
  } // namespace i2s_audio
294
529
  } // namespace esphome