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