esphome 2024.10.3__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.
- esphome/__main__.py +22 -4
- esphome/automation.py +29 -2
- esphome/components/animation/__init__.py +5 -8
- esphome/components/animation/animation.cpp +1 -1
- esphome/components/audio/__init__.py +9 -0
- esphome/components/audio/audio.h +21 -0
- esphome/components/axs15231/__init__.py +6 -0
- esphome/components/axs15231/touchscreen/__init__.py +36 -0
- esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +64 -0
- esphome/components/axs15231/touchscreen/axs15231_touchscreen.h +27 -0
- esphome/components/bme68x_bsec2/__init__.py +1 -1
- esphome/components/bme68x_bsec2/bme68x_bsec2.cpp +50 -47
- esphome/components/bme68x_bsec2/bme68x_bsec2.h +0 -2
- esphome/components/bytebuffer/__init__.py +5 -0
- esphome/components/bytebuffer/bytebuffer.h +421 -0
- esphome/components/climate/__init__.py +14 -13
- esphome/components/datetime/__init__.py +3 -3
- esphome/components/debug/debug_esp32.cpp +16 -8
- esphome/components/dfplayer/dfplayer.cpp +132 -6
- esphome/components/dfplayer/dfplayer.h +19 -53
- esphome/components/display/display.cpp +142 -0
- esphome/components/display/display.h +7 -0
- esphome/components/es8311/__init__.py +0 -0
- esphome/components/es8311/audio_dac.py +70 -0
- esphome/components/es8311/es8311.cpp +227 -0
- esphome/components/es8311/es8311.h +135 -0
- esphome/components/es8311/es8311_const.h +195 -0
- esphome/components/esp32/boards.py +199 -1
- esphome/components/esp32/gpio.py +3 -1
- esphome/components/esp32_ble/const_esp32c6.h +7 -0
- esphome/components/esp32_ble_client/ble_client_base.h +1 -1
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +3 -0
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +2 -1
- esphome/components/esp32_rmt_led_strip/led_strip.cpp +2 -2
- esphome/components/esp32_rmt_led_strip/led_strip.h +2 -0
- esphome/components/esp32_rmt_led_strip/light.py +3 -1
- esphome/components/esp8266/gpio.py +7 -5
- esphome/components/ethernet/__init__.py +55 -1
- esphome/components/ethernet/ethernet_component.cpp +14 -1
- esphome/components/ethernet/ethernet_component.h +7 -1
- esphome/components/font/__init__.py +213 -108
- esphome/components/gp8403/output/__init__.py +1 -1
- esphome/components/host/gpio.py +6 -4
- esphome/components/http_request/__init__.py +12 -0
- esphome/components/http_request/http_request.h +65 -3
- esphome/components/http_request/http_request_arduino.cpp +4 -3
- esphome/components/http_request/http_request_idf.cpp +12 -14
- esphome/components/http_request/ota/ota_http_request.cpp +1 -1
- esphome/components/http_request/update/http_request_update.cpp +1 -1
- esphome/components/i2c_device/__init__.py +26 -0
- esphome/components/i2c_device/i2c_device.cpp +17 -0
- esphome/components/i2c_device/i2c_device.h +18 -0
- esphome/components/i2s_audio/__init__.py +1 -3
- esphome/components/i2s_audio/speaker/__init__.py +12 -4
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +426 -200
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +92 -33
- esphome/components/ili9xxx/display.py +5 -1
- esphome/components/image/__init__.py +5 -8
- esphome/components/image/image.cpp +14 -14
- esphome/components/image/image.h +20 -24
- esphome/components/internal_temperature/internal_temperature.cpp +51 -2
- esphome/components/internal_temperature/internal_temperature.h +1 -0
- esphome/components/ld2420/ld2420.cpp +1 -1
- esphome/components/libretiny/gpio.py +4 -2
- esphome/components/light/__init__.py +32 -1
- esphome/components/light/automation.py +39 -32
- esphome/components/light/effects.py +36 -36
- esphome/components/light/light_state.cpp +6 -16
- esphome/components/light/light_state.h +34 -0
- esphome/components/light/types.py +3 -1
- esphome/components/logger/logger_esp32.cpp +15 -0
- esphome/components/lvgl/__init__.py +202 -95
- esphome/components/lvgl/automation.py +42 -40
- esphome/components/lvgl/binary_sensor/__init__.py +8 -15
- esphome/components/lvgl/defines.py +14 -8
- esphome/components/lvgl/encoders.py +11 -8
- esphome/components/lvgl/keypads.py +77 -0
- esphome/components/lvgl/light/__init__.py +6 -8
- esphome/components/lvgl/lv_validation.py +2 -4
- esphome/components/lvgl/lvcode.py +3 -9
- esphome/components/lvgl/lvgl_esphome.cpp +210 -89
- esphome/components/lvgl/lvgl_esphome.h +113 -30
- esphome/components/lvgl/lvgl_proxy.h +17 -0
- esphome/components/lvgl/number/__init__.py +10 -15
- esphome/components/lvgl/schemas.py +4 -2
- esphome/components/lvgl/select/__init__.py +12 -37
- esphome/components/lvgl/select/lvgl_select.h +27 -33
- esphome/components/lvgl/sensor/__init__.py +8 -14
- esphome/components/lvgl/styles.py +3 -4
- esphome/components/lvgl/switch/__init__.py +8 -13
- esphome/components/lvgl/text/__init__.py +5 -6
- esphome/components/lvgl/text_sensor/__init__.py +15 -15
- esphome/components/lvgl/touchscreens.py +2 -3
- esphome/components/lvgl/trigger.py +7 -9
- esphome/components/lvgl/types.py +9 -3
- esphome/components/lvgl/widgets/__init__.py +32 -21
- esphome/components/lvgl/widgets/dropdown.py +22 -10
- esphome/components/lvgl/widgets/msgbox.py +6 -5
- esphome/components/lvgl/widgets/obj.py +4 -2
- esphome/components/lvgl/widgets/page.py +3 -2
- esphome/components/lvgl/widgets/qrcode.py +54 -0
- esphome/components/lvgl/widgets/roller.py +21 -14
- esphome/components/lvgl/widgets/tileview.py +2 -1
- esphome/components/max17043/__init__.py +1 -0
- esphome/components/max17043/automation.h +20 -0
- esphome/components/max17043/max17043.cpp +98 -0
- esphome/components/max17043/max17043.h +29 -0
- esphome/components/max17043/sensor.py +77 -0
- esphome/components/media_player/__init__.py +11 -0
- esphome/components/media_player/automation.h +10 -0
- esphome/components/media_player/media_player.cpp +4 -0
- esphome/components/midea/air_conditioner.cpp +17 -1
- esphome/components/mlx90393/sensor.py +1 -1
- esphome/components/modbus_controller/__init__.py +31 -1
- esphome/components/modbus_controller/automation.h +16 -0
- esphome/components/modbus_controller/const.py +2 -0
- esphome/components/modbus_controller/modbus_controller.cpp +14 -2
- esphome/components/modbus_controller/modbus_controller.h +9 -0
- esphome/components/mopeka_pro_check/mopeka_pro_check.cpp +40 -21
- esphome/components/mopeka_pro_check/mopeka_pro_check.h +9 -2
- esphome/components/mopeka_pro_check/sensor.py +41 -0
- esphome/components/mqtt/__init__.py +36 -0
- esphome/components/mqtt/mqtt_client.cpp +27 -3
- esphome/components/mqtt/mqtt_client.h +27 -2
- esphome/components/mqtt/mqtt_climate.cpp +4 -2
- esphome/components/mqtt/mqtt_component.cpp +6 -0
- esphome/components/mqtt/mqtt_component.h +4 -0
- esphome/components/mqtt/mqtt_const.h +6 -0
- esphome/components/online_image/online_image.cpp +2 -8
- esphome/components/online_image/online_image.h +2 -6
- esphome/components/opentherm/__init__.py +35 -9
- esphome/components/opentherm/binary_sensor/__init__.py +33 -0
- esphome/components/opentherm/const.py +11 -0
- esphome/components/opentherm/generate.py +142 -0
- esphome/components/opentherm/hub.cpp +130 -24
- esphome/components/opentherm/hub.h +62 -9
- esphome/components/opentherm/input.h +18 -0
- esphome/components/opentherm/input.py +51 -0
- esphome/components/opentherm/number/__init__.py +74 -0
- esphome/components/opentherm/number/number.cpp +40 -0
- esphome/components/opentherm/number/number.h +31 -0
- esphome/components/opentherm/opentherm.cpp +30 -0
- esphome/components/opentherm/opentherm.h +34 -2
- esphome/components/opentherm/opentherm_macros.h +151 -0
- esphome/components/opentherm/output/__init__.py +47 -0
- esphome/components/opentherm/output/output.cpp +18 -0
- esphome/components/opentherm/output/output.h +33 -0
- esphome/components/opentherm/schema.py +814 -0
- esphome/components/opentherm/sensor/__init__.py +51 -0
- esphome/components/opentherm/switch/__init__.py +43 -0
- esphome/components/opentherm/switch/switch.cpp +28 -0
- esphome/components/opentherm/switch/switch.h +20 -0
- esphome/components/opentherm/validate.py +31 -0
- esphome/components/pcd8544/display.py +8 -4
- esphome/components/prometheus/prometheus_handler.cpp +176 -14
- esphome/components/prometheus/prometheus_handler.h +25 -7
- esphome/components/qspi_amoled/display.py +1 -141
- esphome/components/qspi_dbi/display.py +185 -0
- esphome/components/qspi_dbi/models.py +64 -0
- esphome/components/{qspi_amoled/qspi_amoled.cpp → qspi_dbi/qspi_dbi.cpp} +95 -46
- esphome/components/{qspi_amoled/qspi_amoled.h → qspi_dbi/qspi_dbi.h} +26 -15
- esphome/components/rp2040/__init__.py +6 -3
- esphome/components/rp2040/gpio.py +5 -3
- esphome/components/rtttl/rtttl.cpp +4 -1
- esphome/components/rtttl/rtttl.h +1 -0
- esphome/components/sdl/sdl_esphome.cpp +22 -5
- esphome/components/sdl/sdl_esphome.h +1 -0
- esphome/components/sdm_meter/sdm_meter.cpp +1 -1
- esphome/components/sensor/__init__.py +18 -8
- esphome/components/sensor/filter.cpp +19 -18
- esphome/components/sensor/filter.h +9 -10
- esphome/components/sgp4x/sgp4x.cpp +40 -74
- esphome/components/sgp4x/sgp4x.h +5 -3
- esphome/components/speaker/__init__.py +51 -5
- esphome/components/speaker/automation.h +25 -0
- esphome/components/speaker/speaker.h +72 -1
- esphome/components/spi/__init__.py +15 -14
- esphome/components/spi_device/__init__.py +4 -15
- esphome/components/ssd1306_spi/display.py +6 -2
- esphome/components/ssd1322_spi/display.py +6 -2
- esphome/components/ssd1325_spi/display.py +6 -2
- esphome/components/ssd1327_spi/display.py +6 -2
- esphome/components/ssd1331_spi/display.py +6 -2
- esphome/components/ssd1351_spi/display.py +6 -2
- esphome/components/st7567_spi/display.py +6 -2
- esphome/components/st7701s/display.py +5 -1
- esphome/components/st7735/display.py +10 -5
- esphome/components/st7789v/display.py +12 -7
- esphome/components/statsd/statsd.cpp +2 -0
- esphome/components/statsd/statsd.h +2 -0
- esphome/components/sun/sun.h +3 -0
- esphome/components/tc74/__init__.py +1 -0
- esphome/components/tc74/sensor.py +32 -0
- esphome/components/tc74/tc74.cpp +68 -0
- esphome/components/tc74/tc74.h +28 -0
- esphome/components/touchscreen/__init__.py +41 -50
- esphome/components/touchscreen/touchscreen.h +4 -8
- esphome/components/tuya/fan/tuya_fan.cpp +1 -1
- esphome/components/udp/udp_component.cpp +6 -3
- esphome/components/udp/udp_component.h +4 -2
- esphome/components/waveshare_epaper/display.py +6 -2
- esphome/components/web_server/web_server.cpp +22 -0
- esphome/components/web_server/web_server.h +3 -0
- esphome/components/weikai/weikai.h +2 -2
- esphome/components/wifi/wifi_component.cpp +2 -2
- esphome/components/wifi/wifi_component_esp32_arduino.cpp +4 -4
- esphome/components/wifi/wifi_component_esp8266.cpp +4 -4
- esphome/components/wifi/wifi_component_esp_idf.cpp +2 -2
- esphome/components/xpt2046/touchscreen/__init__.py +7 -32
- esphome/config_validation.py +3 -1
- esphome/const.py +9 -2
- esphome/core/defines.h +8 -2
- esphome/core/helpers.cpp +32 -17
- esphome/core/helpers.h +32 -16
- esphome/core/ring_buffer.cpp +2 -2
- esphome/core/ring_buffer.h +2 -2
- esphome/dashboard/core.py +25 -0
- esphome/dashboard/status/mdns.py +3 -4
- esphome/dashboard/web_server.py +54 -19
- esphome/espota2.py +36 -35
- esphome/helpers.py +68 -16
- esphome/mqtt.py +9 -2
- esphome/storage_json.py +4 -0
- esphome/writer.py +7 -18
- esphome/zeroconf.py +8 -6
- {esphome-2024.10.3.dist-info → esphome-2024.11.0.dist-info}/METADATA +7 -5
- {esphome-2024.10.3.dist-info → esphome-2024.11.0.dist-info}/RECORD +232 -186
- esphome/core/bytebuffer.cpp +0 -167
- esphome/core/bytebuffer.h +0 -144
- /esphome/components/{qspi_amoled → qspi_dbi}/__init__.py +0 -0
- {esphome-2024.10.3.dist-info → esphome-2024.11.0.dist-info}/LICENSE +0 -0
- {esphome-2024.10.3.dist-info → esphome-2024.11.0.dist-info}/WHEEL +0 -0
- {esphome-2024.10.3.dist-info → esphome-2024.11.0.dist-info}/entry_points.txt +0 -0
- {esphome-2024.10.3.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
|
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->
|
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->
|
29
|
-
|
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::
|
37
|
-
|
38
|
-
|
39
|
-
|
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 (
|
42
|
-
|
43
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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 (
|
52
|
-
|
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
|
-
|
56
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
75
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
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
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
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
|
-
|
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
|
-
|
130
|
-
|
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
|
-
|
261
|
+
xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_RUNNING);
|
133
262
|
|
134
|
-
|
135
|
-
|
136
|
-
|
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
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
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
|
-
|
155
|
-
|
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
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
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
|
-
|
179
|
-
|
180
|
-
|
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
|
-
|
188
|
-
|
189
|
-
|
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
|
-
|
193
|
-
|
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
|
-
|
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
|
-
|
363
|
+
xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY);
|
215
364
|
} else {
|
216
|
-
|
365
|
+
xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_STOP);
|
217
366
|
}
|
218
367
|
}
|
219
368
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
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
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
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
|
-
|
267
|
-
if (this->
|
268
|
-
|
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
|
-
|
272
|
-
|
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
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
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
|
-
|
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
|
-
|
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
|