esphome 2024.12.4__py3-none-any.whl → 2025.2.0b2__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/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 +2 -1
- esphome/components/display/__init__.py +18 -5
- esphome/components/display/display.cpp +2 -1
- 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_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/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 +43 -7
- esphome/components/logger/logger.cpp +16 -11
- 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 +28 -2
- esphome/components/online_image/image_decoder.h +24 -15
- esphome/components/online_image/jpeg_image.cpp +90 -0
- esphome/components/online_image/jpeg_image.h +34 -0
- esphome/components/online_image/online_image.cpp +112 -53
- esphome/components/online_image/online_image.h +24 -7
- 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/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/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.0b2.dist-info}/METADATA +12 -7
- {esphome-2024.12.4.dist-info → esphome-2025.2.0b2.dist-info}/RECORD +341 -292
- 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.0b2.dist-info}/LICENSE +0 -0
- {esphome-2024.12.4.dist-info → esphome-2025.2.0b2.dist-info}/WHEEL +0 -0
- {esphome-2024.12.4.dist-info → esphome-2025.2.0b2.dist-info}/entry_points.txt +0 -0
- {esphome-2024.12.4.dist-info → esphome-2025.2.0b2.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
|