esphome 2024.12.4__py3-none-any.whl → 2025.2.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 +16 -3
- esphome/components/adc/__init__.py +17 -11
- esphome/components/adc/adc_sensor.h +17 -0
- esphome/components/adc/adc_sensor_common.cpp +55 -0
- esphome/components/adc/adc_sensor_esp32.cpp +8 -5
- esphome/components/adc/adc_sensor_esp8266.cpp +10 -6
- esphome/components/adc/adc_sensor_libretiny.cpp +11 -6
- esphome/components/adc/adc_sensor_rp2040.cpp +13 -10
- esphome/components/adc/sensor.py +9 -3
- esphome/components/ads1115/ads1115.cpp +56 -7
- esphome/components/ads1115/ads1115.h +13 -1
- esphome/components/ads1115/sensor/__init__.py +16 -0
- esphome/components/ads1115/sensor/ads1115_sensor.cpp +2 -1
- esphome/components/ads1115/sensor/ads1115_sensor.h +2 -0
- esphome/components/animation/__init__.py +23 -261
- esphome/components/animation/animation.cpp +2 -2
- esphome/components/animation/animation.h +2 -1
- esphome/components/api/api_pb2.cpp +14 -0
- esphome/components/api/api_pb2.h +1 -0
- esphome/components/api/client.py +8 -3
- esphome/components/audio/__init__.py +112 -0
- esphome/components/audio/audio.cpp +67 -0
- esphome/components/audio/audio.h +125 -7
- esphome/components/audio/audio_decoder.cpp +361 -0
- esphome/components/audio/audio_decoder.h +135 -0
- esphome/components/audio/audio_reader.cpp +308 -0
- esphome/components/audio/audio_reader.h +85 -0
- esphome/components/audio/audio_resampler.cpp +159 -0
- esphome/components/audio/audio_resampler.h +101 -0
- esphome/components/audio/audio_transfer_buffer.cpp +165 -0
- esphome/components/audio/audio_transfer_buffer.h +139 -0
- esphome/components/audio_adc/__init__.py +41 -0
- esphome/components/audio_adc/audio_adc.h +17 -0
- esphome/components/audio_adc/automation.h +23 -0
- esphome/components/bk72xx/__init__.py +1 -0
- esphome/components/ble_client/ble_client.cpp +1 -2
- esphome/components/ble_client/sensor/__init__.py +1 -1
- esphome/components/ble_client/text_sensor/__init__.py +1 -1
- esphome/components/bluetooth_proxy/bluetooth_connection.cpp +5 -0
- esphome/components/bluetooth_proxy/bluetooth_connection.h +1 -0
- esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +5 -0
- esphome/components/ch422g/ch422g.h +2 -0
- esphome/components/climate/__init__.py +1 -1
- esphome/components/climate_ir/climate_ir.cpp +2 -1
- esphome/components/coolix/coolix.cpp +2 -1
- esphome/components/cse7766/cse7766.cpp +8 -16
- esphome/components/custom/__init__.py +0 -3
- esphome/components/custom/binary_sensor/__init__.py +2 -28
- esphome/components/custom/climate/__init__.py +2 -27
- esphome/components/custom/cover/__init__.py +2 -27
- esphome/components/custom/light/__init__.py +2 -27
- esphome/components/custom/output/__init__.py +2 -58
- esphome/components/custom/sensor/__init__.py +2 -24
- esphome/components/custom/switch/__init__.py +2 -24
- esphome/components/custom/text_sensor/__init__.py +2 -29
- esphome/components/custom_component/__init__.py +3 -27
- esphome/components/daly_bms/daly_bms.cpp +6 -0
- esphome/components/daly_bms/daly_bms.h +2 -0
- esphome/components/daly_bms/sensor.py +6 -0
- esphome/components/debug/debug_component.cpp +4 -0
- esphome/components/debug/debug_component.h +14 -0
- esphome/components/debug/debug_esp32.cpp +154 -74
- esphome/components/dfplayer/dfplayer.cpp +15 -2
- esphome/components/dfrobot_sen0395/dfrobot_sen0395.cpp +2 -1
- esphome/components/dht/dht.cpp +4 -2
- esphome/components/dht/sensor.py +1 -1
- esphome/components/display/__init__.py +18 -5
- esphome/components/display/display.cpp +16 -3
- esphome/components/display/rect.cpp +2 -1
- esphome/components/es7210/__init__.py +0 -0
- esphome/components/es7210/audio_adc.py +51 -0
- esphome/components/es7210/es7210.cpp +228 -0
- esphome/components/es7210/es7210.h +62 -0
- esphome/components/es7210/es7210_const.h +129 -0
- esphome/components/es7243e/__init__.py +0 -0
- esphome/components/es7243e/audio_adc.py +34 -0
- esphome/components/es7243e/es7243e.cpp +125 -0
- esphome/components/es7243e/es7243e.h +37 -0
- esphome/components/es7243e/es7243e_const.h +54 -0
- esphome/components/es8156/__init__.py +0 -0
- esphome/components/es8156/audio_dac.py +27 -0
- esphome/components/es8156/es8156.cpp +87 -0
- esphome/components/es8156/es8156.h +51 -0
- esphome/components/es8156/es8156_const.h +68 -0
- esphome/components/es8311/audio_dac.py +1 -2
- esphome/components/esp32/__init__.py +1 -0
- esphome/components/esp32/core.cpp +5 -1
- esphome/components/esp32/gpio.h +2 -0
- esphome/components/esp32_ble/__init__.py +39 -0
- esphome/components/esp32_ble/queue.h +4 -4
- esphome/components/esp32_ble_client/ble_client_base.cpp +46 -0
- esphome/components/esp32_ble_client/ble_client_base.h +2 -0
- esphome/components/esp32_ble_server/__init__.py +582 -12
- esphome/components/esp32_ble_server/ble_characteristic.cpp +48 -60
- esphome/components/esp32_ble_server/ble_characteristic.h +24 -17
- esphome/components/esp32_ble_server/ble_descriptor.cpp +21 -9
- esphome/components/esp32_ble_server/ble_descriptor.h +17 -6
- esphome/components/esp32_ble_server/ble_server.cpp +62 -67
- esphome/components/esp32_ble_server/ble_server.h +28 -32
- esphome/components/esp32_ble_server/ble_server_automations.cpp +77 -0
- esphome/components/esp32_ble_server/ble_server_automations.h +115 -0
- esphome/components/esp32_ble_server/ble_service.cpp +17 -15
- esphome/components/esp32_ble_server/ble_service.h +10 -14
- esphome/components/esp32_ble_tracker/__init__.py +6 -39
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +33 -10
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +8 -4
- esphome/components/esp32_dac/esp32_dac.cpp +16 -7
- esphome/components/esp32_dac/esp32_dac.h +8 -0
- esphome/components/esp32_dac/output.py +16 -4
- esphome/components/esp32_improv/__init__.py +2 -8
- esphome/components/esp32_improv/esp32_improv_component.cpp +21 -20
- esphome/components/esp32_improv/esp32_improv_component.h +3 -4
- esphome/components/esp32_rmt/__init__.py +28 -3
- esphome/components/esp32_rmt_led_strip/led_strip.cpp +73 -6
- esphome/components/esp32_rmt_led_strip/led_strip.h +21 -3
- esphome/components/esp32_rmt_led_strip/light.py +72 -7
- esphome/components/esp32_touch/esp32_touch.cpp +5 -0
- esphome/components/esp8266/__init__.py +1 -0
- esphome/components/esp8266/gpio.h +1 -0
- esphome/components/ethernet/__init__.py +10 -10
- esphome/components/event/event.cpp +4 -2
- esphome/components/event/event.h +2 -0
- esphome/components/event_emitter/__init__.py +5 -0
- esphome/components/event_emitter/event_emitter.cpp +14 -0
- esphome/components/event_emitter/event_emitter.h +63 -0
- esphome/components/font/__init__.py +1 -1
- esphome/components/gcja5/gcja5.cpp +2 -1
- esphome/components/graph/graph.cpp +4 -9
- esphome/components/haier/haier_base.cpp +2 -1
- esphome/components/haier/hon_climate.cpp +2 -1
- esphome/components/heatpumpir/heatpumpir.cpp +2 -1
- esphome/components/host/__init__.py +1 -0
- esphome/components/host/gpio.h +1 -0
- esphome/components/http_request/http_request.h +2 -2
- esphome/components/http_request/http_request_arduino.cpp +1 -1
- esphome/components/http_request/http_request_idf.cpp +1 -1
- esphome/components/i2c/i2c_bus_esp_idf.cpp +4 -0
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +7 -5
- esphome/components/i2s_audio/speaker/__init__.py +53 -6
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +92 -46
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +8 -0
- esphome/components/ili9xxx/display.py +29 -11
- esphome/components/ili9xxx/ili9xxx_display.cpp +2 -5
- esphome/components/ili9xxx/ili9xxx_display.h +2 -1
- esphome/components/image/__init__.py +443 -255
- esphome/components/image/image.cpp +115 -61
- esphome/components/image/image.h +15 -24
- esphome/components/json/json_util.cpp +8 -34
- esphome/components/libretiny/__init__.py +1 -0
- esphome/components/libretiny/gpio_arduino.h +1 -0
- esphome/components/light/light_color_values.h +1 -1
- esphome/components/logger/__init__.py +45 -9
- esphome/components/logger/logger.cpp +16 -14
- esphome/components/logger/logger.h +11 -7
- esphome/components/logger/select/__init__.py +29 -0
- esphome/components/logger/select/logger_level_select.cpp +27 -0
- esphome/components/logger/select/logger_level_select.h +15 -0
- esphome/components/lvgl/__init__.py +96 -73
- esphome/components/lvgl/automation.py +39 -7
- esphome/components/lvgl/defines.py +8 -2
- esphome/components/lvgl/lvgl_esphome.cpp +8 -15
- esphome/components/lvgl/lvgl_esphome.h +20 -5
- esphome/components/lvgl/schemas.py +25 -14
- esphome/components/lvgl/trigger.py +27 -3
- esphome/components/lvgl/widgets/dropdown.py +1 -1
- esphome/components/lvgl/widgets/keyboard.py +8 -1
- esphome/components/lvgl/widgets/meter.py +2 -1
- esphome/components/lvgl/widgets/msgbox.py +1 -1
- esphome/components/lvgl/widgets/obj.py +1 -12
- esphome/components/lvgl/widgets/page.py +37 -2
- esphome/components/lvgl/widgets/tabview.py +1 -1
- esphome/components/max6956/max6956.h +2 -0
- esphome/components/mcp23016/mcp23016.h +2 -0
- esphome/components/mcp23xxx_base/mcp23xxx_base.h +2 -0
- esphome/components/mdns/__init__.py +1 -1
- esphome/components/media_player/__init__.py +37 -8
- esphome/components/media_player/automation.h +11 -2
- esphome/components/media_player/media_player.cpp +8 -0
- esphome/components/media_player/media_player.h +8 -4
- esphome/components/micronova/switch/micronova_switch.cpp +4 -2
- esphome/components/midea/ac_automations.h +3 -1
- esphome/components/midea/air_conditioner.cpp +7 -5
- esphome/components/midea/air_conditioner.h +1 -1
- esphome/components/midea/climate.py +4 -2
- esphome/components/midea/ir_transmitter.h +36 -5
- esphome/components/mixer/__init__.py +0 -0
- esphome/components/mixer/speaker/__init__.py +172 -0
- esphome/components/mixer/speaker/automation.h +19 -0
- esphome/components/mixer/speaker/mixer_speaker.cpp +624 -0
- esphome/components/mixer/speaker/mixer_speaker.h +207 -0
- esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp +7 -13
- esphome/components/mpr121/mpr121.h +2 -0
- esphome/components/mqtt/__init__.py +1 -1
- esphome/components/mqtt/mqtt_client.cpp +7 -1
- esphome/components/mqtt/mqtt_client.h +1 -1
- esphome/components/mqtt/mqtt_climate.cpp +2 -2
- esphome/components/network/ip_address.h +2 -0
- esphome/components/nextion/automation.h +17 -0
- esphome/components/nextion/display.py +42 -17
- esphome/components/nextion/nextion.cpp +4 -10
- esphome/components/nextion/nextion.h +89 -82
- esphome/components/nextion/nextion_commands.cpp +10 -10
- esphome/components/ntc/sensor.py +2 -4
- esphome/components/online_image/__init__.py +98 -46
- esphome/components/online_image/bmp_image.cpp +101 -0
- esphome/components/online_image/bmp_image.h +40 -0
- esphome/components/online_image/image_decoder.cpp +31 -2
- esphome/components/online_image/image_decoder.h +24 -15
- esphome/components/online_image/jpeg_image.cpp +92 -0
- esphome/components/online_image/jpeg_image.h +34 -0
- esphome/components/online_image/online_image.cpp +118 -58
- esphome/components/online_image/online_image.h +39 -9
- esphome/components/online_image/png_image.cpp +7 -3
- esphome/components/online_image/png_image.h +2 -1
- esphome/components/opentherm/__init__.py +73 -7
- esphome/components/opentherm/automation.h +25 -0
- esphome/components/opentherm/const.py +1 -0
- esphome/components/opentherm/generate.py +39 -6
- esphome/components/opentherm/hub.cpp +117 -79
- esphome/components/opentherm/hub.h +31 -15
- esphome/components/opentherm/opentherm.cpp +47 -23
- esphome/components/opentherm/opentherm.h +27 -6
- esphome/components/opentherm/opentherm_macros.h +11 -0
- esphome/components/opentherm/schema.py +78 -1
- esphome/components/opentherm/validate.py +7 -2
- esphome/components/pca6416a/pca6416a.h +2 -0
- esphome/components/pca9554/pca9554.h +2 -0
- esphome/components/pcf8574/pcf8574.h +2 -0
- esphome/components/preferences/__init__.py +2 -4
- esphome/components/preferences/syncer.h +10 -3
- esphome/components/prometheus/prometheus_handler.cpp +313 -0
- esphome/components/prometheus/prometheus_handler.h +48 -7
- esphome/components/psram/psram.cpp +8 -1
- esphome/components/pulse_counter/pulse_counter_sensor.cpp +14 -9
- esphome/components/pulse_counter/pulse_counter_sensor.h +4 -4
- esphome/components/pulse_meter/pulse_meter_sensor.cpp +2 -0
- esphome/components/qspi_dbi/__init__.py +3 -0
- esphome/components/qspi_dbi/display.py +74 -47
- esphome/components/qspi_dbi/models.py +245 -2
- esphome/components/qspi_dbi/qspi_dbi.cpp +9 -16
- esphome/components/qspi_dbi/qspi_dbi.h +2 -2
- esphome/components/remote_base/__init__.py +77 -25
- esphome/components/remote_base/remote_base.cpp +1 -1
- esphome/components/remote_base/remote_base.h +20 -2
- esphome/components/remote_base/toto_protocol.cpp +100 -0
- esphome/components/remote_base/toto_protocol.h +45 -0
- esphome/components/remote_receiver/__init__.py +55 -10
- esphome/components/remote_receiver/remote_receiver.h +36 -3
- esphome/components/remote_receiver/remote_receiver_esp32.cpp +145 -6
- esphome/components/remote_transmitter/__init__.py +62 -4
- esphome/components/remote_transmitter/remote_transmitter.h +21 -2
- esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +140 -4
- esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +3 -3
- esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +3 -3
- esphome/components/resampler/__init__.py +0 -0
- esphome/components/resampler/speaker/__init__.py +103 -0
- esphome/components/resampler/speaker/resampler_speaker.cpp +318 -0
- esphome/components/resampler/speaker/resampler_speaker.h +107 -0
- esphome/components/resistance/resistance_sensor.h +2 -3
- esphome/components/resistance/sensor.py +2 -9
- esphome/components/rotary_encoder/rotary_encoder.cpp +8 -4
- esphome/components/rp2040/__init__.py +1 -0
- esphome/components/rp2040/gpio.h +1 -0
- esphome/components/rtl87xx/__init__.py +2 -0
- esphome/components/scd30/sensor.py +1 -1
- esphome/components/sdl/binary_sensor.py +270 -0
- esphome/components/sdl/sdl_esphome.cpp +16 -0
- esphome/components/sdl/sdl_esphome.h +9 -0
- esphome/components/seeed_mr60bha2/binary_sensor.py +25 -0
- esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp +26 -2
- esphome/components/seeed_mr60bha2/seeed_mr60bha2.h +9 -20
- esphome/components/seeed_mr60bha2/sensor.py +9 -1
- esphome/components/sn74hc165/sn74hc165.h +3 -0
- esphome/components/sn74hc595/sn74hc595.h +3 -0
- esphome/components/speaker/__init__.py +5 -4
- esphome/components/speaker/media_player/__init__.py +458 -0
- esphome/components/speaker/media_player/audio_pipeline.cpp +568 -0
- esphome/components/speaker/media_player/audio_pipeline.h +159 -0
- esphome/components/speaker/media_player/automation.h +26 -0
- esphome/components/speaker/media_player/speaker_media_player.cpp +577 -0
- esphome/components/speaker/media_player/speaker_media_player.h +160 -0
- esphome/components/speaker/speaker.h +20 -0
- esphome/components/spi/__init__.py +1 -5
- esphome/components/spi/spi.cpp +7 -1
- esphome/components/spi/spi.h +21 -2
- esphome/components/spi_led_strip/light.py +3 -5
- esphome/components/spi_led_strip/spi_led_strip.cpp +67 -0
- esphome/components/spi_led_strip/spi_led_strip.h +8 -60
- esphome/components/sprinkler/sprinkler.cpp +3 -1
- esphome/components/sx1509/sx1509_gpio_pin.h +2 -0
- esphome/components/tca9555/tca9555.h +2 -0
- esphome/components/toshiba/toshiba.cpp +2 -1
- esphome/components/tuya/light/tuya_light.cpp +4 -2
- esphome/components/uart/uart_component_esp32_arduino.cpp +2 -2
- esphome/components/uart/uart_component_esp_idf.cpp +2 -2
- esphome/components/udp/__init__.py +8 -2
- esphome/components/udp/udp_component.cpp +25 -56
- esphome/components/udp/udp_component.h +3 -0
- esphome/components/uponor_smatrix/sensor/__init__.py +14 -4
- esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +5 -0
- esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.h +1 -0
- esphome/components/uptime/text_sensor/__init__.py +19 -0
- esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +63 -0
- esphome/components/uptime/text_sensor/uptime_text_sensor.h +25 -0
- esphome/components/voice_assistant/voice_assistant.cpp +24 -14
- esphome/components/voice_assistant/voice_assistant.h +8 -0
- esphome/components/waveshare_epaper/display.py +22 -1
- esphome/components/waveshare_epaper/waveshare_213v3.cpp +9 -3
- esphome/components/waveshare_epaper/waveshare_epaper.cpp +1155 -44
- esphome/components/waveshare_epaper/waveshare_epaper.h +208 -7
- esphome/components/web_server/web_server.cpp +28 -6
- esphome/components/weikai/weikai.h +2 -0
- esphome/components/wifi/__init__.py +6 -6
- esphome/components/wifi/wifi_component.cpp +1 -1
- esphome/components/wifi/wifi_component_esp32_arduino.cpp +30 -1
- esphome/components/wireguard/__init__.py +2 -2
- esphome/components/xl9535/xl9535.h +2 -0
- esphome/components/xxtea/__init__.py +3 -0
- esphome/components/xxtea/xxtea.cpp +46 -0
- esphome/components/xxtea/xxtea.h +26 -0
- esphome/components/yashima/yashima.cpp +2 -1
- esphome/config.py +9 -5
- esphome/config_validation.py +55 -17
- esphome/const.py +7 -10
- esphome/core/__init__.py +6 -13
- esphome/core/base_automation.h +1 -0
- esphome/core/config.py +59 -72
- esphome/core/defines.h +9 -1
- esphome/core/gpio.h +7 -0
- esphome/core/helpers.cpp +19 -15
- esphome/core/helpers.h +57 -8
- esphome/core/log.h +9 -7
- esphome/cpp_generator.py +2 -2
- esphome/dashboard/web_server.py +1 -1
- esphome/espota2.py +3 -2
- esphome/loader.py +12 -4
- esphome/log.py +5 -7
- esphome/yaml_util.py +2 -2
- {esphome-2024.12.4.dist-info → esphome-2025.2.0.dist-info}/METADATA +14 -9
- {esphome-2024.12.4.dist-info → esphome-2025.2.0.dist-info}/RECORD +349 -300
- esphome/components/custom/binary_sensor/custom_binary_sensor.cpp +0 -16
- esphome/components/custom/binary_sensor/custom_binary_sensor.h +0 -26
- esphome/components/custom/climate/custom_climate.h +0 -22
- esphome/components/custom/cover/custom_cover.h +0 -21
- esphome/components/custom/light/custom_light_output.h +0 -24
- esphome/components/custom/output/custom_output.h +0 -37
- esphome/components/custom/sensor/custom_sensor.cpp +0 -16
- esphome/components/custom/sensor/custom_sensor.h +0 -24
- esphome/components/custom/switch/custom_switch.cpp +0 -16
- esphome/components/custom/switch/custom_switch.h +0 -24
- esphome/components/custom/text_sensor/custom_text_sensor.cpp +0 -16
- esphome/components/custom/text_sensor/custom_text_sensor.h +0 -26
- esphome/components/custom_component/custom_component.h +0 -28
- esphome/components/esp32_ble_server/ble_2901.cpp +0 -18
- esphome/components/esp32_ble_server/ble_2901.h +0 -19
- esphome/components/resistance_sampler/__init__.py +0 -6
- esphome/components/resistance_sampler/resistance_sampler.h +0 -10
- esphome/components/uptime/{sensor.py → sensor/__init__.py} +3 -3
- /esphome/components/uptime/{uptime_seconds_sensor.cpp → sensor/uptime_seconds_sensor.cpp} +0 -0
- /esphome/components/uptime/{uptime_seconds_sensor.h → sensor/uptime_seconds_sensor.h} +0 -0
- /esphome/components/uptime/{uptime_timestamp_sensor.cpp → sensor/uptime_timestamp_sensor.cpp} +0 -0
- /esphome/components/uptime/{uptime_timestamp_sensor.h → sensor/uptime_timestamp_sensor.h} +0 -0
- {esphome-2024.12.4.dist-info → esphome-2025.2.0.dist-info}/LICENSE +0 -0
- {esphome-2024.12.4.dist-info → esphome-2025.2.0.dist-info}/WHEEL +0 -0
- {esphome-2024.12.4.dist-info → esphome-2025.2.0.dist-info}/entry_points.txt +0 -0
- {esphome-2024.12.4.dist-info → esphome-2025.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,568 @@
|
|
1
|
+
#include "audio_pipeline.h"
|
2
|
+
|
3
|
+
#ifdef USE_ESP_IDF
|
4
|
+
|
5
|
+
#include "esphome/core/defines.h"
|
6
|
+
#include "esphome/core/hal.h"
|
7
|
+
#include "esphome/core/helpers.h"
|
8
|
+
#include "esphome/core/log.h"
|
9
|
+
|
10
|
+
namespace esphome {
|
11
|
+
namespace speaker {
|
12
|
+
|
13
|
+
static const uint32_t INITIAL_BUFFER_MS = 1000; // Start playback after buffering this duration of the file
|
14
|
+
|
15
|
+
static const uint32_t READ_TASK_STACK_SIZE = 5 * 1024;
|
16
|
+
static const uint32_t DECODE_TASK_STACK_SIZE = 3 * 1024;
|
17
|
+
|
18
|
+
static const uint32_t INFO_ERROR_QUEUE_COUNT = 5;
|
19
|
+
|
20
|
+
static const char *const TAG = "speaker_media_player.pipeline";
|
21
|
+
|
22
|
+
enum EventGroupBits : uint32_t {
|
23
|
+
// MESSAGE_* bits are only set by their respective tasks
|
24
|
+
|
25
|
+
// Stops all activity in the pipeline elements; cleared by process_state() and set by stop() or by each task
|
26
|
+
PIPELINE_COMMAND_STOP = (1 << 0),
|
27
|
+
|
28
|
+
// Read audio from an HTTP source; cleared by reader task and set by start_url
|
29
|
+
READER_COMMAND_INIT_HTTP = (1 << 4),
|
30
|
+
// Read audio from an audio file from the flash; cleared by reader task and set by start_file
|
31
|
+
READER_COMMAND_INIT_FILE = (1 << 5),
|
32
|
+
|
33
|
+
// Audio file type is read after checking it is supported; cleared by decoder task
|
34
|
+
READER_MESSAGE_LOADED_MEDIA_TYPE = (1 << 6),
|
35
|
+
// Reader is done (either through a failure or just end of the stream); cleared by reader task
|
36
|
+
READER_MESSAGE_FINISHED = (1 << 7),
|
37
|
+
// Error reading the file; cleared by process_state()
|
38
|
+
READER_MESSAGE_ERROR = (1 << 8),
|
39
|
+
|
40
|
+
// Decoder is done (either through a faiilure or the end of the stream); cleared by decoder task
|
41
|
+
DECODER_MESSAGE_FINISHED = (1 << 12),
|
42
|
+
// Error decoding the file; cleared by process_state() by decoder task
|
43
|
+
DECODER_MESSAGE_ERROR = (1 << 13),
|
44
|
+
};
|
45
|
+
|
46
|
+
AudioPipeline::AudioPipeline(speaker::Speaker *speaker, size_t buffer_size, bool task_stack_in_psram,
|
47
|
+
std::string base_name, UBaseType_t priority)
|
48
|
+
: base_name_(std::move(base_name)),
|
49
|
+
priority_(priority),
|
50
|
+
task_stack_in_psram_(task_stack_in_psram),
|
51
|
+
speaker_(speaker),
|
52
|
+
buffer_size_(buffer_size) {
|
53
|
+
this->allocate_communications_();
|
54
|
+
this->transfer_buffer_size_ = std::min(buffer_size_ / 4, DEFAULT_TRANSFER_BUFFER_SIZE);
|
55
|
+
}
|
56
|
+
|
57
|
+
void AudioPipeline::start_url(const std::string &uri) {
|
58
|
+
if (this->is_playing_) {
|
59
|
+
xEventGroupSetBits(this->event_group_, PIPELINE_COMMAND_STOP);
|
60
|
+
}
|
61
|
+
this->current_uri_ = uri;
|
62
|
+
this->pending_url_ = true;
|
63
|
+
}
|
64
|
+
|
65
|
+
void AudioPipeline::start_file(audio::AudioFile *audio_file) {
|
66
|
+
if (this->is_playing_) {
|
67
|
+
xEventGroupSetBits(this->event_group_, PIPELINE_COMMAND_STOP);
|
68
|
+
}
|
69
|
+
this->current_audio_file_ = audio_file;
|
70
|
+
this->pending_file_ = true;
|
71
|
+
}
|
72
|
+
|
73
|
+
esp_err_t AudioPipeline::stop() {
|
74
|
+
xEventGroupSetBits(this->event_group_, EventGroupBits::PIPELINE_COMMAND_STOP);
|
75
|
+
|
76
|
+
return ESP_OK;
|
77
|
+
}
|
78
|
+
void AudioPipeline::set_pause_state(bool pause_state) {
|
79
|
+
this->speaker_->set_pause_state(pause_state);
|
80
|
+
|
81
|
+
this->pause_state_ = pause_state;
|
82
|
+
}
|
83
|
+
|
84
|
+
void AudioPipeline::suspend_tasks() {
|
85
|
+
if (this->read_task_handle_ != nullptr) {
|
86
|
+
vTaskSuspend(this->read_task_handle_);
|
87
|
+
}
|
88
|
+
if (this->decode_task_handle_ != nullptr) {
|
89
|
+
vTaskSuspend(this->decode_task_handle_);
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
void AudioPipeline::resume_tasks() {
|
94
|
+
if (this->read_task_handle_ != nullptr) {
|
95
|
+
vTaskResume(this->read_task_handle_);
|
96
|
+
}
|
97
|
+
if (this->decode_task_handle_ != nullptr) {
|
98
|
+
vTaskResume(this->decode_task_handle_);
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
AudioPipelineState AudioPipeline::process_state() {
|
103
|
+
/*
|
104
|
+
* Log items from info error queue
|
105
|
+
*/
|
106
|
+
InfoErrorEvent event;
|
107
|
+
if (this->info_error_queue_ != nullptr) {
|
108
|
+
while (xQueueReceive(this->info_error_queue_, &event, 0)) {
|
109
|
+
switch (event.source) {
|
110
|
+
case InfoErrorSource::READER:
|
111
|
+
if (event.err.has_value()) {
|
112
|
+
ESP_LOGE(TAG, "Media reader encountered an error: %s", esp_err_to_name(event.err.value()));
|
113
|
+
} else if (event.file_type.has_value()) {
|
114
|
+
ESP_LOGD(TAG, "Reading %s file type", audio_file_type_to_string(event.file_type.value()));
|
115
|
+
}
|
116
|
+
|
117
|
+
break;
|
118
|
+
case InfoErrorSource::DECODER:
|
119
|
+
if (event.err.has_value()) {
|
120
|
+
ESP_LOGE(TAG, "Decoder encountered an error: %s", esp_err_to_name(event.err.value()));
|
121
|
+
}
|
122
|
+
|
123
|
+
if (event.audio_stream_info.has_value()) {
|
124
|
+
ESP_LOGD(TAG, "Decoded audio has %d channels, %" PRId32 " Hz sample rate, and %d bits per sample",
|
125
|
+
event.audio_stream_info.value().get_channels(), event.audio_stream_info.value().get_sample_rate(),
|
126
|
+
event.audio_stream_info.value().get_bits_per_sample());
|
127
|
+
}
|
128
|
+
|
129
|
+
if (event.decoding_err.has_value()) {
|
130
|
+
switch (event.decoding_err.value()) {
|
131
|
+
case DecodingError::FAILED_HEADER:
|
132
|
+
ESP_LOGE(TAG, "Failed to parse the file's header.");
|
133
|
+
break;
|
134
|
+
case DecodingError::INCOMPATIBLE_BITS_PER_SAMPLE:
|
135
|
+
ESP_LOGE(TAG, "Incompatible bits per sample. Only 16 bits per sample is supported");
|
136
|
+
break;
|
137
|
+
case DecodingError::INCOMPATIBLE_CHANNELS:
|
138
|
+
ESP_LOGE(TAG, "Incompatible number of channels. Only 1 or 2 channel audio is supported.");
|
139
|
+
break;
|
140
|
+
}
|
141
|
+
}
|
142
|
+
break;
|
143
|
+
}
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
/*
|
148
|
+
* Determine the current state based on the event group bits and tasks' status
|
149
|
+
*/
|
150
|
+
|
151
|
+
EventBits_t event_bits = xEventGroupGetBits(this->event_group_);
|
152
|
+
|
153
|
+
if (this->pending_url_ || this->pending_file_) {
|
154
|
+
// Init command pending
|
155
|
+
if (!(event_bits & EventGroupBits::PIPELINE_COMMAND_STOP)) {
|
156
|
+
// Only start if there is no pending stop command
|
157
|
+
if ((this->read_task_handle_ == nullptr) || (this->decode_task_handle_ == nullptr)) {
|
158
|
+
// At least one task isn't running
|
159
|
+
this->start_tasks_();
|
160
|
+
}
|
161
|
+
|
162
|
+
if (this->pending_url_) {
|
163
|
+
xEventGroupSetBits(this->event_group_, EventGroupBits::READER_COMMAND_INIT_HTTP);
|
164
|
+
this->playback_ms_ = 0;
|
165
|
+
this->pending_url_ = false;
|
166
|
+
} else if (this->pending_file_) {
|
167
|
+
xEventGroupSetBits(this->event_group_, EventGroupBits::READER_COMMAND_INIT_FILE);
|
168
|
+
this->playback_ms_ = 0;
|
169
|
+
this->pending_file_ = false;
|
170
|
+
}
|
171
|
+
|
172
|
+
this->is_playing_ = true;
|
173
|
+
return AudioPipelineState::PLAYING;
|
174
|
+
}
|
175
|
+
}
|
176
|
+
|
177
|
+
if ((event_bits & EventGroupBits::READER_MESSAGE_FINISHED) &&
|
178
|
+
(!(event_bits & EventGroupBits::READER_MESSAGE_LOADED_MEDIA_TYPE) &&
|
179
|
+
(event_bits & EventGroupBits::DECODER_MESSAGE_FINISHED))) {
|
180
|
+
// Tasks are finished and there's no media in between the reader and decoder
|
181
|
+
|
182
|
+
if (event_bits & EventGroupBits::PIPELINE_COMMAND_STOP) {
|
183
|
+
// Stop command is fully processed, so clear the command bit
|
184
|
+
xEventGroupClearBits(this->event_group_, EventGroupBits::PIPELINE_COMMAND_STOP);
|
185
|
+
this->hard_stop_ = true;
|
186
|
+
}
|
187
|
+
|
188
|
+
if (!this->is_playing_) {
|
189
|
+
// The tasks have been stopped for two ``process_state`` calls in a row, so delete the tasks
|
190
|
+
if ((this->read_task_handle_ != nullptr) || (this->decode_task_handle_ != nullptr)) {
|
191
|
+
this->delete_tasks_();
|
192
|
+
if (this->hard_stop_) {
|
193
|
+
// Stop command was sent, so immediately end of the playback
|
194
|
+
this->speaker_->stop();
|
195
|
+
this->hard_stop_ = false;
|
196
|
+
} else {
|
197
|
+
// Decoded all the audio, so let the speaker finish playing before stopping
|
198
|
+
this->speaker_->finish();
|
199
|
+
}
|
200
|
+
}
|
201
|
+
}
|
202
|
+
this->is_playing_ = false;
|
203
|
+
return AudioPipelineState::STOPPED;
|
204
|
+
}
|
205
|
+
|
206
|
+
if ((event_bits & EventGroupBits::READER_MESSAGE_ERROR)) {
|
207
|
+
xEventGroupClearBits(this->event_group_, EventGroupBits::READER_MESSAGE_ERROR);
|
208
|
+
return AudioPipelineState::ERROR_READING;
|
209
|
+
}
|
210
|
+
|
211
|
+
if ((event_bits & EventGroupBits::DECODER_MESSAGE_ERROR)) {
|
212
|
+
xEventGroupClearBits(this->event_group_, EventGroupBits::DECODER_MESSAGE_ERROR);
|
213
|
+
return AudioPipelineState::ERROR_DECODING;
|
214
|
+
}
|
215
|
+
|
216
|
+
if (this->pause_state_) {
|
217
|
+
return AudioPipelineState::PAUSED;
|
218
|
+
}
|
219
|
+
|
220
|
+
if ((this->read_task_handle_ == nullptr) && (this->decode_task_handle_ == nullptr)) {
|
221
|
+
// No tasks are running, so the pipeline is stopped.
|
222
|
+
xEventGroupClearBits(this->event_group_, EventGroupBits::PIPELINE_COMMAND_STOP);
|
223
|
+
return AudioPipelineState::STOPPED;
|
224
|
+
}
|
225
|
+
|
226
|
+
this->is_playing_ = true;
|
227
|
+
return AudioPipelineState::PLAYING;
|
228
|
+
}
|
229
|
+
|
230
|
+
esp_err_t AudioPipeline::allocate_communications_() {
|
231
|
+
if (this->event_group_ == nullptr)
|
232
|
+
this->event_group_ = xEventGroupCreate();
|
233
|
+
|
234
|
+
if (this->event_group_ == nullptr) {
|
235
|
+
return ESP_ERR_NO_MEM;
|
236
|
+
}
|
237
|
+
|
238
|
+
if (this->info_error_queue_ == nullptr)
|
239
|
+
this->info_error_queue_ = xQueueCreate(INFO_ERROR_QUEUE_COUNT, sizeof(InfoErrorEvent));
|
240
|
+
|
241
|
+
if (this->info_error_queue_ == nullptr)
|
242
|
+
return ESP_ERR_NO_MEM;
|
243
|
+
|
244
|
+
return ESP_OK;
|
245
|
+
}
|
246
|
+
|
247
|
+
esp_err_t AudioPipeline::start_tasks_() {
|
248
|
+
if (this->read_task_handle_ == nullptr) {
|
249
|
+
if (this->read_task_stack_buffer_ == nullptr) {
|
250
|
+
if (this->task_stack_in_psram_) {
|
251
|
+
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_EXTERNAL);
|
252
|
+
this->read_task_stack_buffer_ = stack_allocator.allocate(READ_TASK_STACK_SIZE);
|
253
|
+
} else {
|
254
|
+
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_INTERNAL);
|
255
|
+
this->read_task_stack_buffer_ = stack_allocator.allocate(READ_TASK_STACK_SIZE);
|
256
|
+
}
|
257
|
+
}
|
258
|
+
|
259
|
+
if (this->read_task_stack_buffer_ == nullptr) {
|
260
|
+
return ESP_ERR_NO_MEM;
|
261
|
+
}
|
262
|
+
|
263
|
+
if (this->read_task_handle_ == nullptr) {
|
264
|
+
this->read_task_handle_ =
|
265
|
+
xTaskCreateStatic(read_task, (this->base_name_ + "_read").c_str(), READ_TASK_STACK_SIZE, (void *) this,
|
266
|
+
this->priority_, this->read_task_stack_buffer_, &this->read_task_stack_);
|
267
|
+
}
|
268
|
+
|
269
|
+
if (this->read_task_handle_ == nullptr) {
|
270
|
+
return ESP_ERR_INVALID_STATE;
|
271
|
+
}
|
272
|
+
}
|
273
|
+
|
274
|
+
if (this->decode_task_handle_ == nullptr) {
|
275
|
+
if (this->decode_task_stack_buffer_ == nullptr) {
|
276
|
+
if (this->task_stack_in_psram_) {
|
277
|
+
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_EXTERNAL);
|
278
|
+
this->decode_task_stack_buffer_ = stack_allocator.allocate(DECODE_TASK_STACK_SIZE);
|
279
|
+
} else {
|
280
|
+
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_INTERNAL);
|
281
|
+
this->decode_task_stack_buffer_ = stack_allocator.allocate(DECODE_TASK_STACK_SIZE);
|
282
|
+
}
|
283
|
+
}
|
284
|
+
|
285
|
+
if (this->decode_task_stack_buffer_ == nullptr) {
|
286
|
+
return ESP_ERR_NO_MEM;
|
287
|
+
}
|
288
|
+
|
289
|
+
if (this->decode_task_handle_ == nullptr) {
|
290
|
+
this->decode_task_handle_ =
|
291
|
+
xTaskCreateStatic(decode_task, (this->base_name_ + "_decode").c_str(), DECODE_TASK_STACK_SIZE, (void *) this,
|
292
|
+
this->priority_, this->decode_task_stack_buffer_, &this->decode_task_stack_);
|
293
|
+
}
|
294
|
+
|
295
|
+
if (this->decode_task_handle_ == nullptr) {
|
296
|
+
return ESP_ERR_INVALID_STATE;
|
297
|
+
}
|
298
|
+
}
|
299
|
+
|
300
|
+
return ESP_OK;
|
301
|
+
}
|
302
|
+
|
303
|
+
void AudioPipeline::delete_tasks_() {
|
304
|
+
if (this->read_task_handle_ != nullptr) {
|
305
|
+
vTaskDelete(this->read_task_handle_);
|
306
|
+
|
307
|
+
if (this->read_task_stack_buffer_ != nullptr) {
|
308
|
+
if (this->task_stack_in_psram_) {
|
309
|
+
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_EXTERNAL);
|
310
|
+
stack_allocator.deallocate(this->read_task_stack_buffer_, READ_TASK_STACK_SIZE);
|
311
|
+
} else {
|
312
|
+
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_INTERNAL);
|
313
|
+
stack_allocator.deallocate(this->read_task_stack_buffer_, READ_TASK_STACK_SIZE);
|
314
|
+
}
|
315
|
+
|
316
|
+
this->read_task_stack_buffer_ = nullptr;
|
317
|
+
this->read_task_handle_ = nullptr;
|
318
|
+
}
|
319
|
+
}
|
320
|
+
|
321
|
+
if (this->decode_task_handle_ != nullptr) {
|
322
|
+
vTaskDelete(this->decode_task_handle_);
|
323
|
+
|
324
|
+
if (this->decode_task_stack_buffer_ != nullptr) {
|
325
|
+
if (this->task_stack_in_psram_) {
|
326
|
+
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_EXTERNAL);
|
327
|
+
stack_allocator.deallocate(this->decode_task_stack_buffer_, DECODE_TASK_STACK_SIZE);
|
328
|
+
} else {
|
329
|
+
RAMAllocator<StackType_t> stack_allocator(RAMAllocator<StackType_t>::ALLOC_INTERNAL);
|
330
|
+
stack_allocator.deallocate(this->decode_task_stack_buffer_, DECODE_TASK_STACK_SIZE);
|
331
|
+
}
|
332
|
+
|
333
|
+
this->decode_task_stack_buffer_ = nullptr;
|
334
|
+
this->decode_task_handle_ = nullptr;
|
335
|
+
}
|
336
|
+
}
|
337
|
+
}
|
338
|
+
|
339
|
+
void AudioPipeline::read_task(void *params) {
|
340
|
+
AudioPipeline *this_pipeline = (AudioPipeline *) params;
|
341
|
+
|
342
|
+
while (true) {
|
343
|
+
xEventGroupSetBits(this_pipeline->event_group_, EventGroupBits::READER_MESSAGE_FINISHED);
|
344
|
+
|
345
|
+
// Wait until the pipeline notifies us the source of the media file
|
346
|
+
EventBits_t event_bits =
|
347
|
+
xEventGroupWaitBits(this_pipeline->event_group_,
|
348
|
+
EventGroupBits::READER_COMMAND_INIT_FILE | EventGroupBits::READER_COMMAND_INIT_HTTP |
|
349
|
+
EventGroupBits::PIPELINE_COMMAND_STOP, // Bit message to read
|
350
|
+
pdFALSE, // Clear the bit on exit
|
351
|
+
pdFALSE, // Wait for all the bits,
|
352
|
+
portMAX_DELAY); // Block indefinitely until bit is set
|
353
|
+
|
354
|
+
if (!(event_bits & EventGroupBits::PIPELINE_COMMAND_STOP)) {
|
355
|
+
xEventGroupClearBits(this_pipeline->event_group_, EventGroupBits::READER_MESSAGE_FINISHED |
|
356
|
+
EventGroupBits::READER_COMMAND_INIT_FILE |
|
357
|
+
EventGroupBits::READER_COMMAND_INIT_HTTP);
|
358
|
+
InfoErrorEvent event;
|
359
|
+
event.source = InfoErrorSource::READER;
|
360
|
+
esp_err_t err = ESP_OK;
|
361
|
+
|
362
|
+
std::unique_ptr<audio::AudioReader> reader =
|
363
|
+
make_unique<audio::AudioReader>(this_pipeline->transfer_buffer_size_);
|
364
|
+
|
365
|
+
if (event_bits & EventGroupBits::READER_COMMAND_INIT_FILE) {
|
366
|
+
err = reader->start(this_pipeline->current_audio_file_, this_pipeline->current_audio_file_type_);
|
367
|
+
} else {
|
368
|
+
err = reader->start(this_pipeline->current_uri_, this_pipeline->current_audio_file_type_);
|
369
|
+
}
|
370
|
+
|
371
|
+
if (err == ESP_OK) {
|
372
|
+
size_t file_ring_buffer_size = this_pipeline->buffer_size_;
|
373
|
+
|
374
|
+
std::shared_ptr<RingBuffer> temp_ring_buffer;
|
375
|
+
|
376
|
+
if (!this_pipeline->raw_file_ring_buffer_.use_count()) {
|
377
|
+
temp_ring_buffer = RingBuffer::create(file_ring_buffer_size);
|
378
|
+
this_pipeline->raw_file_ring_buffer_ = temp_ring_buffer;
|
379
|
+
}
|
380
|
+
|
381
|
+
if (!this_pipeline->raw_file_ring_buffer_.use_count()) {
|
382
|
+
err = ESP_ERR_NO_MEM;
|
383
|
+
} else {
|
384
|
+
reader->add_sink(this_pipeline->raw_file_ring_buffer_);
|
385
|
+
}
|
386
|
+
}
|
387
|
+
|
388
|
+
if (err != ESP_OK) {
|
389
|
+
// Send specific error message
|
390
|
+
event.err = err;
|
391
|
+
xQueueSend(this_pipeline->info_error_queue_, &event, portMAX_DELAY);
|
392
|
+
|
393
|
+
// Setting up the reader failed, stop the pipeline
|
394
|
+
xEventGroupSetBits(this_pipeline->event_group_,
|
395
|
+
EventGroupBits::READER_MESSAGE_ERROR | EventGroupBits::PIPELINE_COMMAND_STOP);
|
396
|
+
} else {
|
397
|
+
// Send the file type to the pipeline
|
398
|
+
event.file_type = this_pipeline->current_audio_file_type_;
|
399
|
+
xQueueSend(this_pipeline->info_error_queue_, &event, portMAX_DELAY);
|
400
|
+
xEventGroupSetBits(this_pipeline->event_group_, EventGroupBits::READER_MESSAGE_LOADED_MEDIA_TYPE);
|
401
|
+
}
|
402
|
+
|
403
|
+
while (true) {
|
404
|
+
event_bits = xEventGroupGetBits(this_pipeline->event_group_);
|
405
|
+
|
406
|
+
if (event_bits & EventGroupBits::PIPELINE_COMMAND_STOP) {
|
407
|
+
break;
|
408
|
+
}
|
409
|
+
|
410
|
+
audio::AudioReaderState reader_state = reader->read();
|
411
|
+
|
412
|
+
if (reader_state == audio::AudioReaderState::FINISHED) {
|
413
|
+
break;
|
414
|
+
} else if (reader_state == audio::AudioReaderState::FAILED) {
|
415
|
+
xEventGroupSetBits(this_pipeline->event_group_,
|
416
|
+
EventGroupBits::READER_MESSAGE_ERROR | EventGroupBits::PIPELINE_COMMAND_STOP);
|
417
|
+
break;
|
418
|
+
}
|
419
|
+
}
|
420
|
+
event_bits = xEventGroupGetBits(this_pipeline->event_group_);
|
421
|
+
if ((event_bits & EventGroupBits::READER_MESSAGE_LOADED_MEDIA_TYPE) ||
|
422
|
+
(this_pipeline->raw_file_ring_buffer_.use_count() == 1)) {
|
423
|
+
// Decoder task hasn't started yet, so delay a bit before releasing ownership of the ring buffer
|
424
|
+
delay(10);
|
425
|
+
}
|
426
|
+
}
|
427
|
+
}
|
428
|
+
}
|
429
|
+
|
430
|
+
void AudioPipeline::decode_task(void *params) {
|
431
|
+
AudioPipeline *this_pipeline = (AudioPipeline *) params;
|
432
|
+
|
433
|
+
while (true) {
|
434
|
+
xEventGroupSetBits(this_pipeline->event_group_, EventGroupBits::DECODER_MESSAGE_FINISHED);
|
435
|
+
|
436
|
+
// Wait until the reader notifies us that the media type is available
|
437
|
+
EventBits_t event_bits = xEventGroupWaitBits(this_pipeline->event_group_,
|
438
|
+
EventGroupBits::READER_MESSAGE_LOADED_MEDIA_TYPE |
|
439
|
+
EventGroupBits::PIPELINE_COMMAND_STOP, // Bit message to read
|
440
|
+
pdFALSE, // Clear the bit on exit
|
441
|
+
pdFALSE, // Wait for all the bits,
|
442
|
+
portMAX_DELAY); // Block indefinitely until bit is set
|
443
|
+
|
444
|
+
if (!(event_bits & EventGroupBits::PIPELINE_COMMAND_STOP)) {
|
445
|
+
xEventGroupClearBits(this_pipeline->event_group_,
|
446
|
+
EventGroupBits::DECODER_MESSAGE_FINISHED | EventGroupBits::READER_MESSAGE_LOADED_MEDIA_TYPE);
|
447
|
+
InfoErrorEvent event;
|
448
|
+
event.source = InfoErrorSource::DECODER;
|
449
|
+
|
450
|
+
std::unique_ptr<audio::AudioDecoder> decoder =
|
451
|
+
make_unique<audio::AudioDecoder>(this_pipeline->transfer_buffer_size_, this_pipeline->transfer_buffer_size_);
|
452
|
+
|
453
|
+
esp_err_t err = decoder->start(this_pipeline->current_audio_file_type_);
|
454
|
+
decoder->add_source(this_pipeline->raw_file_ring_buffer_);
|
455
|
+
|
456
|
+
if (err != ESP_OK) {
|
457
|
+
// Send specific error message
|
458
|
+
event.err = err;
|
459
|
+
xQueueSend(this_pipeline->info_error_queue_, &event, portMAX_DELAY);
|
460
|
+
|
461
|
+
// Setting up the decoder failed, stop the pipeline
|
462
|
+
xEventGroupSetBits(this_pipeline->event_group_,
|
463
|
+
EventGroupBits::DECODER_MESSAGE_ERROR | EventGroupBits::PIPELINE_COMMAND_STOP);
|
464
|
+
}
|
465
|
+
|
466
|
+
bool has_stream_info = false;
|
467
|
+
bool started_playback = false;
|
468
|
+
|
469
|
+
size_t initial_bytes_to_buffer = 0;
|
470
|
+
|
471
|
+
while (true) {
|
472
|
+
event_bits = xEventGroupGetBits(this_pipeline->event_group_);
|
473
|
+
|
474
|
+
if (event_bits & EventGroupBits::PIPELINE_COMMAND_STOP) {
|
475
|
+
break;
|
476
|
+
}
|
477
|
+
|
478
|
+
// Update pause state
|
479
|
+
if (!started_playback) {
|
480
|
+
if (!(event_bits & EventGroupBits::READER_MESSAGE_FINISHED)) {
|
481
|
+
decoder->set_pause_output_state(true);
|
482
|
+
} else {
|
483
|
+
started_playback = true;
|
484
|
+
}
|
485
|
+
} else {
|
486
|
+
decoder->set_pause_output_state(this_pipeline->pause_state_);
|
487
|
+
}
|
488
|
+
|
489
|
+
// Stop gracefully if the reader has finished
|
490
|
+
audio::AudioDecoderState decoder_state = decoder->decode(event_bits & EventGroupBits::READER_MESSAGE_FINISHED);
|
491
|
+
|
492
|
+
if ((decoder_state == audio::AudioDecoderState::DECODING) ||
|
493
|
+
(decoder_state == audio::AudioDecoderState::FINISHED)) {
|
494
|
+
this_pipeline->playback_ms_ = decoder->get_playback_ms();
|
495
|
+
}
|
496
|
+
|
497
|
+
if (decoder_state == audio::AudioDecoderState::FINISHED) {
|
498
|
+
break;
|
499
|
+
} else if (decoder_state == audio::AudioDecoderState::FAILED) {
|
500
|
+
if (!has_stream_info) {
|
501
|
+
event.decoding_err = DecodingError::FAILED_HEADER;
|
502
|
+
xQueueSend(this_pipeline->info_error_queue_, &event, portMAX_DELAY);
|
503
|
+
}
|
504
|
+
xEventGroupSetBits(this_pipeline->event_group_,
|
505
|
+
EventGroupBits::DECODER_MESSAGE_ERROR | EventGroupBits::PIPELINE_COMMAND_STOP);
|
506
|
+
break;
|
507
|
+
}
|
508
|
+
|
509
|
+
if (!has_stream_info && decoder->get_audio_stream_info().has_value()) {
|
510
|
+
has_stream_info = true;
|
511
|
+
|
512
|
+
this_pipeline->current_audio_stream_info_ = decoder->get_audio_stream_info().value();
|
513
|
+
|
514
|
+
// Send the stream information to the pipeline
|
515
|
+
event.audio_stream_info = this_pipeline->current_audio_stream_info_;
|
516
|
+
|
517
|
+
if (this_pipeline->current_audio_stream_info_.get_bits_per_sample() != 16) {
|
518
|
+
// Error state, incompatible bits per sample
|
519
|
+
event.decoding_err = DecodingError::INCOMPATIBLE_BITS_PER_SAMPLE;
|
520
|
+
xEventGroupSetBits(this_pipeline->event_group_,
|
521
|
+
EventGroupBits::DECODER_MESSAGE_ERROR | EventGroupBits::PIPELINE_COMMAND_STOP);
|
522
|
+
} else if ((this_pipeline->current_audio_stream_info_.get_channels() > 2)) {
|
523
|
+
// Error state, incompatible number of channels
|
524
|
+
event.decoding_err = DecodingError::INCOMPATIBLE_CHANNELS;
|
525
|
+
xEventGroupSetBits(this_pipeline->event_group_,
|
526
|
+
EventGroupBits::DECODER_MESSAGE_ERROR | EventGroupBits::PIPELINE_COMMAND_STOP);
|
527
|
+
} else {
|
528
|
+
// Send audio directly to the speaker
|
529
|
+
this_pipeline->speaker_->set_audio_stream_info(this_pipeline->current_audio_stream_info_);
|
530
|
+
decoder->add_sink(this_pipeline->speaker_);
|
531
|
+
}
|
532
|
+
|
533
|
+
initial_bytes_to_buffer = std::min(this_pipeline->current_audio_stream_info_.ms_to_bytes(INITIAL_BUFFER_MS),
|
534
|
+
this_pipeline->buffer_size_ * 3 / 4);
|
535
|
+
|
536
|
+
switch (this_pipeline->current_audio_file_type_) {
|
537
|
+
#ifdef USE_AUDIO_MP3_SUPPORT
|
538
|
+
case audio::AudioFileType::MP3:
|
539
|
+
initial_bytes_to_buffer /= 8; // Estimate the MP3 compression factor is 8
|
540
|
+
break;
|
541
|
+
#endif
|
542
|
+
#ifdef USE_AUDIO_FLAC_SUPPORT
|
543
|
+
case audio::AudioFileType::FLAC:
|
544
|
+
initial_bytes_to_buffer /= 2; // Estimate the FLAC compression factor is 2
|
545
|
+
break;
|
546
|
+
#endif
|
547
|
+
default:
|
548
|
+
break;
|
549
|
+
}
|
550
|
+
xQueueSend(this_pipeline->info_error_queue_, &event, portMAX_DELAY);
|
551
|
+
}
|
552
|
+
|
553
|
+
if (!started_playback && has_stream_info) {
|
554
|
+
// Verify enough data is available before starting playback
|
555
|
+
std::shared_ptr<RingBuffer> temp_ring_buffer = this_pipeline->raw_file_ring_buffer_.lock();
|
556
|
+
if (temp_ring_buffer->available() >= initial_bytes_to_buffer) {
|
557
|
+
started_playback = true;
|
558
|
+
}
|
559
|
+
}
|
560
|
+
}
|
561
|
+
}
|
562
|
+
}
|
563
|
+
}
|
564
|
+
|
565
|
+
} // namespace speaker
|
566
|
+
} // namespace esphome
|
567
|
+
|
568
|
+
#endif
|