summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorailurux <ailuruxx@gmail.com>2024-05-10 13:06:20 +1000
committerailurux <ailuruxx@gmail.com>2024-05-10 13:06:20 +1000
commit3f177cdb8880abf199f4445f1398cd69fb813892 (patch)
treee20de4949b1344c826e5af41ab701f3db75b21bc /src
parent8019c7691889cde4c3d40bbd78d485a92d713bbf (diff)
parente4ce7c4ac23402e09be8d6a52e0f739c0dff4ff0 (diff)
downloadtangara-fw-3f177cdb8880abf199f4445f1398cd69fb813892.tar.gz
Merge branch 'main' into file-browser
Diffstat (limited to 'src')
-rw-r--r--src/app_console/CMakeLists.txt9
-rw-r--r--src/audio/CMakeLists.txt14
-rw-r--r--src/audio/audio_decoder.cpp148
-rw-r--r--src/audio/fatfs_audio_input.cpp161
-rw-r--r--src/audio/include/audio_decoder.hpp56
-rw-r--r--src/audio/include/fatfs_audio_input.hpp66
-rw-r--r--src/battery/CMakeLists.txt10
-rw-r--r--src/codecs/CMakeLists.txt2
-rw-r--r--src/codecs/codec.cpp2
-rw-r--r--src/codecs/dr_flac.cpp2
-rw-r--r--src/codecs/include/codec.hpp13
-rw-r--r--src/codecs/include/dr_flac.hpp8
-rw-r--r--src/codecs/include/mad.hpp4
-rw-r--r--src/codecs/include/opus.hpp6
-rw-r--r--src/codecs/include/source_buffer.hpp9
-rw-r--r--src/codecs/include/vorbis.hpp6
-rw-r--r--src/codecs/include/wav.hpp2
-rw-r--r--src/codecs/mad.cpp10
-rw-r--r--src/codecs/opus.cpp2
-rw-r--r--src/codecs/source_buffer.cpp10
-rw-r--r--src/codecs/test/test_mad.cpp6
-rw-r--r--src/codecs/vorbis.cpp2
-rw-r--r--src/codecs/wav.cpp59
-rw-r--r--src/database/CMakeLists.txt22
-rw-r--r--src/dev_console/CMakeLists.txt9
-rw-r--r--src/drivers/CMakeLists.txt4
-rw-r--r--src/drivers/adc.cpp2
-rw-r--r--src/drivers/bluetooth.cpp19
-rw-r--r--src/drivers/display.cpp8
-rw-r--r--src/drivers/display_init.cpp2
-rw-r--r--src/drivers/gpios.cpp4
-rw-r--r--src/drivers/haptics.cpp12
-rw-r--r--src/drivers/i2c.cpp2
-rw-r--r--src/drivers/i2s_dac.cpp27
-rw-r--r--src/drivers/include/drivers/a2dp_audio_output.hpp (renamed from src/drivers/include/a2dp_audio_output.hpp)0
-rw-r--r--src/drivers/include/drivers/adc.hpp (renamed from src/drivers/include/adc.hpp)0
-rw-r--r--src/drivers/include/drivers/bluetooth.hpp (renamed from src/drivers/include/bluetooth.hpp)5
-rw-r--r--src/drivers/include/drivers/bluetooth_types.hpp (renamed from src/drivers/include/bluetooth_types.hpp)0
-rw-r--r--src/drivers/include/drivers/display.hpp (renamed from src/drivers/include/display.hpp)4
-rw-r--r--src/drivers/include/drivers/display_init.hpp (renamed from src/drivers/include/display_init.hpp)0
-rw-r--r--src/drivers/include/drivers/fatfs_audio_input.hpp (renamed from src/drivers/include/fatfs_audio_input.hpp)0
-rw-r--r--src/drivers/include/drivers/gpios.hpp (renamed from src/drivers/include/gpios.hpp)0
-rw-r--r--src/drivers/include/drivers/haptics.hpp (renamed from src/drivers/include/haptics.hpp)0
-rw-r--r--src/drivers/include/drivers/i2c.hpp (renamed from src/drivers/include/i2c.hpp)0
-rw-r--r--src/drivers/include/drivers/i2s_dac.hpp (renamed from src/drivers/include/i2s_dac.hpp)8
-rw-r--r--src/drivers/include/drivers/nvs.hpp (renamed from src/drivers/include/nvs.hpp)3
-rw-r--r--src/drivers/include/drivers/samd.hpp (renamed from src/drivers/include/samd.hpp)0
-rw-r--r--src/drivers/include/drivers/spi.hpp (renamed from src/drivers/include/spi.hpp)0
-rw-r--r--src/drivers/include/drivers/spiffs.hpp (renamed from src/drivers/include/spiffs.hpp)0
-rw-r--r--src/drivers/include/drivers/storage.hpp (renamed from src/drivers/include/storage.hpp)2
-rw-r--r--src/drivers/include/drivers/touchwheel.hpp (renamed from src/drivers/include/touchwheel.hpp)2
-rw-r--r--src/drivers/include/drivers/wm8523.hpp (renamed from src/drivers/include/wm8523.hpp)0
-rw-r--r--src/drivers/nvs.cpp25
-rw-r--r--src/drivers/samd.cpp4
-rw-r--r--src/drivers/spi.cpp2
-rw-r--r--src/drivers/spiffs.cpp2
-rw-r--r--src/drivers/storage.cpp4
-rw-r--r--src/drivers/test/test_dac.cpp4
-rw-r--r--src/drivers/test/test_gpio_expander.cpp4
-rw-r--r--src/drivers/test/test_storage.cpp8
-rw-r--r--src/drivers/touchwheel.cpp4
-rw-r--r--src/drivers/wm8523.cpp4
-rw-r--r--src/events/CMakeLists.txt9
-rw-r--r--src/graphics/CMakeLists.txt7
-rw-r--r--src/graphics/font_fusion_10.c (renamed from src/ui/font_fusion_10.c)0
-rw-r--r--src/graphics/font_fusion_12.c (renamed from src/ui/font_fusion_12.c)0
-rw-r--r--src/graphics/splash.c (renamed from src/ui/splash.c)0
-rw-r--r--src/input/CMakeLists.txt13
-rw-r--r--src/input/include/lvgl_input_driver.hpp60
-rw-r--r--src/input/lvgl_input_driver.cpp127
-rw-r--r--src/locale/CMakeLists.txt2
-rw-r--r--src/locale/include/collation.hpp2
-rw-r--r--src/lua/CMakeLists.txt13
-rw-r--r--src/main/CMakeLists.txt5
-rw-r--r--src/main/main.cpp12
-rw-r--r--src/memory/include/himem.hpp6
-rw-r--r--src/system_fsm/CMakeLists.txt11
-rw-r--r--src/tangara/CMakeLists.txt22
-rw-r--r--src/tangara/app_console/app_console.cpp (renamed from src/app_console/app_console.cpp)36
-rw-r--r--src/tangara/app_console/app_console.hpp (renamed from src/app_console/include/app_console.hpp)12
-rw-r--r--src/tangara/audio/README.md (renamed from src/audio/README.md)0
-rw-r--r--src/tangara/audio/audio_decoder.cpp199
-rw-r--r--src/tangara/audio/audio_decoder.hpp60
-rw-r--r--src/tangara/audio/audio_events.hpp (renamed from src/audio/include/audio_events.hpp)37
-rw-r--r--src/tangara/audio/audio_fsm.cpp (renamed from src/audio/audio_fsm.cpp)346
-rw-r--r--src/tangara/audio/audio_fsm.hpp (renamed from src/audio/include/audio_fsm.hpp)64
-rw-r--r--src/tangara/audio/audio_sink.hpp (renamed from src/audio/include/audio_sink.hpp)1
-rw-r--r--src/tangara/audio/audio_source.cpp (renamed from src/audio/audio_source.cpp)10
-rw-r--r--src/tangara/audio/audio_source.hpp (renamed from src/audio/include/audio_source.hpp)7
-rw-r--r--src/tangara/audio/bt_audio_output.cpp (renamed from src/audio/bt_audio_output.cpp)14
-rw-r--r--src/tangara/audio/bt_audio_output.hpp (renamed from src/audio/include/bt_audio_output.hpp)10
-rw-r--r--src/tangara/audio/fatfs_source.cpp (renamed from src/audio/fatfs_source.cpp)12
-rw-r--r--src/tangara/audio/fatfs_source.hpp (renamed from src/audio/include/fatfs_source.hpp)4
-rw-r--r--src/tangara/audio/fatfs_stream_factory.cpp104
-rw-r--r--src/tangara/audio/fatfs_stream_factory.hpp53
-rw-r--r--src/tangara/audio/i2s_audio_output.cpp (renamed from src/audio/i2s_audio_output.cpp)21
-rw-r--r--src/tangara/audio/i2s_audio_output.hpp (renamed from src/audio/include/i2s_audio_output.hpp)8
-rw-r--r--src/tangara/audio/processor.cpp (renamed from src/audio/audio_converter.cpp)132
-rw-r--r--src/tangara/audio/processor.hpp (renamed from src/audio/include/audio_converter.hpp)37
-rw-r--r--src/tangara/audio/readahead_source.cpp (renamed from src/audio/readahead_source.cpp)8
-rw-r--r--src/tangara/audio/readahead_source.hpp (renamed from src/audio/include/readahead_source.hpp)4
-rw-r--r--src/tangara/audio/resample.cpp (renamed from src/audio/resample.cpp)6
-rw-r--r--src/tangara/audio/resample.hpp (renamed from src/audio/include/resample.hpp)8
-rw-r--r--src/tangara/audio/stream_cues.cpp65
-rw-r--r--src/tangara/audio/stream_cues.hpp49
-rw-r--r--src/tangara/audio/test/CMakeLists.txt (renamed from src/audio/test/CMakeLists.txt)0
-rw-r--r--src/tangara/audio/track_queue.cpp (renamed from src/audio/track_queue.cpp)14
-rw-r--r--src/tangara/audio/track_queue.hpp (renamed from src/audio/include/track_queue.hpp)6
-rw-r--r--src/tangara/battery/battery.cpp (renamed from src/battery/battery.cpp)10
-rw-r--r--src/tangara/battery/battery.hpp (renamed from src/battery/include/battery.hpp)4
-rw-r--r--src/tangara/database/database.cpp (renamed from src/database/database.cpp)24
-rw-r--r--src/tangara/database/database.hpp (renamed from src/database/include/database.hpp)10
-rw-r--r--src/tangara/database/db_events.hpp (renamed from src/database/include/db_events.hpp)0
-rw-r--r--src/tangara/database/env_esp.cpp (renamed from src/database/env_esp.cpp)6
-rw-r--r--src/tangara/database/env_esp.hpp (renamed from src/database/include/env_esp.hpp)0
-rw-r--r--src/tangara/database/file_gatherer.cpp (renamed from src/database/file_gatherer.cpp)4
-rw-r--r--src/tangara/database/file_gatherer.hpp (renamed from src/database/include/file_gatherer.hpp)12
-rw-r--r--src/tangara/database/future_fetcher.hpp (renamed from src/database/include/future_fetcher.hpp)2
-rw-r--r--src/tangara/database/index.cpp (renamed from src/database/index.cpp)21
-rw-r--r--src/tangara/database/index.hpp (renamed from src/database/include/index.hpp)7
-rw-r--r--src/tangara/database/records.cpp (renamed from src/database/records.cpp)8
-rw-r--r--src/tangara/database/records.hpp (renamed from src/database/include/records.hpp)6
-rw-r--r--src/tangara/database/tag_parser.cpp (renamed from src/database/tag_parser.cpp)4
-rw-r--r--src/tangara/database/tag_parser.hpp (renamed from src/database/include/tag_parser.hpp)2
-rw-r--r--src/tangara/database/test/CMakeLists.txt (renamed from src/database/test/CMakeLists.txt)0
-rw-r--r--src/tangara/database/test/test_database.cpp (renamed from src/database/test/test_database.cpp)16
-rw-r--r--src/tangara/database/test/test_records.cpp (renamed from src/database/test/test_records.cpp)2
-rw-r--r--src/tangara/database/track.cpp (renamed from src/database/track.cpp)10
-rw-r--r--src/tangara/database/track.hpp (renamed from src/database/include/track.hpp)9
-rw-r--r--src/tangara/dev_console/console.cpp (renamed from src/dev_console/console.cpp)2
-rw-r--r--src/tangara/dev_console/console.hpp (renamed from src/dev_console/include/console.hpp)0
-rw-r--r--src/tangara/events/event_queue.cpp (renamed from src/events/event_queue.cpp)9
-rw-r--r--src/tangara/events/event_queue.hpp (renamed from src/events/include/event_queue.hpp)6
-rw-r--r--src/tangara/input/device_factory.cpp (renamed from src/input/device_factory.cpp)14
-rw-r--r--src/tangara/input/device_factory.hpp (renamed from src/input/include/device_factory.hpp)10
-rw-r--r--src/tangara/input/feedback_device.hpp (renamed from src/input/include/feedback_device.hpp)0
-rw-r--r--src/tangara/input/feedback_haptics.cpp (renamed from src/input/feedback_haptics.cpp)4
-rw-r--r--src/tangara/input/feedback_haptics.hpp (renamed from src/input/include/feedback_haptics.hpp)4
-rw-r--r--src/tangara/input/input_device.hpp (renamed from src/input/include/input_device.hpp)8
-rw-r--r--src/tangara/input/input_hook.cpp (renamed from src/input/input_hook.cpp)36
-rw-r--r--src/tangara/input/input_hook.hpp (renamed from src/input/include/input_hook.hpp)18
-rw-r--r--src/tangara/input/input_hook_actions.cpp (renamed from src/input/input_hook_actions.cpp)6
-rw-r--r--src/tangara/input/input_hook_actions.hpp (renamed from src/input/include/input_hook_actions.hpp)2
-rw-r--r--src/tangara/input/input_nav_buttons.cpp (renamed from src/input/input_nav_buttons.cpp)15
-rw-r--r--src/tangara/input/input_nav_buttons.hpp (renamed from src/input/include/input_nav_buttons.hpp)14
-rw-r--r--src/tangara/input/input_touch_dpad.cpp (renamed from src/input/input_touch_dpad.cpp)28
-rw-r--r--src/tangara/input/input_touch_dpad.hpp (renamed from src/input/include/input_touch_dpad.hpp)12
-rw-r--r--src/tangara/input/input_touch_wheel.cpp (renamed from src/input/input_touch_wheel.cpp)68
-rw-r--r--src/tangara/input/input_touch_wheel.hpp (renamed from src/input/include/input_touch_wheel.hpp)16
-rw-r--r--src/tangara/input/input_trigger.cpp (renamed from src/input/input_trigger.cpp)55
-rw-r--r--src/tangara/input/input_trigger.hpp (renamed from src/input/include/input_trigger.hpp)8
-rw-r--r--src/tangara/input/input_volume_buttons.cpp (renamed from src/input/input_volume_buttons.cpp)11
-rw-r--r--src/tangara/input/input_volume_buttons.hpp (renamed from src/input/include/input_volume_buttons.hpp)12
-rw-r--r--src/tangara/input/lvgl_input_driver.cpp258
-rw-r--r--src/tangara/input/lvgl_input_driver.hpp112
-rw-r--r--src/tangara/lua/bridge.cpp (renamed from src/lua/bridge.cpp)35
-rw-r--r--src/tangara/lua/bridge.hpp (renamed from src/lua/include/bridge.hpp)9
-rw-r--r--src/tangara/lua/file_iterator.cpp (renamed from src/lua/file_iterator.cpp)0
-rw-r--r--src/tangara/lua/file_iterator.hpp (renamed from src/lua/include/file_iterator.hpp)0
-rw-r--r--src/tangara/lua/lua_controls.cpp (renamed from src/lua/lua_controls.cpp)6
-rw-r--r--src/tangara/lua/lua_controls.hpp (renamed from src/lua/include/lua_controls.hpp)0
-rw-r--r--src/tangara/lua/lua_database.cpp (renamed from src/lua/lua_database.cpp)36
-rw-r--r--src/tangara/lua/lua_database.hpp (renamed from src/lua/include/lua_database.hpp)2
-rw-r--r--src/tangara/lua/lua_filesystem.cpp (renamed from src/lua/lua_filesystem.cpp)0
-rw-r--r--src/tangara/lua/lua_filesystem.hpp (renamed from src/lua/include/lua_filesystem.hpp)0
-rw-r--r--src/tangara/lua/lua_queue.cpp (renamed from src/lua/lua_queue.cpp)20
-rw-r--r--src/tangara/lua/lua_queue.hpp (renamed from src/lua/include/lua_queue.hpp)0
-rw-r--r--src/tangara/lua/lua_registry.hpp (renamed from src/lua/include/lua_registry.hpp)6
-rw-r--r--src/tangara/lua/lua_screen.cpp (renamed from src/lua/lua_screen.cpp)20
-rw-r--r--src/tangara/lua/lua_screen.hpp (renamed from src/lua/include/lua_screen.hpp)0
-rw-r--r--src/tangara/lua/lua_theme.cpp (renamed from src/lua/lua_theme.cpp)25
-rw-r--r--src/tangara/lua/lua_theme.hpp (renamed from src/lua/include/lua_theme.hpp)0
-rw-r--r--src/tangara/lua/lua_thread.cpp (renamed from src/lua/lua_thread.cpp)16
-rw-r--r--src/tangara/lua/lua_thread.hpp (renamed from src/lua/include/lua_thread.hpp)2
-rw-r--r--src/tangara/lua/lua_version.cpp (renamed from src/lua/lua_version.cpp)6
-rw-r--r--src/tangara/lua/lua_version.hpp (renamed from src/lua/include/lua_version.hpp)0
-rw-r--r--src/tangara/lua/property.cpp (renamed from src/lua/property.cpp)12
-rw-r--r--src/tangara/lua/property.hpp (renamed from src/lua/include/property.hpp)6
-rw-r--r--src/tangara/lua/registry.cpp (renamed from src/lua/registry.cpp)10
-rw-r--r--src/tangara/system_fsm/booting.cpp (renamed from src/system_fsm/booting.cpp)42
-rw-r--r--src/tangara/system_fsm/idle.cpp (renamed from src/system_fsm/idle.cpp)20
-rw-r--r--src/tangara/system_fsm/running.cpp (renamed from src/system_fsm/running.cpp)84
-rw-r--r--src/tangara/system_fsm/service_locator.cpp (renamed from src/system_fsm/service_locator.cpp)8
-rw-r--r--src/tangara/system_fsm/service_locator.hpp (renamed from src/system_fsm/include/service_locator.hpp)22
-rw-r--r--src/tangara/system_fsm/system_events.hpp (renamed from src/system_fsm/include/system_events.hpp)18
-rw-r--r--src/tangara/system_fsm/system_fsm.cpp (renamed from src/system_fsm/system_fsm.cpp)16
-rw-r--r--src/tangara/system_fsm/system_fsm.hpp (renamed from src/system_fsm/include/system_fsm.hpp)39
-rw-r--r--src/tangara/ui/fonts.hpp (renamed from src/ui/include/fonts.hpp)0
-rw-r--r--src/tangara/ui/lvgl_task.cpp (renamed from src/ui/lvgl_task.cpp)16
-rw-r--r--src/tangara/ui/lvgl_task.hpp (renamed from src/ui/include/lvgl_task.hpp)10
-rw-r--r--src/tangara/ui/modal.cpp (renamed from src/ui/modal.cpp)14
-rw-r--r--src/tangara/ui/modal.hpp (renamed from src/ui/include/modal.hpp)2
-rw-r--r--src/tangara/ui/screen.cpp (renamed from src/ui/screen.cpp)2
-rw-r--r--src/tangara/ui/screen.hpp (renamed from src/ui/include/screen.hpp)0
-rw-r--r--src/tangara/ui/screen_lua.cpp (renamed from src/ui/screen_lua.cpp)8
-rw-r--r--src/tangara/ui/screen_lua.hpp (renamed from src/ui/include/screen_lua.hpp)4
-rw-r--r--src/tangara/ui/screen_splash.cpp (renamed from src/ui/screen_splash.cpp)2
-rw-r--r--src/tangara/ui/screen_splash.hpp (renamed from src/ui/include/screen_splash.hpp)2
-rw-r--r--src/tangara/ui/themes.cpp (renamed from src/ui/themes.cpp)25
-rw-r--r--src/tangara/ui/themes.hpp (renamed from src/ui/include/themes.hpp)3
-rw-r--r--src/tangara/ui/ui_events.hpp (renamed from src/ui/include/ui_events.hpp)10
-rw-r--r--src/tangara/ui/ui_fsm.cpp (renamed from src/ui/ui_fsm.cpp)133
-rw-r--r--src/tangara/ui/ui_fsm.hpp (renamed from src/ui/include/ui_fsm.hpp)54
-rw-r--r--src/tasks/CMakeLists.txt2
-rw-r--r--src/tasks/tasks.cpp10
-rw-r--r--src/tasks/tasks.hpp8
-rw-r--r--src/ui/CMakeLists.txt11
-rw-r--r--src/ui/font_symbols.c425
-rw-r--r--src/ui/icons/battery_20.c52
-rw-r--r--src/ui/icons/battery_40.c52
-rw-r--r--src/ui/icons/battery_60.c52
-rw-r--r--src/ui/icons/battery_80.c52
-rw-r--r--src/ui/icons/battery_empty.c52
-rw-r--r--src/ui/icons/battery_full.c52
-rw-r--r--src/ui/icons/bluetooth.c54
-rw-r--r--src/ui/icons/pause.c54
-rw-r--r--src/ui/icons/play.c54
-rw-r--r--src/ui/include/ui_tick.hpp11
-rw-r--r--src/util/CMakeLists.txt2
-rw-r--r--src/util/include/debug.hpp5
-rw-r--r--src/util/random.cpp4
221 files changed, 2180 insertions, 2821 deletions
diff --git a/src/app_console/CMakeLists.txt b/src/app_console/CMakeLists.txt
deleted file mode 100644
index 1a2ae8d6..00000000
--- a/src/app_console/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2023 jacqueline <me@jacqueline.id.au>
-#
-# SPDX-License-Identifier: GPL-3.0-only
-
-idf_component_register(
- SRCS "app_console.cpp"
- INCLUDE_DIRS "include"
- REQUIRES "dev_console" "events" "database")
-target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt
deleted file mode 100644
index 8ed5efbb..00000000
--- a/src/audio/CMakeLists.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright 2023 jacqueline <me@jacqueline.id.au>
-#
-# SPDX-License-Identifier: GPL-3.0-only
-
-idf_component_register(
- SRCS "audio_decoder.cpp" "fatfs_audio_input.cpp" "i2s_audio_output.cpp"
- "track_queue.cpp" "audio_fsm.cpp" "audio_converter.cpp" "resample.cpp"
- "fatfs_source.cpp" "bt_audio_output.cpp" "readahead_source.cpp"
- "audio_source.cpp"
- INCLUDE_DIRS "include"
- REQUIRES "codecs" "drivers" "cbor" "result" "tasks" "span" "memory" "tinyfsm"
- "database" "system_fsm" "speexdsp" "millershuffle" "libcppbor")
-
-target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/audio/audio_decoder.cpp b/src/audio/audio_decoder.cpp
deleted file mode 100644
index 90c69c16..00000000
--- a/src/audio/audio_decoder.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#include "audio_decoder.hpp"
-#include <stdint.h>
-
-#include <cstdint>
-#include <cstdlib>
-
-#include <algorithm>
-#include <cmath>
-#include <cstddef>
-#include <cstdint>
-#include <cstring>
-#include <deque>
-#include <memory>
-#include <variant>
-
-#include "cbor.h"
-#include "esp_err.h"
-#include "esp_heap_caps.h"
-#include "esp_log.h"
-#include "freertos/portmacro.h"
-#include "freertos/projdefs.h"
-#include "freertos/queue.h"
-#include "freertos/ringbuf.h"
-#include "i2s_dac.hpp"
-#include "span.hpp"
-
-#include "audio_converter.hpp"
-#include "audio_events.hpp"
-#include "audio_fsm.hpp"
-#include "audio_sink.hpp"
-#include "audio_source.hpp"
-#include "codec.hpp"
-#include "event_queue.hpp"
-#include "fatfs_audio_input.hpp"
-#include "sample.hpp"
-#include "tasks.hpp"
-#include "track.hpp"
-#include "types.hpp"
-#include "ui_fsm.hpp"
-
-namespace audio {
-
-[[maybe_unused]] static const char* kTag = "audio_dec";
-
-static constexpr std::size_t kCodecBufferLength =
- drivers::kI2SBufferLengthFrames * sizeof(sample::Sample);
-
-auto Decoder::Start(std::shared_ptr<IAudioSource> source,
- std::shared_ptr<SampleConverter> sink) -> Decoder* {
- Decoder* task = new Decoder(source, sink);
- tasks::StartPersistent<tasks::Type::kAudioDecoder>([=]() { task->Main(); });
- return task;
-}
-
-Decoder::Decoder(std::shared_ptr<IAudioSource> source,
- std::shared_ptr<SampleConverter> mixer)
- : source_(source), converter_(mixer), codec_(), current_format_() {
- ESP_LOGI(kTag, "allocating codec buffer, %u KiB", kCodecBufferLength / 1024);
- codec_buffer_ = {
- reinterpret_cast<sample::Sample*>(heap_caps_calloc(
- kCodecBufferLength, sizeof(sample::Sample), MALLOC_CAP_DMA)),
- kCodecBufferLength};
-}
-
-void Decoder::Main() {
- for (;;) {
- if (source_->HasNewStream() || !stream_) {
- std::shared_ptr<TaggedStream> new_stream = source_->NextStream();
- if (new_stream && BeginDecoding(new_stream)) {
- stream_ = new_stream;
- } else {
- continue;
- }
- }
-
- if (ContinueDecoding()) {
- stream_.reset();
- }
- }
-}
-
-auto Decoder::BeginDecoding(std::shared_ptr<TaggedStream> stream) -> bool {
- // Ensure any previous codec is freed before creating a new one.
- codec_.reset();
- codec_.reset(codecs::CreateCodecForType(stream->type()).value_or(nullptr));
- if (!codec_) {
- ESP_LOGE(kTag, "no codec found for stream");
- return false;
- }
-
- auto open_res = codec_->OpenStream(stream, stream->Offset());
- if (open_res.has_error()) {
- ESP_LOGE(kTag, "codec failed to start: %s",
- codecs::ICodec::ErrorString(open_res.error()).c_str());
- return false;
- }
- stream->SetPreambleFinished();
- current_sink_format_ = IAudioOutput::Format{
- .sample_rate = open_res->sample_rate_hz,
- .num_channels = open_res->num_channels,
- .bits_per_sample = 16,
- };
-
- std::optional<uint32_t> duration;
- if (open_res->total_samples) {
- duration = open_res->total_samples.value() / open_res->num_channels /
- open_res->sample_rate_hz;
- }
-
- converter_->beginStream(std::make_shared<TrackInfo>(TrackInfo{
- .tags = stream->tags(),
- .uri = stream->Filepath(),
- .duration = duration,
- .start_offset = stream->Offset(),
- .bitrate_kbps = open_res->sample_rate_hz,
- .encoding = stream->type(),
- .format = *current_sink_format_,
- }));
-
- return true;
-}
-
-auto Decoder::ContinueDecoding() -> bool {
- auto res = codec_->DecodeTo(codec_buffer_);
- if (res.has_error()) {
- converter_->endStream();
- return true;
- }
-
- if (res->samples_written > 0) {
- converter_->continueStream(codec_buffer_.first(res->samples_written));
- }
-
- if (res->is_stream_finished) {
- converter_->endStream();
- codec_.reset();
- }
-
- return res->is_stream_finished;
-}
-
-} // namespace audio
diff --git a/src/audio/fatfs_audio_input.cpp b/src/audio/fatfs_audio_input.cpp
deleted file mode 100644
index 29d32390..00000000
--- a/src/audio/fatfs_audio_input.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#include "fatfs_audio_input.hpp"
-
-#include <algorithm>
-#include <climits>
-#include <cstddef>
-#include <cstdint>
-#include <functional>
-#include <future>
-#include <memory>
-#include <mutex>
-#include <string>
-#include <variant>
-
-#include "esp_heap_caps.h"
-#include "esp_log.h"
-#include "ff.h"
-#include "freertos/portmacro.h"
-#include "freertos/projdefs.h"
-#include "readahead_source.hpp"
-#include "span.hpp"
-
-#include "audio_events.hpp"
-#include "audio_fsm.hpp"
-#include "audio_source.hpp"
-#include "codec.hpp"
-#include "event_queue.hpp"
-#include "fatfs_source.hpp"
-#include "future_fetcher.hpp"
-#include "spi.hpp"
-#include "tag_parser.hpp"
-#include "tasks.hpp"
-#include "track.hpp"
-#include "types.hpp"
-
-[[maybe_unused]] static const char* kTag = "SRC";
-
-namespace audio {
-
-FatfsAudioInput::FatfsAudioInput(database::ITagParser& tag_parser,
- tasks::WorkerPool& bg_worker)
- : IAudioSource(),
- tag_parser_(tag_parser),
- bg_worker_(bg_worker),
- new_stream_mutex_(),
- new_stream_(),
- has_new_stream_(false) {}
-
-FatfsAudioInput::~FatfsAudioInput() {}
-
-auto FatfsAudioInput::SetPath(std::optional<std::string> path) -> void {
- if (path) {
- SetPath(*path);
- } else {
- SetPath();
- }
-}
-
-auto FatfsAudioInput::SetPath(const std::string& path,uint32_t offset) -> void {
- std::lock_guard<std::mutex> guard{new_stream_mutex_};
- if (OpenFile(path, offset)) {
- has_new_stream_ = true;
- has_new_stream_.notify_one();
- }
-}
-
-auto FatfsAudioInput::SetPath() -> void {
- std::lock_guard<std::mutex> guard{new_stream_mutex_};
- new_stream_.reset();
- has_new_stream_ = true;
- has_new_stream_.notify_one();
-}
-
-auto FatfsAudioInput::HasNewStream() -> bool {
- return has_new_stream_;
-}
-
-auto FatfsAudioInput::NextStream() -> std::shared_ptr<TaggedStream> {
- while (true) {
- has_new_stream_.wait(false);
-
- {
- std::lock_guard<std::mutex> guard{new_stream_mutex_};
- if (!has_new_stream_.exchange(false)) {
- // If the new stream went away, then we need to go back to waiting.
- continue;
- }
-
- if (new_stream_ == nullptr) {
- continue;
- }
-
- auto stream = new_stream_;
- new_stream_ = nullptr;
- return stream;
- }
- }
-}
-
-auto FatfsAudioInput::OpenFile(const std::string& path,uint32_t offset) -> bool {
- ESP_LOGI(kTag, "opening file %s", path.c_str());
-
- auto tags = tag_parser_.ReadAndParseTags(path);
- if (!tags) {
- ESP_LOGE(kTag, "failed to read tags");
- return false;
- }
- if (!tags->title()) {
- tags->title(path);
- }
-
- auto stream_type = ContainerToStreamType(tags->encoding());
- if (!stream_type.has_value()) {
- ESP_LOGE(kTag, "couldn't match container to stream");
- return false;
- }
-
- std::unique_ptr<FIL> file = std::make_unique<FIL>();
- FRESULT res;
-
- {
- auto lock = drivers::acquire_spi();
- res = f_open(file.get(), path.c_str(), FA_READ);
- }
-
- if (res != FR_OK) {
- ESP_LOGE(kTag, "failed to open file! res: %i", res);
- return false;
- }
-
- auto source =
- std::make_unique<FatfsSource>(stream_type.value(), std::move(file));
- new_stream_.reset(new TaggedStream(tags, std::move(source), path, offset));
- return true;
-}
-
-auto FatfsAudioInput::ContainerToStreamType(database::Container enc)
- -> std::optional<codecs::StreamType> {
- switch (enc) {
- case database::Container::kMp3:
- return codecs::StreamType::kMp3;
- case database::Container::kWav:
- return codecs::StreamType::kWav;
- case database::Container::kOgg:
- return codecs::StreamType::kVorbis;
- case database::Container::kFlac:
- return codecs::StreamType::kFlac;
- case database::Container::kOpus:
- return codecs::StreamType::kOpus;
- case database::Container::kUnsupported:
- default:
- return {};
- }
-}
-
-} // namespace audio
diff --git a/src/audio/include/audio_decoder.hpp b/src/audio/include/audio_decoder.hpp
deleted file mode 100644
index 89f0f43c..00000000
--- a/src/audio/include/audio_decoder.hpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#pragma once
-
-#include <cstdint>
-#include <memory>
-
-#include "audio_converter.hpp"
-#include "audio_events.hpp"
-#include "audio_sink.hpp"
-#include "audio_source.hpp"
-#include "codec.hpp"
-#include "track.hpp"
-#include "types.hpp"
-
-namespace audio {
-
-/*
- * Handle to a persistent task that takes bytes from the given source, decodes
- * them into sample::Sample (normalised to 16 bit signed PCM), and then
- * forwards the resulting stream to the given converter.
- */
-class Decoder {
- public:
- static auto Start(std::shared_ptr<IAudioSource> source,
- std::shared_ptr<SampleConverter> converter) -> Decoder*;
-
- auto Main() -> void;
-
- Decoder(const Decoder&) = delete;
- Decoder& operator=(const Decoder&) = delete;
-
- private:
- Decoder(std::shared_ptr<IAudioSource> source,
- std::shared_ptr<SampleConverter> converter);
-
- auto BeginDecoding(std::shared_ptr<TaggedStream>) -> bool;
- auto ContinueDecoding() -> bool;
-
- std::shared_ptr<IAudioSource> source_;
- std::shared_ptr<SampleConverter> converter_;
-
- std::shared_ptr<codecs::IStream> stream_;
- std::unique_ptr<codecs::ICodec> codec_;
-
- std::optional<codecs::ICodec::OutputFormat> current_format_;
- std::optional<IAudioOutput::Format> current_sink_format_;
-
- cpp::span<sample::Sample> codec_buffer_;
-};
-
-} // namespace audio
diff --git a/src/audio/include/fatfs_audio_input.hpp b/src/audio/include/fatfs_audio_input.hpp
deleted file mode 100644
index 10b7433e..00000000
--- a/src/audio/include/fatfs_audio_input.hpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#pragma once
-
-#include <cstddef>
-#include <cstdint>
-#include <future>
-#include <memory>
-#include <string>
-
-#include "ff.h"
-#include "freertos/portmacro.h"
-
-#include "audio_source.hpp"
-#include "codec.hpp"
-#include "future_fetcher.hpp"
-#include "tag_parser.hpp"
-#include "tasks.hpp"
-#include "types.hpp"
-
-namespace audio {
-
-/*
- * Audio source that fetches data from a FatFs (or exfat i guess) filesystem.
- *
- * All public methods are safe to call from any task.
- */
-class FatfsAudioInput : public IAudioSource {
- public:
- explicit FatfsAudioInput(database::ITagParser&, tasks::WorkerPool&);
- ~FatfsAudioInput();
-
- /*
- * Immediately cease reading any current source, and begin reading from the
- * given file path.
- */
- auto SetPath(std::optional<std::string>) -> void;
- auto SetPath(const std::string&,uint32_t offset = 0) -> void;
- auto SetPath() -> void;
-
- auto HasNewStream() -> bool override;
- auto NextStream() -> std::shared_ptr<TaggedStream> override;
-
- FatfsAudioInput(const FatfsAudioInput&) = delete;
- FatfsAudioInput& operator=(const FatfsAudioInput&) = delete;
-
- private:
- auto OpenFile(const std::string& path,uint32_t offset) -> bool;
-
- auto ContainerToStreamType(database::Container)
- -> std::optional<codecs::StreamType>;
-
- database::ITagParser& tag_parser_;
- tasks::WorkerPool& bg_worker_;
-
- std::mutex new_stream_mutex_;
- std::shared_ptr<TaggedStream> new_stream_;
-
- std::atomic<bool> has_new_stream_;
-};
-
-} // namespace audio
diff --git a/src/battery/CMakeLists.txt b/src/battery/CMakeLists.txt
deleted file mode 100644
index 313a3731..00000000
--- a/src/battery/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright 2023 jacqueline <me@jacqueline.id.au>
-#
-# SPDX-License-Identifier: GPL-3.0-only
-
-idf_component_register(
- SRCS "battery.cpp"
- INCLUDE_DIRS "include"
- REQUIRES "drivers" "events")
-
-target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/codecs/CMakeLists.txt b/src/codecs/CMakeLists.txt
index b6481bd1..a1221adf 100644
--- a/src/codecs/CMakeLists.txt
+++ b/src/codecs/CMakeLists.txt
@@ -6,7 +6,7 @@ idf_component_register(
SRCS "dr_flac.cpp" "codec.cpp" "mad.cpp" "opus.cpp" "vorbis.cpp"
"source_buffer.cpp" "sample.cpp" "wav.cpp"
INCLUDE_DIRS "include"
- REQUIRES "result" "span" "libmad" "drflac" "tremor" "opusfile" "memory" "util"
+ REQUIRES "result" "libmad" "drflac" "tremor" "opusfile" "memory" "util"
"komihash")
target_compile_options("${COMPONENT_LIB}" PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/codecs/codec.cpp b/src/codecs/codec.cpp
index a51c40d6..c8e1a72c 100644
--- a/src/codecs/codec.cpp
+++ b/src/codecs/codec.cpp
@@ -9,8 +9,8 @@
#include <memory>
#include <optional>
-#include "mad.hpp"
#include "dr_flac.hpp"
+#include "mad.hpp"
#include "opus.hpp"
#include "types.hpp"
#include "vorbis.hpp"
diff --git a/src/codecs/dr_flac.cpp b/src/codecs/dr_flac.cpp
index 2f9acf8c..9341e938 100644
--- a/src/codecs/dr_flac.cpp
+++ b/src/codecs/dr_flac.cpp
@@ -100,7 +100,7 @@ auto DrFlacDecoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
return format;
}
-auto DrFlacDecoder::DecodeTo(cpp::span<sample::Sample> output)
+auto DrFlacDecoder::DecodeTo(std::span<sample::Sample> output)
-> cpp::result<OutputInfo, Error> {
size_t frames_to_read = output.size() / flac_->channels / 2;
diff --git a/src/codecs/include/codec.hpp b/src/codecs/include/codec.hpp
index e48e3c58..4d588a98 100644
--- a/src/codecs/include/codec.hpp
+++ b/src/codecs/include/codec.hpp
@@ -6,19 +6,16 @@
#pragma once
-#include <stdint.h>
-#include <sys/_stdint.h>
-
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
+#include <span>
#include <string>
#include <utility>
#include "result.hpp"
#include "sample.hpp"
-#include "span.hpp"
#include "types.hpp"
#include "memory_resource.hpp"
@@ -35,7 +32,7 @@ class IStream {
auto type() -> StreamType { return t_; }
- virtual auto Read(cpp::span<std::byte> dest) -> ssize_t = 0;
+ virtual auto Read(std::span<std::byte> dest) -> ssize_t = 0;
virtual auto CanSeek() -> bool = 0;
@@ -54,7 +51,7 @@ class IStream {
/*
* Called by codecs to indicate that they've finished parsing any header data
* within this stream, and are about to begin decoding.
- *
+ *
* Currently used as a hint to the readahead stream to begin prefetching file
* data.
*/
@@ -117,7 +114,7 @@ class ICodec {
* Decodes metadata or headers from the given input stream, and returns the
* format for the samples that will be decoded from it.
*/
- virtual auto OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
+ virtual auto OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
-> cpp::result<OutputFormat, Error> = 0;
struct OutputInfo {
@@ -128,7 +125,7 @@ class ICodec {
/*
* Writes PCM samples to the given output buffer.
*/
- virtual auto DecodeTo(cpp::span<sample::Sample> destination)
+ virtual auto DecodeTo(std::span<sample::Sample> destination)
-> cpp::result<OutputInfo, Error> = 0;
};
diff --git a/src/codecs/include/dr_flac.hpp b/src/codecs/include/dr_flac.hpp
index 547876f4..ac46e92f 100644
--- a/src/codecs/include/dr_flac.hpp
+++ b/src/codecs/include/dr_flac.hpp
@@ -10,13 +10,13 @@
#include <cstdint>
#include <memory>
#include <optional>
+#include <span>
#include <string>
#include <utility>
#include "dr_flac.h"
#include "sample.hpp"
#include "source_buffer.hpp"
-#include "span.hpp"
#include "codec.hpp"
@@ -27,10 +27,10 @@ class DrFlacDecoder : public ICodec {
DrFlacDecoder();
~DrFlacDecoder();
- auto OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
+ auto OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
-> cpp::result<OutputFormat, Error> override;
- auto DecodeTo(cpp::span<sample::Sample> destination)
+ auto DecodeTo(std::span<sample::Sample> destination)
-> cpp::result<OutputInfo, Error> override;
DrFlacDecoder(const DrFlacDecoder&) = delete;
@@ -38,7 +38,7 @@ class DrFlacDecoder : public ICodec {
private:
std::shared_ptr<IStream> input_;
- drflac *flac_;
+ drflac* flac_;
};
} // namespace codecs
diff --git a/src/codecs/include/mad.hpp b/src/codecs/include/mad.hpp
index ead0b2a2..d1d0aac5 100644
--- a/src/codecs/include/mad.hpp
+++ b/src/codecs/include/mad.hpp
@@ -11,11 +11,11 @@
#include <optional>
#include <string>
#include <utility>
+#include <span>
#include "mad.h"
#include "sample.hpp"
#include "source_buffer.hpp"
-#include "span.hpp"
#include "codec.hpp"
@@ -29,7 +29,7 @@ class MadMp3Decoder : public ICodec {
auto OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
-> cpp::result<OutputFormat, Error> override;
- auto DecodeTo(cpp::span<sample::Sample> destination)
+ auto DecodeTo(std::span<sample::Sample> destination)
-> cpp::result<OutputInfo, Error> override;
MadMp3Decoder(const MadMp3Decoder&) = delete;
diff --git a/src/codecs/include/opus.hpp b/src/codecs/include/opus.hpp
index de2f7131..200b2d44 100644
--- a/src/codecs/include/opus.hpp
+++ b/src/codecs/include/opus.hpp
@@ -10,12 +10,12 @@
#include <cstdint>
#include <memory>
#include <optional>
+#include <span>
#include <string>
#include <utility>
#include "opusfile.h"
#include "sample.hpp"
-#include "span.hpp"
#include "codec.hpp"
@@ -26,10 +26,10 @@ class XiphOpusDecoder : public ICodec {
XiphOpusDecoder();
~XiphOpusDecoder();
- auto OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
+ auto OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
-> cpp::result<OutputFormat, Error> override;
- auto DecodeTo(cpp::span<sample::Sample> destination)
+ auto DecodeTo(std::span<sample::Sample> destination)
-> cpp::result<OutputInfo, Error> override;
XiphOpusDecoder(const XiphOpusDecoder&) = delete;
diff --git a/src/codecs/include/source_buffer.hpp b/src/codecs/include/source_buffer.hpp
index 7834834d..6444dd2c 100644
--- a/src/codecs/include/source_buffer.hpp
+++ b/src/codecs/include/source_buffer.hpp
@@ -9,8 +9,7 @@
#include <cstddef>
#include <cstdint>
#include <functional>
-
-#include "span.hpp"
+#include <span>
#include "codec.hpp"
@@ -22,15 +21,15 @@ class SourceBuffer {
~SourceBuffer();
auto Refill(IStream* src) -> bool;
- auto AddBytes(std::function<size_t(cpp::span<std::byte>)> writer) -> void;
- auto ConsumeBytes(std::function<size_t(cpp::span<std::byte>)> reader) -> void;
+ auto AddBytes(std::function<size_t(std::span<std::byte>)> writer) -> void;
+ auto ConsumeBytes(std::function<size_t(std::span<std::byte>)> reader) -> void;
auto Empty() -> void;
SourceBuffer(const SourceBuffer&) = delete;
SourceBuffer& operator=(const SourceBuffer&) = delete;
private:
- const cpp::span<std::byte> buffer_;
+ const std::span<std::byte> buffer_;
size_t bytes_in_buffer_;
size_t offset_of_bytes_;
};
diff --git a/src/codecs/include/vorbis.hpp b/src/codecs/include/vorbis.hpp
index 3cf0f9ce..e6f393dc 100644
--- a/src/codecs/include/vorbis.hpp
+++ b/src/codecs/include/vorbis.hpp
@@ -10,12 +10,12 @@
#include <cstdint>
#include <memory>
#include <optional>
+#include <span>
#include <string>
#include <utility>
#include "ivorbisfile.h"
#include "sample.hpp"
-#include "span.hpp"
#include "codec.hpp"
@@ -26,10 +26,10 @@ class TremorVorbisDecoder : public ICodec {
TremorVorbisDecoder();
~TremorVorbisDecoder();
- auto OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
+ auto OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
-> cpp::result<OutputFormat, Error> override;
- auto DecodeTo(cpp::span<sample::Sample> destination)
+ auto DecodeTo(std::span<sample::Sample> destination)
-> cpp::result<OutputInfo, Error> override;
TremorVorbisDecoder(const TremorVorbisDecoder&) = delete;
diff --git a/src/codecs/include/wav.hpp b/src/codecs/include/wav.hpp
index 40138968..c09a3bb3 100644
--- a/src/codecs/include/wav.hpp
+++ b/src/codecs/include/wav.hpp
@@ -34,7 +34,7 @@ class WavDecoder : public ICodec {
auto OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
-> cpp::result<OutputFormat, Error> override;
- auto DecodeTo(cpp::span<sample::Sample> destination)
+ auto DecodeTo(std::span<sample::Sample> destination)
-> cpp::result<OutputInfo, Error> override;
WavDecoder(const WavDecoder&) = delete;
diff --git a/src/codecs/mad.cpp b/src/codecs/mad.cpp
index e44e9922..01b2f721 100644
--- a/src/codecs/mad.cpp
+++ b/src/codecs/mad.cpp
@@ -74,7 +74,7 @@ auto MadMp3Decoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
while (!eof && !got_header) {
eof = buffer_.Refill(input_.get());
- buffer_.ConsumeBytes([&](cpp::span<std::byte> buf) -> size_t {
+ buffer_.ConsumeBytes([&](std::span<std::byte> buf) -> size_t {
mad_stream_buffer(stream_.get(),
reinterpret_cast<const unsigned char*>(buf.data()),
buf.size_bytes());
@@ -130,7 +130,7 @@ auto MadMp3Decoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
}
need_refill = false;
- buffer_.ConsumeBytes([&](cpp::span<std::byte> buf) -> size_t {
+ buffer_.ConsumeBytes([&](std::span<std::byte> buf) -> size_t {
mad_stream_buffer(stream_.get(),
reinterpret_cast<const unsigned char*>(buf.data()),
buf.size());
@@ -156,13 +156,13 @@ auto MadMp3Decoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
return output;
}
-auto MadMp3Decoder::DecodeTo(cpp::span<sample::Sample> output)
+auto MadMp3Decoder::DecodeTo(std::span<sample::Sample> output)
-> cpp::result<OutputInfo, Error> {
if (current_sample_ < 0 && !is_eos_) {
if (!is_eof_) {
is_eof_ = buffer_.Refill(input_.get());
if (is_eof_) {
- buffer_.AddBytes([&](cpp::span<std::byte> buf) -> size_t {
+ buffer_.AddBytes([&](std::span<std::byte> buf) -> size_t {
if (buf.size() < MAD_BUFFER_GUARD) {
is_eof_ = false;
return 0;
@@ -174,7 +174,7 @@ auto MadMp3Decoder::DecodeTo(cpp::span<sample::Sample> output)
}
}
- buffer_.ConsumeBytes([&](cpp::span<std::byte> buf) -> size_t {
+ buffer_.ConsumeBytes([&](std::span<std::byte> buf) -> size_t {
mad_stream_buffer(stream_.get(),
reinterpret_cast<const unsigned char*>(buf.data()),
buf.size());
diff --git a/src/codecs/opus.cpp b/src/codecs/opus.cpp
index a5220c4b..b5e7c3fc 100644
--- a/src/codecs/opus.cpp
+++ b/src/codecs/opus.cpp
@@ -140,7 +140,7 @@ auto XiphOpusDecoder::OpenStream(std::shared_ptr<IStream> input,
};
}
-auto XiphOpusDecoder::DecodeTo(cpp::span<sample::Sample> output)
+auto XiphOpusDecoder::DecodeTo(std::span<sample::Sample> output)
-> cpp::result<OutputInfo, Error> {
int samples_written = op_read_stereo(opus_, output.data(), output.size());
diff --git a/src/codecs/source_buffer.cpp b/src/codecs/source_buffer.cpp
index 0a986bc3..9cd648cd 100644
--- a/src/codecs/source_buffer.cpp
+++ b/src/codecs/source_buffer.cpp
@@ -39,7 +39,7 @@ auto SourceBuffer::Refill(IStream* src) -> bool {
return false;
}
bool eof = false;
- AddBytes([&](cpp::span<std::byte> buf) -> size_t {
+ AddBytes([&](std::span<std::byte> buf) -> size_t {
ssize_t bytes_read = src->Read(buf);
// Treat read errors as EOF.
eof = bytes_read <= 0;
@@ -48,7 +48,7 @@ auto SourceBuffer::Refill(IStream* src) -> bool {
return eof;
}
-auto SourceBuffer::AddBytes(std::function<size_t(cpp::span<std::byte>)> writer)
+auto SourceBuffer::AddBytes(std::function<size_t(std::span<std::byte>)> writer)
-> void {
if (offset_of_bytes_ > 0) {
std::memmove(buffer_.data(), buffer_.data() + offset_of_bytes_,
@@ -61,9 +61,9 @@ auto SourceBuffer::AddBytes(std::function<size_t(cpp::span<std::byte>)> writer)
}
auto SourceBuffer::ConsumeBytes(
- std::function<size_t(cpp::span<std::byte>)> reader) -> void {
- size_t bytes_consumed = std::invoke(
- reader, buffer_.subspan(offset_of_bytes_, bytes_in_buffer_));
+ std::function<size_t(std::span<std::byte>)> reader) -> void {
+ size_t bytes_consumed =
+ std::invoke(reader, buffer_.subspan(offset_of_bytes_, bytes_in_buffer_));
assert(bytes_consumed <= bytes_in_buffer_);
bytes_in_buffer_ -= bytes_consumed;
diff --git a/src/codecs/test/test_mad.cpp b/src/codecs/test/test_mad.cpp
index e8c714e7..15c96eae 100644
--- a/src/codecs/test/test_mad.cpp
+++ b/src/codecs/test/test_mad.cpp
@@ -8,14 +8,14 @@
#include <algorithm>
#include <cstdint>
+#include <span>
#include "catch2/catch.hpp"
-#include "span.hpp"
#include "test.mp3.hpp"
-void load_mp3(cpp::span<std::byte> dest) {
- cpp::span<std::byte> src(reinterpret_cast<std::byte*>(test_mp3),
+void load_mp3(std::span<std::byte> dest) {
+ std::span<std::byte> src(reinterpret_cast<std::byte*>(test_mp3),
test_mp3_len);
std::copy(src.begin(), src.begin() + dest.size(), dest.begin());
}
diff --git a/src/codecs/vorbis.cpp b/src/codecs/vorbis.cpp
index 9131451b..0b2af691 100644
--- a/src/codecs/vorbis.cpp
+++ b/src/codecs/vorbis.cpp
@@ -129,7 +129,7 @@ auto TremorVorbisDecoder::OpenStream(std::shared_ptr<IStream> input,
};
}
-auto TremorVorbisDecoder::DecodeTo(cpp::span<sample::Sample> output)
+auto TremorVorbisDecoder::DecodeTo(std::span<sample::Sample> output)
-> cpp::result<OutputInfo, Error> {
int unused = 0;
long bytes_written =
diff --git a/src/codecs/wav.cpp b/src/codecs/wav.cpp
index 714ec237..f5b9d789 100644
--- a/src/codecs/wav.cpp
+++ b/src/codecs/wav.cpp
@@ -20,24 +20,24 @@ namespace codecs {
[[maybe_unused]] static const char kTag[] = "wav";
-static inline auto bytes_to_u16(cpp::span<std::byte const, 2> bytes)
+static inline auto bytes_to_u16(std::span<std::byte const, 2> bytes)
-> uint16_t {
return (uint16_t)bytes[0] | (uint16_t)bytes[1] << 8;
}
-static inline auto bytes_to_u32(cpp::span<std::byte const, 4> bytes)
+static inline auto bytes_to_u32(std::span<std::byte const, 4> bytes)
-> uint32_t {
return (uint32_t)bytes[0] | (uint32_t)bytes[1] << 8 |
(uint32_t)bytes[2] << 16 | (uint32_t)bytes[3] << 24;
}
-static inline auto bytes_to_str(cpp::span<std::byte const> bytes)
+static inline auto bytes_to_str(std::span<std::byte const> bytes)
-> std::string {
return std::string(reinterpret_cast<const char*>(bytes.data()),
- bytes.size_bytes());
+ bytes.size_bytes());
}
-static int16_t convert_f32_to_16_bit(cpp::span<const std::byte> bytes) {
+static int16_t convert_f32_to_16_bit(std::span<const std::byte> bytes) {
uint64_t val = 0;
val = (uint8_t)bytes[3];
val = (val << 8) | (uint8_t)bytes[2];
@@ -57,7 +57,7 @@ static int16_t convert_f32_to_16_bit(cpp::span<const std::byte> bytes) {
return sample::FromDouble(*fval);
}
-static int16_t convert_f64_to_16_bit(cpp::span<const std::byte> bytes) {
+static int16_t convert_f64_to_16_bit(std::span<const std::byte> bytes) {
uint64_t val = 0;
val = (uint8_t)bytes[7];
val = (val << 8) | (uint8_t)bytes[6];
@@ -71,7 +71,7 @@ static int16_t convert_f64_to_16_bit(cpp::span<const std::byte> bytes) {
return sample::FromDouble(*fval);
}
-static int16_t convert_to_16_bit(cpp::span<const std::byte> bytes) {
+static int16_t convert_to_16_bit(std::span<const std::byte> bytes) {
int depth = bytes.size();
int32_t val = 0;
// If 8-bit Assume Unsigned
@@ -82,10 +82,13 @@ static int16_t convert_to_16_bit(cpp::span<const std::byte> bytes) {
switch (depth) {
case 4:
val = (uint8_t)bytes[3];
+ [[fallthrough]];
case 3:
val = (val << 8) | (uint8_t)bytes[2];
+ [[fallthrough]];
case 2:
val = (val << 8) | (uint8_t)bytes[1];
+ [[fallthrough]];
case 1:
val = (val << 8) | (uint8_t)bytes[0];
}
@@ -98,7 +101,7 @@ WavDecoder::WavDecoder() : input_(), buffer_() {}
WavDecoder::~WavDecoder() {}
-auto WavDecoder::OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
+auto WavDecoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
-> cpp::result<OutputFormat, Error> {
input_ = input;
@@ -123,7 +126,7 @@ auto WavDecoder::OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
// - end of this part, next header we care about is 'data'
// - and then the next 4 bytes = 32 bit int = size of data
- auto buffer_span = cpp::span{buf};
+ auto buffer_span = std::span{buf};
std::string riff = bytes_to_str(buffer_span.subspan(0, 4));
if (riff != "RIFF") {
@@ -131,7 +134,7 @@ auto WavDecoder::OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
return cpp::fail(Error::kMalformedData);
}
- uint32_t file_size = bytes_to_u32(buffer_span.subspan(4, 4)) + 8;
+ // uint32_t file_size = bytes_to_u32(buffer_span.subspan(4, 4)) + 8;
std::string fmt_header = bytes_to_str(buffer_span.subspan(12, 4));
ESP_LOGI(kTag, "fmt header found? %s",
@@ -142,9 +145,9 @@ auto WavDecoder::OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
}
// Size of the fmt header, should be 16, 18 or 40
- uint32_t fmt_header_size = bytes_to_u32(buffer_span.subspan(16, 4));
+ // uint32_t fmt_header_size = bytes_to_u32(buffer_span.subspan(16, 4));
- wave_format_ = bytes_to_u16(buffer_span.subspan(20, 2));
+ wave_format_ = bytes_to_u16(buffer_span.subspan<20, 2>());
if (wave_format_ == kWaveFormatPCM) {
ESP_LOGD(kTag, "wave format: PCM");
} else if (wave_format_ == kWaveFormatExtensible) {
@@ -156,17 +159,17 @@ auto WavDecoder::OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
return cpp::fail(Error::kUnsupportedFormat);
}
- num_channels_ = bytes_to_u16(buffer_span.subspan(22, 2));
+ num_channels_ = bytes_to_u16(buffer_span.subspan<22, 2>());
- uint32_t samples_per_second = bytes_to_u32(buffer_span.subspan(24, 4));
+ uint32_t samples_per_second = bytes_to_u32(buffer_span.subspan<24, 4>());
- uint32_t avg_bytes_per_second = bytes_to_u32(buffer_span.subspan(28, 4));
+ // uint32_t avg_bytes_per_second = bytes_to_u32(buffer_span.subspan(28, 4));
- uint16_t block_align = bytes_to_u16(buffer_span.subspan(32, 2));
+ uint16_t block_align = bytes_to_u16(buffer_span.subspan<32, 2>());
bytes_per_sample_ = block_align / num_channels_;
- uint16_t bits_per_sample = bytes_to_u16(buffer_span.subspan(34, 2));
+ // uint16_t bits_per_sample = bytes_to_u16(buffer_span.subspan(34, 2));
// find the start of the data chunk
std::array<std::byte, 4> data_tag = {std::byte{0x64}, std::byte{0x61},
@@ -180,7 +183,7 @@ auto WavDecoder::OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
int data_chunk_index = std::distance(buffer_span.begin(), data_loc.begin());
uint32_t data_chunk_size =
- bytes_to_u32(buffer_span.subspan(data_chunk_index + 4, 4));
+ bytes_to_u32(buffer_span.subspan(data_chunk_index + 4, 4).first<4>());
// calculate number of samples
int number_of_samples = data_chunk_size / bytes_per_sample_;
@@ -188,20 +191,20 @@ auto WavDecoder::OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
// extension to the fmt chunk size (0 or 22)
uint16_t extension_size = 0;
if (wave_format_ == kWaveFormatExtensible) {
- extension_size = bytes_to_u16(buffer_span.subspan(36, 2));
+ extension_size = bytes_to_u16(buffer_span.subspan<36, 2>());
}
// Parse extension if applicable
if (extension_size == 22) {
// Valid bits per sample
- uint16_t valid_bits_per_sample = bytes_to_u16(buffer_span.subspan(38, 2));
+ // uint16_t valid_bits_per_sample = bytes_to_u16(buffer_span.subspan(38,
+ // 2));
- uint32_t speaker_mask = bytes_to_u32(buffer_span.subspan(40, 4));
+ // uint32_t speaker_mask = bytes_to_u32(buffer_span.subspan(40, 4));
// Parse subformat
- subformat_ = bytes_to_u16(buffer_span.subspan(44, 2));
- if (!(subformat_ == kWaveFormatPCM ||
- subformat_ == kWaveFormatIEEEFloat)) {
+ subformat_ = bytes_to_u16(buffer_span.subspan<44, 2>());
+ if (!(subformat_ == kWaveFormatPCM || subformat_ == kWaveFormatIEEEFloat)) {
ESP_LOGW(kTag, "WAVE extensible subformat_ not supported");
return cpp::fail(Error::kUnsupportedFormat);
}
@@ -210,7 +213,8 @@ auto WavDecoder::OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
int64_t data_offset = offset * samples_per_second * bytes_per_sample_;
// Seek track to start of data
- input->SeekTo(data_chunk_index + 8 + data_offset, IStream::SeekFrom::kStartOfStream);
+ input->SeekTo(data_chunk_index + 8 + data_offset,
+ IStream::SeekFrom::kStartOfStream);
output_format_ = {.num_channels = (uint8_t)num_channels_,
.sample_rate_hz = samples_per_second,
@@ -219,12 +223,12 @@ auto WavDecoder::OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
return output_format_;
}
-auto WavDecoder::DecodeTo(cpp::span<sample::Sample> output)
+auto WavDecoder::DecodeTo(std::span<sample::Sample> output)
-> cpp::result<OutputInfo, Error> {
bool is_eof = buffer_.Refill(input_.get());
size_t samples_written = 0;
- buffer_.ConsumeBytes([&](cpp::span<std::byte> buf) -> size_t {
+ buffer_.ConsumeBytes([&](std::span<std::byte> buf) -> size_t {
size_t bytes_read = buf.size_bytes();
size_t frames_read =
bytes_read / bytes_per_sample_ / output_format_.num_channels;
@@ -254,7 +258,6 @@ auto WavDecoder::DecodeTo(cpp::span<sample::Sample> output)
return samples_written * bytes_per_sample_;
});
-
return OutputInfo{.samples_written = samples_written,
.is_stream_finished = samples_written == 0 && is_eof};
}
diff --git a/src/database/CMakeLists.txt b/src/database/CMakeLists.txt
deleted file mode 100644
index 26c14815..00000000
--- a/src/database/CMakeLists.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2023 jacqueline <me@jacqueline.id.au>
-#
-# SPDX-License-Identifier: GPL-3.0-only
-
-idf_component_register(
- SRCS "env_esp.cpp" "database.cpp" "track.cpp" "records.cpp"
- "file_gatherer.cpp" "tag_parser.cpp" "index.cpp"
- INCLUDE_DIRS "include"
- REQUIRES "result" "span" "esp_psram" "fatfs" "libtags" "komihash" "cbor"
- "tasks" "memory" "util" "tinyfsm" "events" "opusfile" "libcppbor")
-
-target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
-
-set(LEVELDB_BUILD_TESTS OFF)
-set(LEVELDB_BUILD_BENCHMARKS OFF)
-set(LEVELDB_INSTALL OFF)
-
-set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
-
-add_subdirectory($ENV{PROJ_PATH}/lib/leveldb ${CMAKE_CURRENT_BINARY_DIR}/leveldb)
-
-target_link_libraries(${COMPONENT_LIB} PUBLIC leveldb)
diff --git a/src/dev_console/CMakeLists.txt b/src/dev_console/CMakeLists.txt
deleted file mode 100644
index 5555bf61..00000000
--- a/src/dev_console/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2023 jacqueline <me@jacqueline.id.au>
-#
-# SPDX-License-Identifier: GPL-3.0-only
-
-idf_component_register(
- SRCS "console.cpp"
- INCLUDE_DIRS "include"
- REQUIRES "console" "memory")
-target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/drivers/CMakeLists.txt b/src/drivers/CMakeLists.txt
index 891115d4..91534edb 100644
--- a/src/drivers/CMakeLists.txt
+++ b/src/drivers/CMakeLists.txt
@@ -7,6 +7,6 @@ idf_component_register(
"i2c.cpp" "bluetooth.cpp" "spi.cpp" "display.cpp" "display_init.cpp"
"samd.cpp" "wm8523.cpp" "nvs.cpp" "haptics.cpp" "spiffs.cpp"
INCLUDE_DIRS "include"
- REQUIRES "esp_adc" "fatfs" "result" "lvgl" "span" "tasks" "nvs_flash" "spiffs"
- "bt" "tinyfsm" "util")
+ REQUIRES "esp_adc" "fatfs" "result" "lvgl" "nvs_flash" "spiffs" "bt"
+ "tasks" "tinyfsm" "util" "libcppbor")
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/drivers/adc.cpp b/src/drivers/adc.cpp
index 3379a1ae..3038f7f6 100644
--- a/src/drivers/adc.cpp
+++ b/src/drivers/adc.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "adc.hpp"
+#include "drivers/adc.hpp"
#include <cstdint>
#include "esp_adc/adc_cali.h"
diff --git a/src/drivers/bluetooth.cpp b/src/drivers/bluetooth.cpp
index 5d1b35fa..fcb764f6 100644
--- a/src/drivers/bluetooth.cpp
+++ b/src/drivers/bluetooth.cpp
@@ -1,4 +1,4 @@
-#include "bluetooth.hpp"
+#include "drivers/bluetooth.hpp"
#include <stdint.h>
@@ -25,12 +25,11 @@
#include "freertos/portmacro.h"
#include "freertos/projdefs.h"
#include "freertos/timers.h"
-#include "sample.hpp"
#include "tinyfsm/include/tinyfsm.hpp"
-#include "bluetooth_types.hpp"
+#include "drivers/bluetooth_types.hpp"
+#include "drivers/nvs.hpp"
#include "memory_resource.hpp"
-#include "nvs.hpp"
#include "tasks.hpp"
namespace drivers {
@@ -39,6 +38,7 @@ namespace drivers {
DRAM_ATTR static StreamBufferHandle_t sStream = nullptr;
DRAM_ATTR static std::atomic<float> sVolumeFactor = 1.f;
+DRAM_ATTR static std::atomic<uint32_t> sSamplesUsed = 0;
static tasks::WorkerPool* sBgWorker;
@@ -74,6 +74,13 @@ IRAM_ATTR auto a2dp_data_cb(uint8_t* buf, int32_t buf_size) -> int32_t {
}
size_t bytes_received = xStreamBufferReceive(stream, buf, buf_size, 0);
+ size_t samples_received = bytes_received / 2;
+ if (UINT32_MAX - sSamplesUsed < samples_received) {
+ sSamplesUsed = samples_received - (UINT32_MAX - sSamplesUsed);
+ } else {
+ sSamplesUsed += samples_received;
+ }
+
// Apply software volume scaling.
int16_t* samples = reinterpret_cast<int16_t*>(buf);
float factor = sVolumeFactor.load();
@@ -166,6 +173,10 @@ auto Bluetooth::SetVolumeFactor(float f) -> void {
sVolumeFactor = f;
}
+auto Bluetooth::SamplesUsed() -> uint32_t {
+ return sSamplesUsed;
+}
+
auto Bluetooth::SetEventHandler(std::function<void(bluetooth::Event)> cb)
-> void {
auto lock = bluetooth::BluetoothState::lock();
diff --git a/src/drivers/display.cpp b/src/drivers/display.cpp
index 5c686811..b0a97b30 100644
--- a/src/drivers/display.cpp
+++ b/src/drivers/display.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "display.hpp"
+#include "drivers/display.hpp"
#include <stdint.h>
#include <cmath>
@@ -31,11 +31,11 @@
#include "hal/spi_types.h"
#include "lvgl/lvgl.h"
-#include "display_init.hpp"
-#include "gpios.hpp"
+#include "drivers/display_init.hpp"
+#include "drivers/gpios.hpp"
+#include "drivers/spi.hpp"
#include "misc/lv_color.h"
#include "soc/soc.h"
-#include "spi.hpp"
#include "tasks.hpp"
[[maybe_unused]] static const char* kTag = "DISPLAY";
diff --git a/src/drivers/display_init.cpp b/src/drivers/display_init.cpp
index a69826fa..edd36a8d 100644
--- a/src/drivers/display_init.cpp
+++ b/src/drivers/display_init.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "display_init.hpp"
+#include "drivers/display_init.hpp"
namespace drivers {
namespace displays {
diff --git a/src/drivers/gpios.cpp b/src/drivers/gpios.cpp
index aab932a7..64ba26a7 100644
--- a/src/drivers/gpios.cpp
+++ b/src/drivers/gpios.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "gpios.hpp"
+#include "drivers/gpios.hpp"
#include <cstdint>
@@ -15,7 +15,7 @@
#include "esp_intr_alloc.h"
#include "hal/gpio_types.h"
-#include "i2c.hpp"
+#include "drivers/i2c.hpp"
namespace drivers {
diff --git a/src/drivers/haptics.cpp b/src/drivers/haptics.cpp
index f7b18086..55fa9ad8 100644
--- a/src/drivers/haptics.cpp
+++ b/src/drivers/haptics.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "haptics.hpp"
+#include "drivers/haptics.hpp"
#include <stdint.h>
#include <cstdint>
@@ -21,7 +21,7 @@
#include "hal/gpio_types.h"
#include "hal/i2c_types.h"
-#include "i2c.hpp"
+#include "drivers/i2c.hpp"
namespace drivers {
@@ -54,7 +54,8 @@ Haptics::Haptics(const std::variant<ErmMotor, LraMotor>& motor) {
// Set library
// TODO(robin): try the other libraries and test response. C is marginal, D
// too much?
- WriteRegister(Register::kWaveformLibrary, static_cast<uint8_t>(kDefaultErmLibrary));
+ WriteRegister(Register::kWaveformLibrary,
+ static_cast<uint8_t>(kDefaultErmLibrary));
} else if (std::holds_alternative<LraMotor>(motor)) {
ESP_LOGI(kTag, "Setting up LRA motor...");
@@ -75,7 +76,8 @@ Haptics::Haptics(const std::variant<ErmMotor, LraMotor>& motor) {
ControlMask::kLraOpenLoop);
// Set library; only option is the LRA one for, well, LRA motors.
- WriteRegister(Register::kWaveformLibrary, static_cast<uint8_t>(Library::LRA));
+ WriteRegister(Register::kWaveformLibrary,
+ static_cast<uint8_t>(Library::LRA));
}
// Set mode (internal trigger, on writing 1 to Go register)
@@ -123,7 +125,6 @@ auto Haptics::SetWaveformEffect(Effect effect) -> void {
current_effect_ = effect;
}
-
auto Haptics::TourEffects() -> void {
TourEffects(Effect::kFirst, Effect::kLast, kDefaultErmLibrary);
}
@@ -174,7 +175,6 @@ auto Haptics::TourLibraries(Effect from, Effect to) -> void {
}
}
-
auto Haptics::PowerDown() -> void {
WriteRegister(Register::kMode, static_cast<uint8_t>(Mode::kInternalTrigger) |
ModeMask::kStandby);
diff --git a/src/drivers/i2c.cpp b/src/drivers/i2c.cpp
index 7d25717a..793ce9f9 100644
--- a/src/drivers/i2c.cpp
+++ b/src/drivers/i2c.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "i2c.hpp"
+#include "drivers/i2c.hpp"
#include <cstdint>
diff --git a/src/drivers/i2s_dac.cpp b/src/drivers/i2s_dac.cpp
index aef466c2..e5efe198 100644
--- a/src/drivers/i2s_dac.cpp
+++ b/src/drivers/i2s_dac.cpp
@@ -4,7 +4,8 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "i2s_dac.hpp"
+#include "drivers/i2s_dac.hpp"
+#include <stdint.h>
#include <cmath>
#include <cstdint>
@@ -25,11 +26,11 @@
#include "hal/gpio_types.h"
#include "hal/i2c_types.h"
-#include "gpios.hpp"
+#include "drivers/gpios.hpp"
+#include "drivers/i2c.hpp"
+#include "drivers/wm8523.hpp"
#include "hal/i2s_types.h"
-#include "i2c.hpp"
#include "soc/clk_tree_defs.h"
-#include "wm8523.hpp"
namespace drivers {
@@ -140,7 +141,7 @@ auto I2SDac::SetPaused(bool paused) -> void {
}
}
-static volatile bool sSwapWords = false;
+DRAM_ATTR static volatile bool sSwapWords = false;
auto I2SDac::Reconfigure(Channels ch, BitsPerSample bps, SampleRate rate)
-> void {
@@ -207,7 +208,7 @@ auto I2SDac::Reconfigure(Channels ch, BitsPerSample bps, SampleRate rate)
}
}
-auto I2SDac::WriteData(const cpp::span<const std::byte>& data) -> void {
+auto I2SDac::WriteData(const std::span<const std::byte>& data) -> void {
std::size_t bytes_written = 0;
esp_err_t err = i2s_channel_write(i2s_handle_, data.data(), data.size_bytes(),
&bytes_written, portMAX_DELAY);
@@ -216,6 +217,8 @@ auto I2SDac::WriteData(const cpp::span<const std::byte>& data) -> void {
}
}
+DRAM_ATTR static volatile uint32_t sSamplesRead = 0;
+
extern "C" IRAM_ATTR auto callback(i2s_chan_handle_t handle,
i2s_event_data_t* event,
void* user_ctx) -> bool {
@@ -234,6 +237,14 @@ extern "C" IRAM_ATTR auto callback(i2s_chan_handle_t handle,
size_t bytes_written =
xStreamBufferReceiveFromISR(src, buf, event->size, &ret);
+ // Assume 16 bit samples.
+ size_t samples = bytes_written / 2;
+ if (UINT32_MAX - sSamplesRead < samples) {
+ sSamplesRead = samples - (UINT32_MAX - sSamplesRead);
+ } else {
+ sSamplesRead = sSamplesRead + samples;
+ }
+
// The ESP32's I2S peripheral has a different endianness to its processors.
// ESP-IDF handles this difference for stereo channels, but not for mono
// channels. We therefore sometimes need to swap each pair of words as they're
@@ -275,6 +286,10 @@ auto I2SDac::SetSource(StreamBufferHandle_t buffer) -> void {
}
}
+auto I2SDac::SamplesUsed() -> uint32_t {
+ return sSamplesRead;
+}
+
auto I2SDac::set_channel(bool enabled) -> void {
if (i2s_active_ == enabled) {
return;
diff --git a/src/drivers/include/a2dp_audio_output.hpp b/src/drivers/include/drivers/a2dp_audio_output.hpp
index 010470c6..010470c6 100644
--- a/src/drivers/include/a2dp_audio_output.hpp
+++ b/src/drivers/include/drivers/a2dp_audio_output.hpp
diff --git a/src/drivers/include/adc.hpp b/src/drivers/include/drivers/adc.hpp
index 3e94a9ee..3e94a9ee 100644
--- a/src/drivers/include/adc.hpp
+++ b/src/drivers/include/drivers/adc.hpp
diff --git a/src/drivers/include/bluetooth.hpp b/src/drivers/include/drivers/bluetooth.hpp
index 8da5ce2e..ad61fcc1 100644
--- a/src/drivers/include/bluetooth.hpp
+++ b/src/drivers/include/drivers/bluetooth.hpp
@@ -12,11 +12,11 @@
#include <freertos/FreeRTOS.h>
#include <freertos/stream_buffer.h>
#include <stdint.h>
-#include "bluetooth_types.hpp"
+#include "drivers/bluetooth_types.hpp"
+#include "drivers/nvs.hpp"
#include "esp_a2dp_api.h"
#include "esp_avrc_api.h"
#include "esp_gap_bt_api.h"
-#include "nvs.hpp"
#include "tasks.hpp"
#include "tinyfsm.hpp"
#include "tinyfsm/include/tinyfsm.hpp"
@@ -44,6 +44,7 @@ class Bluetooth {
auto SetSource(StreamBufferHandle_t) -> void;
auto SetVolumeFactor(float) -> void;
+ auto SamplesUsed() -> uint32_t;
auto SetEventHandler(std::function<void(bluetooth::Event)> cb) -> void;
};
diff --git a/src/drivers/include/bluetooth_types.hpp b/src/drivers/include/drivers/bluetooth_types.hpp
index 7cfb236f..7cfb236f 100644
--- a/src/drivers/include/bluetooth_types.hpp
+++ b/src/drivers/include/drivers/bluetooth_types.hpp
diff --git a/src/drivers/include/display.hpp b/src/drivers/include/drivers/display.hpp
index d2e18a5c..6dc78e01 100644
--- a/src/drivers/include/display.hpp
+++ b/src/drivers/include/drivers/display.hpp
@@ -15,8 +15,8 @@
#include "result.hpp"
#include "tasks.hpp"
-#include "display_init.hpp"
-#include "gpios.hpp"
+#include "drivers/display_init.hpp"
+#include "drivers/gpios.hpp"
namespace drivers {
diff --git a/src/drivers/include/display_init.hpp b/src/drivers/include/drivers/display_init.hpp
index 9bf5b3f5..9bf5b3f5 100644
--- a/src/drivers/include/display_init.hpp
+++ b/src/drivers/include/drivers/display_init.hpp
diff --git a/src/drivers/include/fatfs_audio_input.hpp b/src/drivers/include/drivers/fatfs_audio_input.hpp
index 705f6e7d..705f6e7d 100644
--- a/src/drivers/include/fatfs_audio_input.hpp
+++ b/src/drivers/include/drivers/fatfs_audio_input.hpp
diff --git a/src/drivers/include/gpios.hpp b/src/drivers/include/drivers/gpios.hpp
index e27a3ade..e27a3ade 100644
--- a/src/drivers/include/gpios.hpp
+++ b/src/drivers/include/drivers/gpios.hpp
diff --git a/src/drivers/include/haptics.hpp b/src/drivers/include/drivers/haptics.hpp
index 940c3c6d..940c3c6d 100644
--- a/src/drivers/include/haptics.hpp
+++ b/src/drivers/include/drivers/haptics.hpp
diff --git a/src/drivers/include/i2c.hpp b/src/drivers/include/drivers/i2c.hpp
index 0dc1e7c0..0dc1e7c0 100644
--- a/src/drivers/include/i2c.hpp
+++ b/src/drivers/include/drivers/i2c.hpp
diff --git a/src/drivers/include/i2s_dac.hpp b/src/drivers/include/drivers/i2s_dac.hpp
index bd837ca0..0776dbab 100644
--- a/src/drivers/include/i2s_dac.hpp
+++ b/src/drivers/include/drivers/i2s_dac.hpp
@@ -11,6 +11,7 @@
#include <functional>
#include <memory>
#include <optional>
+#include <span>
#include <utility>
#include "driver/i2s_std.h"
@@ -20,9 +21,8 @@
#include "freertos/portmacro.h"
#include "freertos/stream_buffer.h"
#include "result.hpp"
-#include "span.hpp"
-#include "gpios.hpp"
+#include "drivers/gpios.hpp"
#include "sys/_stdint.h"
namespace drivers {
@@ -68,9 +68,11 @@ class I2SDac {
auto Reconfigure(Channels ch, BitsPerSample bps, SampleRate rate) -> void;
- auto WriteData(const cpp::span<const std::byte>& data) -> void;
+ auto WriteData(const std::span<const std::byte>& data) -> void;
auto SetSource(StreamBufferHandle_t buffer) -> void;
+ auto SamplesUsed() -> uint32_t;
+
// Not copyable or movable.
I2SDac(const I2SDac&) = delete;
I2SDac& operator=(const I2SDac&) = delete;
diff --git a/src/drivers/include/nvs.hpp b/src/drivers/include/drivers/nvs.hpp
index 7c74ceb0..83bb8097 100644
--- a/src/drivers/include/nvs.hpp
+++ b/src/drivers/include/drivers/nvs.hpp
@@ -13,9 +13,8 @@
#include "esp_err.h"
#include "nvs.h"
-#include "bluetooth_types.hpp"
+#include "drivers/bluetooth_types.hpp"
#include "lru_cache.hpp"
-#include "tasks.hpp"
namespace drivers {
diff --git a/src/drivers/include/samd.hpp b/src/drivers/include/drivers/samd.hpp
index 55ea513c..55ea513c 100644
--- a/src/drivers/include/samd.hpp
+++ b/src/drivers/include/drivers/samd.hpp
diff --git a/src/drivers/include/spi.hpp b/src/drivers/include/drivers/spi.hpp
index 60638f71..60638f71 100644
--- a/src/drivers/include/spi.hpp
+++ b/src/drivers/include/drivers/spi.hpp
diff --git a/src/drivers/include/spiffs.hpp b/src/drivers/include/drivers/spiffs.hpp
index 04478590..04478590 100644
--- a/src/drivers/include/spiffs.hpp
+++ b/src/drivers/include/drivers/spiffs.hpp
diff --git a/src/drivers/include/storage.hpp b/src/drivers/include/drivers/storage.hpp
index 836bbbdc..3aefff2d 100644
--- a/src/drivers/include/storage.hpp
+++ b/src/drivers/include/drivers/storage.hpp
@@ -15,7 +15,7 @@
#include "ff.h"
#include "result.hpp"
-#include "gpios.hpp"
+#include "drivers/gpios.hpp"
namespace drivers {
diff --git a/src/drivers/include/touchwheel.hpp b/src/drivers/include/drivers/touchwheel.hpp
index 18ace2b4..60902087 100644
--- a/src/drivers/include/touchwheel.hpp
+++ b/src/drivers/include/drivers/touchwheel.hpp
@@ -12,7 +12,7 @@
#include "esp_err.h"
#include "result.hpp"
-#include "gpios.hpp"
+#include "drivers/gpios.hpp"
namespace drivers {
diff --git a/src/drivers/include/wm8523.hpp b/src/drivers/include/drivers/wm8523.hpp
index a64f6bac..a64f6bac 100644
--- a/src/drivers/include/wm8523.hpp
+++ b/src/drivers/include/drivers/wm8523.hpp
diff --git a/src/drivers/nvs.cpp b/src/drivers/nvs.cpp
index c8befe48..1389fd0d 100644
--- a/src/drivers/nvs.cpp
+++ b/src/drivers/nvs.cpp
@@ -4,22 +4,19 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "nvs.hpp"
-#include <stdint.h>
-#include <sys/_stdint.h>
+#include "drivers/nvs.hpp"
#include <cstdint>
#include <memory>
-#include "bluetooth.hpp"
-#include "bluetooth_types.hpp"
#include "cppbor.h"
#include "cppbor_parse.h"
+#include "drivers/bluetooth.hpp"
+#include "drivers/bluetooth_types.hpp"
+#include "drivers/wm8523.hpp"
#include "esp_log.h"
#include "nvs.h"
#include "nvs_flash.h"
-#include "tasks.hpp"
-#include "wm8523.hpp"
namespace drivers {
@@ -42,8 +39,8 @@ static constexpr char kKeyDisplayRows[] = "disprows";
static constexpr char kKeyHapticMotorType[] = "hapticmtype";
static constexpr char kKeyDbAutoIndex[] = "dbautoindex";
-static auto nvs_get_string(nvs_handle_t nvs, const char* key)
- -> std::optional<std::string> {
+static auto nvs_get_string(nvs_handle_t nvs,
+ const char* key) -> std::optional<std::string> {
size_t len = 0;
if (nvs_get_blob(nvs, key, NULL, &len) != ESP_OK) {
return {};
@@ -190,8 +187,7 @@ auto NvsStorage::Read() -> void {
lock_polarity_.read(handle_);
display_cols_.read(handle_);
display_rows_.read(handle_);
- haptic_motor_type_.read(handle_),
- brightness_.read(handle_);
+ haptic_motor_type_.read(handle_), brightness_.read(handle_);
sensitivity_.read(handle_);
amp_max_vol_.read(handle_);
amp_cur_vol_.read(handle_);
@@ -208,8 +204,7 @@ auto NvsStorage::Write() -> bool {
lock_polarity_.write(handle_);
display_cols_.write(handle_);
display_rows_.write(handle_);
- haptic_motor_type_.write(handle_),
- brightness_.write(handle_);
+ haptic_motor_type_.write(handle_), brightness_.write(handle_);
sensitivity_.write(handle_);
amp_max_vol_.write(handle_);
amp_cur_vol_.write(handle_);
@@ -290,8 +285,8 @@ auto NvsStorage::BluetoothVolume(const bluetooth::mac_addr_t& mac) -> uint8_t {
return bt_volumes_.Get(mac).value_or(50);
}
-auto NvsStorage::BluetoothVolume(const bluetooth::mac_addr_t& mac, uint8_t vol)
- -> void {
+auto NvsStorage::BluetoothVolume(const bluetooth::mac_addr_t& mac,
+ uint8_t vol) -> void {
std::lock_guard<std::mutex> lock{mutex_};
bt_volumes_dirty_ = true;
bt_volumes_.Put(mac, vol);
diff --git a/src/drivers/samd.cpp b/src/drivers/samd.cpp
index e47d9cfe..c5e8c08a 100644
--- a/src/drivers/samd.cpp
+++ b/src/drivers/samd.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "samd.hpp"
+#include "drivers/samd.hpp"
#include <cstdint>
#include <optional>
@@ -15,7 +15,7 @@
#include "hal/gpio_types.h"
#include "hal/i2c_types.h"
-#include "i2c.hpp"
+#include "drivers/i2c.hpp"
enum Registers : uint8_t {
kSamdFirmwareVersion = 0,
diff --git a/src/drivers/spi.cpp b/src/drivers/spi.cpp
index ae6f83a2..f4d3ea6e 100644
--- a/src/drivers/spi.cpp
+++ b/src/drivers/spi.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "spi.hpp"
+#include "drivers/spi.hpp"
#include "driver/sdspi_host.h"
#include "driver/spi_common.h"
diff --git a/src/drivers/spiffs.cpp b/src/drivers/spiffs.cpp
index f03d2f68..30a2db33 100644
--- a/src/drivers/spiffs.cpp
+++ b/src/drivers/spiffs.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "spiffs.hpp"
+#include "drivers/spiffs.hpp"
#include "esp_err.h"
#include "esp_log.h"
diff --git a/src/drivers/storage.cpp b/src/drivers/storage.cpp
index b3588130..b2b7174e 100644
--- a/src/drivers/storage.cpp
+++ b/src/drivers/storage.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "storage.hpp"
+#include "drivers/storage.hpp"
#include <atomic>
#include <memory>
@@ -23,7 +23,7 @@
#include "hal/spi_types.h"
#include "sdmmc_cmd.h"
-#include "gpios.hpp"
+#include "drivers/gpios.hpp"
#include "memory_resource.hpp"
[[maybe_unused]] static const char* kTag = "SDSTORAGE";
diff --git a/src/drivers/test/test_dac.cpp b/src/drivers/test/test_dac.cpp
index e8d8dd94..2269f280 100644
--- a/src/drivers/test/test_dac.cpp
+++ b/src/drivers/test/test_dac.cpp
@@ -10,8 +10,8 @@
#include "catch2/catch.hpp"
-#include "gpios.hpp"
-#include "i2c.hpp"
+#include "drivers/gpios.hpp"
+#include "drivers/i2c.hpp"
#include "i2c_fixture.hpp"
namespace drivers {
diff --git a/src/drivers/test/test_gpio_expander.cpp b/src/drivers/test/test_gpio_expander.cpp
index 972bcf09..7c323313 100644
--- a/src/drivers/test/test_gpio_expander.cpp
+++ b/src/drivers/test/test_gpio_expander.cpp
@@ -4,11 +4,11 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "gpios.hpp"
+#include "drivers/gpios.hpp"
#include "catch2/catch.hpp"
-#include "i2c.hpp"
+#include "drivers/i2c.hpp"
#include "i2c_fixture.hpp"
namespace drivers {
diff --git a/src/drivers/test/test_storage.cpp b/src/drivers/test/test_storage.cpp
index 062de3af..f97a0a85 100644
--- a/src/drivers/test/test_storage.cpp
+++ b/src/drivers/test/test_storage.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "storage.hpp"
+#include "drivers/storage.hpp"
#include <dirent.h>
@@ -14,10 +14,10 @@
#include "catch2/catch.hpp"
-#include "gpios.hpp"
-#include "i2c.hpp"
+#include "drivers/gpios.hpp"
+#include "drivers/i2c.hpp"
+#include "drivers/spi.hpp"
#include "i2c_fixture.hpp"
-#include "spi.hpp"
#include "spi_fixture.hpp"
namespace drivers {
diff --git a/src/drivers/touchwheel.cpp b/src/drivers/touchwheel.cpp
index 41b9a6af..5d55c6f2 100644
--- a/src/drivers/touchwheel.cpp
+++ b/src/drivers/touchwheel.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "touchwheel.hpp"
+#include "drivers/touchwheel.hpp"
#include <stdint.h>
#include <cstdint>
@@ -18,7 +18,7 @@
#include "hal/gpio_types.h"
#include "hal/i2c_types.h"
-#include "i2c.hpp"
+#include "drivers/i2c.hpp"
namespace drivers {
diff --git a/src/drivers/wm8523.cpp b/src/drivers/wm8523.cpp
index 9fe3e001..26316387 100644
--- a/src/drivers/wm8523.cpp
+++ b/src/drivers/wm8523.cpp
@@ -3,14 +3,14 @@
*
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "wm8523.hpp"
+#include "drivers/wm8523.hpp"
#include <cstdint>
#include "esp_err.h"
+#include "drivers/i2c.hpp"
#include "hal/i2c_types.h"
-#include "i2c.hpp"
namespace drivers {
namespace wm8523 {
diff --git a/src/events/CMakeLists.txt b/src/events/CMakeLists.txt
deleted file mode 100644
index 132c39c7..00000000
--- a/src/events/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2023 jacqueline <me@jacqueline.id.au>
-#
-# SPDX-License-Identifier: GPL-3.0-only
-
-idf_component_register(
- SRCS "event_queue.cpp"
- INCLUDE_DIRS "include"
- REQUIRES "tinyfsm" "ui")
-target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/graphics/CMakeLists.txt b/src/graphics/CMakeLists.txt
new file mode 100644
index 00000000..5b2c5d58
--- /dev/null
+++ b/src/graphics/CMakeLists.txt
@@ -0,0 +1,7 @@
+# Copyright 2024 jacqueline <me@jacqueline.id.au>
+#
+# SPDX-License-Identifier: GPL-3.0-only
+
+idf_component_register(
+ SRCS "font_fusion_10.c" "font_fusion_12.c" "splash.c"
+ REQUIRES "lvgl")
diff --git a/src/ui/font_fusion_10.c b/src/graphics/font_fusion_10.c
index 67143440..67143440 100644
--- a/src/ui/font_fusion_10.c
+++ b/src/graphics/font_fusion_10.c
diff --git a/src/ui/font_fusion_12.c b/src/graphics/font_fusion_12.c
index 97c311b8..97c311b8 100644
--- a/src/ui/font_fusion_12.c
+++ b/src/graphics/font_fusion_12.c
diff --git a/src/ui/splash.c b/src/graphics/splash.c
index 61eff739..61eff739 100644
--- a/src/ui/splash.c
+++ b/src/graphics/splash.c
diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt
deleted file mode 100644
index 4754ba47..00000000
--- a/src/input/CMakeLists.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright 2023 jacqueline <me@jacqueline.id.au>
-#
-# SPDX-License-Identifier: GPL-3.0-only
-
-idf_component_register(
- SRCS "input_touch_wheel.cpp" "input_touch_dpad.cpp" "input_trigger.cpp"
- "input_volume_buttons.cpp" "lvgl_input_driver.cpp" "feedback_haptics.cpp"
- "device_factory.cpp" "input_nav_buttons.cpp" "input_hook.cpp"
- "input_hook_actions.cpp"
- INCLUDE_DIRS "include"
- REQUIRES "drivers" "lvgl" "events" "system_fsm")
-
-target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/input/include/lvgl_input_driver.hpp b/src/input/include/lvgl_input_driver.hpp
deleted file mode 100644
index 9f43d27f..00000000
--- a/src/input/include/lvgl_input_driver.hpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#pragma once
-
-#include <cstdint>
-#include <deque>
-#include <memory>
-#include <set>
-
-#include "core/lv_group.h"
-#include "device_factory.hpp"
-#include "feedback_device.hpp"
-#include "gpios.hpp"
-#include "hal/lv_hal_indev.h"
-
-#include "input_device.hpp"
-#include "nvs.hpp"
-#include "property.hpp"
-#include "touchwheel.hpp"
-
-namespace input {
-
-/*
- * Implementation of an LVGL input device. This class composes multiple
- * IInputDevice and IFeedbackDevice instances together into a single LVGL
- * device.
- */
-class LvglInputDriver {
- public:
- LvglInputDriver(drivers::NvsStorage& nvs, DeviceFactory&);
-
- auto mode() -> lua::Property& { return mode_; }
-
- auto read(lv_indev_data_t* data) -> void;
- auto feedback(uint8_t) -> void;
-
- auto registration() -> lv_indev_t* { return registration_; }
- auto lock(bool l) -> void { is_locked_ = l; }
-
- auto pushHooks(lua_State* L) -> int;
-
- private:
- drivers::NvsStorage& nvs_;
- DeviceFactory& factory_;
-
- lua::Property mode_;
- lv_indev_drv_t driver_;
- lv_indev_t* registration_;
-
- std::vector<std::shared_ptr<IInputDevice>> inputs_;
- std::vector<std::shared_ptr<IFeedbackDevice>> feedbacks_;
-
- bool is_locked_;
-};
-
-} // namespace input
diff --git a/src/input/lvgl_input_driver.cpp b/src/input/lvgl_input_driver.cpp
deleted file mode 100644
index 61a85fa5..00000000
--- a/src/input/lvgl_input_driver.cpp
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#include "lvgl_input_driver.hpp"
-#include <stdint.h>
-
-#include <cstdint>
-#include <memory>
-#include <variant>
-
-#include "device_factory.hpp"
-#include "feedback_haptics.hpp"
-#include "input_touch_wheel.hpp"
-#include "input_trigger.hpp"
-#include "input_volume_buttons.hpp"
-#include "lauxlib.h"
-#include "lua.h"
-#include "lvgl.h"
-#include "nvs.hpp"
-#include "property.hpp"
-
-[[maybe_unused]] static constexpr char kTag[] = "input";
-
-namespace input {
-
-static void read_cb(lv_indev_drv_t* drv, lv_indev_data_t* data) {
- LvglInputDriver* instance =
- reinterpret_cast<LvglInputDriver*>(drv->user_data);
- instance->read(data);
-}
-
-static void feedback_cb(lv_indev_drv_t* drv, uint8_t event) {
- LvglInputDriver* instance =
- reinterpret_cast<LvglInputDriver*>(drv->user_data);
- instance->feedback(event);
-}
-
-auto intToMode(int raw) -> std::optional<drivers::NvsStorage::InputModes> {
- switch (raw) {
- case 0:
- return drivers::NvsStorage::InputModes::kButtonsOnly;
- case 1:
- return drivers::NvsStorage::InputModes::kButtonsWithWheel;
- case 2:
- return drivers::NvsStorage::InputModes::kDirectionalWheel;
- case 3:
- return drivers::NvsStorage::InputModes::kRotatingWheel;
- default:
- return {};
- }
-}
-
-LvglInputDriver::LvglInputDriver(drivers::NvsStorage& nvs,
- DeviceFactory& factory)
- : nvs_(nvs),
- factory_(factory),
- mode_(static_cast<int>(nvs.PrimaryInput()),
- [&](const lua::LuaValue& val) {
- if (!std::holds_alternative<int>(val)) {
- return false;
- }
- auto mode = intToMode(std::get<int>(val));
- if (!mode) {
- return false;
- }
- nvs.PrimaryInput(*mode);
- inputs_ = factory.createInputs(*mode);
- return true;
- }),
- driver_(),
- registration_(nullptr),
- inputs_(factory.createInputs(nvs.PrimaryInput())),
- feedbacks_(factory.createFeedbacks()),
- is_locked_(false) {
- lv_indev_drv_init(&driver_);
- driver_.type = LV_INDEV_TYPE_ENCODER;
- driver_.read_cb = read_cb;
- driver_.feedback_cb = feedback_cb;
- driver_.user_data = this;
- driver_.long_press_time = kLongPressDelayMs;
- driver_.long_press_repeat_time = kRepeatDelayMs;
-
- registration_ = lv_indev_drv_register(&driver_);
-}
-
-auto LvglInputDriver::read(lv_indev_data_t* data) -> void {
- // TODO: we should pass lock state on to the individual devices, since they
- // may wish to either ignore the lock state, or power down until unlock.
- if (is_locked_) {
- return;
- }
- for (auto&& device : inputs_) {
- device->read(data);
- }
-}
-
-auto LvglInputDriver::feedback(uint8_t event) -> void {
- if (is_locked_) {
- return;
- }
- for (auto&& device : feedbacks_) {
- device->feedback(event);
- }
-}
-
-auto LvglInputDriver::pushHooks(lua_State* L) -> int {
- lua_newtable(L);
-
- for (auto& dev : inputs_) {
- lua_pushlstring(L, dev->name().data(), dev->name().size());
- lua_newtable(L);
-
- for (auto& hook : dev->hooks()) {
- lua_pushlstring(L, hook.name().data(), hook.name().size());
- hook.pushHooks(L);
- lua_rawset(L, -3);
- }
-
- lua_rawset(L, -3);
- }
- return 1;
-}
-
-} // namespace input
diff --git a/src/locale/CMakeLists.txt b/src/locale/CMakeLists.txt
index 627ca314..9c1c2619 100644
--- a/src/locale/CMakeLists.txt
+++ b/src/locale/CMakeLists.txt
@@ -6,6 +6,6 @@ idf_component_register(
SRCS "collation.cpp" "strxfrm_l.c"
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "priv_include"
- REQUIRES "span" "esp_partition" "spi_flash")
+ REQUIRES "esp_partition" "spi_flash")
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/locale/include/collation.hpp b/src/locale/include/collation.hpp
index b666860d..88f499c4 100644
--- a/src/locale/include/collation.hpp
+++ b/src/locale/include/collation.hpp
@@ -9,10 +9,10 @@
#include <cstddef>
#include <memory>
#include <optional>
+#include <span>
#include <string>
#include "esp_partition.h"
-#include "span.hpp"
#include "strxfrm.h"
diff --git a/src/lua/CMakeLists.txt b/src/lua/CMakeLists.txt
deleted file mode 100644
index 4aa5a123..00000000
--- a/src/lua/CMakeLists.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright 2023 jacqueline <me@jacqueline.id.au>
-#
-# SPDX-License-Identifier: GPL-3.0-only
-
-idf_component_register(
- SRCS "lua_theme.cpp" "lua_thread.cpp" "bridge.cpp" "property.cpp" "lua_database.cpp"
- "lua_queue.cpp" "lua_version.cpp" "lua_theme.cpp" "lua_controls.cpp" "registry.cpp"
- "lua_screen.cpp" "lua_filesystem.cpp" "file_iterator.cpp"
- INCLUDE_DIRS "include"
- REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "fatfs"
- "esp_timer" "battery" "esp-idf-lua" "luavgl" "lua-linenoise" "lua-term"
- "esp_app_format")
-target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/main/CMakeLists.txt b/src/main/CMakeLists.txt
index 3ae92ac2..9019c254 100644
--- a/src/main/CMakeLists.txt
+++ b/src/main/CMakeLists.txt
@@ -2,8 +2,5 @@
#
# SPDX-License-Identifier: GPL-3.0-only
-idf_component_register(
- SRCS "main.cpp"
- INCLUDE_DIRS "."
- REQUIRES "audio" "ui" "system_fsm" "events")
+idf_component_register(SRCS "main.cpp" INCLUDE_DIRS "." REQUIRES "tangara")
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/main/main.cpp b/src/main/main.cpp
index cf27b132..8c7565f3 100644
--- a/src/main/main.cpp
+++ b/src/main/main.cpp
@@ -7,14 +7,14 @@
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
-#include "i2c.hpp"
-#include "system_events.hpp"
#include "tinyfsm.hpp"
-#include "audio_fsm.hpp"
-#include "event_queue.hpp"
-#include "system_fsm.hpp"
-#include "ui_fsm.hpp"
+#include "audio/audio_fsm.hpp"
+#include "drivers/i2c.hpp"
+#include "events/event_queue.hpp"
+#include "system_fsm/system_events.hpp"
+#include "system_fsm/system_fsm.hpp"
+#include "ui/ui_fsm.hpp"
extern "C" void app_main(void) {
ESP_ERROR_CHECK(drivers::init_i2c());
diff --git a/src/memory/include/himem.hpp b/src/memory/include/himem.hpp
index 81166e0d..f70648a9 100644
--- a/src/memory/include/himem.hpp
+++ b/src/memory/include/himem.hpp
@@ -8,9 +8,9 @@
#include <cstddef>
#include <cstdint>
+#include <span>
#include "esp32/himem.h"
-#include "span.hpp"
/*
* Wrapper around an ESP-IDF himem allocation, which uses RAII to clean up after
@@ -62,14 +62,14 @@ class MappableRegion {
}
}
- auto Get() -> cpp::span<std::byte> {
+ auto Get() -> std::span<std::byte> {
if (bytes_ == nullptr) {
return {};
}
return {bytes_, size};
}
- auto Map(const HimemAlloc<size>& alloc) -> cpp::span<std::byte> {
+ auto Map(const HimemAlloc<size>& alloc) -> std::span<std::byte> {
assert(bytes_ == nullptr);
ESP_ERROR_CHECK(esp_himem_map(alloc.handle, range_handle, 0, 0, size, 0,
reinterpret_cast<void**>(&bytes_)));
diff --git a/src/system_fsm/CMakeLists.txt b/src/system_fsm/CMakeLists.txt
deleted file mode 100644
index e98d4653..00000000
--- a/src/system_fsm/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2023 jacqueline <me@jacqueline.id.au>
-#
-# SPDX-License-Identifier: GPL-3.0-only
-
-idf_component_register(
- SRCS "system_fsm.cpp" "running.cpp" "booting.cpp" "idle.cpp"
- "service_locator.cpp"
- INCLUDE_DIRS "include"
- REQUIRES "tinyfsm" "drivers" "database" "ui" "result" "events" "audio"
- "app_console" "battery" "locale")
-target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/tangara/CMakeLists.txt b/src/tangara/CMakeLists.txt
new file mode 100644
index 00000000..fb8d1041
--- /dev/null
+++ b/src/tangara/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Copyright 2024 jacqueline <me@jacqueline.id.au>
+#
+# SPDX-License-Identifier: GPL-3.0-only
+
+idf_component_register(
+ SRC_DIRS "app_console" "audio" "battery" "database" "dev_console" "events"
+ "input" "lua" "system_fsm" "ui"
+ INCLUDE_DIRS "."
+ REQUIRES "codecs" "drivers" "locale" "memory" "tasks" "util" "graphics"
+ "tinyfsm" "lvgl" "esp_timer" "luavgl" "esp_app_format" "libcppbor" "libtags"
+ "komihash" "result" "esp_psram" "fatfs" "millershuffle" "speexdsp" "console"
+ "esp-idf-lua" "lua-linenoise" "lua-term")
+target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
+
+set(LEVELDB_BUILD_TESTS OFF)
+set(LEVELDB_BUILD_BENCHMARKS OFF)
+set(LEVELDB_INSTALL OFF)
+
+set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
+
+add_subdirectory($ENV{PROJ_PATH}/lib/leveldb ${CMAKE_CURRENT_BINARY_DIR}/leveldb)
+target_link_libraries(${COMPONENT_LIB} PUBLIC leveldb)
diff --git a/src/app_console/app_console.cpp b/src/tangara/app_console/app_console.cpp
index 7c7c1abc..e3048ba2 100644
--- a/src/app_console/app_console.cpp
+++ b/src/tangara/app_console/app_console.cpp
@@ -4,11 +4,9 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "app_console.hpp"
+#include "app_console/app_console.hpp"
#include <dirent.h>
-#include <stdint.h>
-#include <sys/_stdint.h>
#include <algorithm>
#include <cstdint>
@@ -21,11 +19,7 @@
#include <string>
#include "FreeRTOSConfig.h"
-#include "audio_events.hpp"
-#include "audio_fsm.hpp"
-#include "bluetooth.hpp"
-#include "bluetooth_types.hpp"
-#include "database.hpp"
+
#include "esp_app_desc.h"
#include "esp_console.h"
#include "esp_err.h"
@@ -34,19 +28,25 @@
#include "esp_intr_alloc.h"
#include "esp_log.h"
#include "esp_system.h"
-#include "event_queue.hpp"
#include "ff.h"
-#include "freertos/FreeRTOSConfig_arch.h"
#include "freertos/projdefs.h"
-#include "haptics.hpp"
-#include "index.hpp"
-#include "lua_registry.hpp"
+
+#include "drivers/bluetooth.hpp"
+#include "drivers/bluetooth_types.hpp"
+#include "drivers/haptics.hpp"
+#include "drivers/samd.hpp"
#include "memory_resource.hpp"
-#include "samd.hpp"
-#include "service_locator.hpp"
-#include "system_events.hpp"
-#include "track.hpp"
-#include "ui_events.hpp"
+
+#include "audio/audio_events.hpp"
+#include "audio/audio_fsm.hpp"
+#include "database/database.hpp"
+#include "database/index.hpp"
+#include "database/track.hpp"
+#include "events/event_queue.hpp"
+#include "lua/lua_registry.hpp"
+#include "system_fsm/service_locator.hpp"
+#include "system_fsm/system_events.hpp"
+#include "ui/ui_events.hpp"
namespace console {
diff --git a/src/app_console/include/app_console.hpp b/src/tangara/app_console/app_console.hpp
index 5981cc04..b88b1330 100644
--- a/src/app_console/include/app_console.hpp
+++ b/src/tangara/app_console/app_console.hpp
@@ -8,12 +8,12 @@
#include <memory>
-#include "bluetooth.hpp"
-#include "console.hpp"
-#include "database.hpp"
-#include "samd.hpp"
-#include "service_locator.hpp"
-#include "track_queue.hpp"
+#include "audio/track_queue.hpp"
+#include "drivers/bluetooth.hpp"
+#include "dev_console/console.hpp"
+#include "database/database.hpp"
+#include "drivers/samd.hpp"
+#include "system_fsm/service_locator.hpp"
namespace console {
diff --git a/src/audio/README.md b/src/tangara/audio/README.md
index 218be2c4..218be2c4 100644
--- a/src/audio/README.md
+++ b/src/tangara/audio/README.md
diff --git a/src/tangara/audio/audio_decoder.cpp b/src/tangara/audio/audio_decoder.cpp
new file mode 100644
index 00000000..ee06d984
--- /dev/null
+++ b/src/tangara/audio/audio_decoder.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "audio/audio_decoder.hpp"
+
+#include <cassert>
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <deque>
+#include <memory>
+#include <span>
+#include <variant>
+
+#include "esp_err.h"
+#include "esp_heap_caps.h"
+#include "esp_log.h"
+#include "freertos/portmacro.h"
+#include "freertos/projdefs.h"
+#include "freertos/queue.h"
+
+#include "audio/audio_events.hpp"
+#include "audio/audio_fsm.hpp"
+#include "audio/audio_sink.hpp"
+#include "audio/audio_source.hpp"
+#include "audio/processor.hpp"
+#include "codec.hpp"
+#include "database/track.hpp"
+#include "drivers/i2s_dac.hpp"
+#include "events/event_queue.hpp"
+#include "sample.hpp"
+#include "tasks.hpp"
+#include "types.hpp"
+#include "ui/ui_fsm.hpp"
+
+namespace audio {
+
+static const char* kTag = "decoder";
+
+/*
+ * The size of the buffer used for holding decoded samples. This buffer is
+ * allocated in internal memory for greater speed, so be careful when
+ * increasing its size.
+ */
+static constexpr std::size_t kCodecBufferLength =
+ drivers::kI2SBufferLengthFrames * sizeof(sample::Sample);
+
+auto Decoder::Start(std::shared_ptr<SampleProcessor> sink) -> Decoder* {
+ Decoder* task = new Decoder(sink);
+ tasks::StartPersistent<tasks::Type::kAudioDecoder>([=]() { task->Main(); });
+ return task;
+}
+
+auto Decoder::open(std::shared_ptr<TaggedStream> stream) -> void {
+ NextStream* next = new NextStream();
+ next->stream = stream;
+ // The decoder services its queue very quickly, so blocking on this write
+ // should be fine. If we discover contention here, then adding more space for
+ // items to next_stream_ should be fine too.
+ xQueueSend(next_stream_, &next, portMAX_DELAY);
+}
+
+Decoder::Decoder(std::shared_ptr<SampleProcessor> processor)
+ : processor_(processor), next_stream_(xQueueCreate(1, sizeof(void*))) {
+ ESP_LOGI(kTag, "allocating codec buffer, %u KiB", kCodecBufferLength / 1024);
+ codec_buffer_ = {
+ reinterpret_cast<sample::Sample*>(heap_caps_calloc(
+ kCodecBufferLength, sizeof(sample::Sample), MALLOC_CAP_DMA)),
+ kCodecBufferLength};
+}
+
+/*
+ * Main decoding loop. Handles watching for new streams, or continuing to nudge
+ * along the current stream if we have one.
+ */
+void Decoder::Main() {
+ for (;;) {
+ // Check whether there's a new stream to begin. If we're idle, then we
+ // simply park and wait forever for a stream to arrive.
+ TickType_t wait_time = stream_ ? 0 : portMAX_DELAY;
+ NextStream* next;
+ if (xQueueReceive(next_stream_, &next, wait_time)) {
+ // Copy the data out of the queue, then clean up the item.
+ std::shared_ptr<TaggedStream> new_stream = next->stream;
+ delete next;
+
+ // If we were already decoding, then make sure we finish up the current
+ // file gracefully.
+ if (stream_) {
+ finishDecode(true);
+ }
+
+ // Ensure there's actually stream data; we might have been given nullptr
+ // as a signal to stop.
+ if (!new_stream) {
+ continue;
+ }
+
+ // Start decoding the new stream.
+ prepareDecode(new_stream);
+ }
+
+ if (!continueDecode()) {
+ finishDecode(false);
+ }
+ }
+}
+
+auto Decoder::prepareDecode(std::shared_ptr<TaggedStream> stream) -> void {
+ auto stub_track = std::make_shared<TrackInfo>(TrackInfo{
+ .tags = stream->tags(),
+ .uri = stream->Filepath(),
+ .duration = {},
+ .start_offset = {},
+ .bitrate_kbps = {},
+ .encoding = stream->type(),
+ .format = {},
+ });
+
+ codec_.reset(codecs::CreateCodecForType(stream->type()).value_or(nullptr));
+ if (!codec_) {
+ ESP_LOGE(kTag, "no codec found for stream");
+ events::Audio().Dispatch(
+ internal::DecodingFailedToStart{.track = stub_track});
+ return;
+ }
+
+ auto open_res = codec_->OpenStream(stream, stream->Offset());
+ if (open_res.has_error()) {
+ ESP_LOGE(kTag, "codec failed to start: %s",
+ codecs::ICodec::ErrorString(open_res.error()).c_str());
+ events::Audio().Dispatch(
+ internal::DecodingFailedToStart{.track = stub_track});
+ return;
+ }
+
+ // Decoding started okay! Fill out the rest of the track info for this
+ // stream.
+ stream_ = stream;
+ track_ = std::make_shared<TrackInfo>(TrackInfo{
+ .tags = stream->tags(),
+ .uri = stream->Filepath(),
+ .duration = {},
+ .start_offset = stream->Offset(),
+ .bitrate_kbps = {},
+ .encoding = stream->type(),
+ .format =
+ {
+ .sample_rate = open_res->sample_rate_hz,
+ .num_channels = open_res->num_channels,
+ .bits_per_sample = 16,
+ },
+ });
+
+ if (open_res->total_samples) {
+ track_->duration = open_res->total_samples.value() /
+ open_res->num_channels / open_res->sample_rate_hz;
+ }
+
+ events::Audio().Dispatch(internal::DecodingStarted{.track = track_});
+ processor_->beginStream(track_);
+}
+
+auto Decoder::continueDecode() -> bool {
+ auto res = codec_->DecodeTo(codec_buffer_);
+ if (res.has_error()) {
+ return false;
+ }
+
+ if (res->samples_written > 0) {
+ processor_->continueStream(codec_buffer_.first(res->samples_written));
+ }
+
+ return !res->is_stream_finished;
+}
+
+auto Decoder::finishDecode(bool cancel) -> void {
+ assert(track_);
+
+ // Tell everyone we're finished.
+ if (cancel) {
+ events::Audio().Dispatch(internal::DecodingCancelled{.track = track_});
+ } else {
+ events::Audio().Dispatch(internal::DecodingFinished{.track = track_});
+ }
+ processor_->endStream(cancel);
+
+ // Clean up after ourselves.
+ stream_.reset();
+ codec_.reset();
+ track_.reset();
+}
+
+} // namespace audio
diff --git a/src/tangara/audio/audio_decoder.hpp b/src/tangara/audio/audio_decoder.hpp
new file mode 100644
index 00000000..64561d9d
--- /dev/null
+++ b/src/tangara/audio/audio_decoder.hpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <memory>
+
+#include "audio/audio_events.hpp"
+#include "audio/audio_sink.hpp"
+#include "audio/audio_source.hpp"
+#include "audio/processor.hpp"
+#include "codec.hpp"
+#include "database/track.hpp"
+#include "types.hpp"
+
+namespace audio {
+
+/*
+ * Handle to a persistent task that takes encoded bytes from arbitrary sources,
+ * decodes them into sample::Sample (normalised to 16 bit signed PCM), and then
+ * streams them onward to the sample processor.
+ */
+class Decoder {
+ public:
+ static auto Start(std::shared_ptr<SampleProcessor>) -> Decoder*;
+
+ auto open(std::shared_ptr<TaggedStream>) -> void;
+
+ Decoder(const Decoder&) = delete;
+ Decoder& operator=(const Decoder&) = delete;
+
+ private:
+ Decoder(std::shared_ptr<SampleProcessor>);
+
+ auto Main() -> void;
+
+ auto prepareDecode(std::shared_ptr<TaggedStream>) -> void;
+ auto continueDecode() -> bool;
+ auto finishDecode(bool cancel) -> void;
+
+ std::shared_ptr<SampleProcessor> processor_;
+
+ // Struct used with the next_stream_ queue.
+ struct NextStream {
+ std::shared_ptr<TaggedStream> stream;
+ };
+ QueueHandle_t next_stream_;
+
+ std::shared_ptr<codecs::IStream> stream_;
+ std::unique_ptr<codecs::ICodec> codec_;
+ std::shared_ptr<TrackInfo> track_;
+
+ std::span<sample::Sample> codec_buffer_;
+};
+
+} // namespace audio
diff --git a/src/audio/include/audio_events.hpp b/src/tangara/audio/audio_events.hpp
index b8a0dba6..f7eaba67 100644
--- a/src/audio/include/audio_events.hpp
+++ b/src/tangara/audio/audio_events.hpp
@@ -12,10 +12,10 @@
#include <optional>
#include <string>
-#include "audio_sink.hpp"
+#include "audio/audio_sink.hpp"
#include "tinyfsm.hpp"
-#include "track.hpp"
+#include "database/track.hpp"
#include "types.hpp"
namespace audio {
@@ -84,13 +84,6 @@ struct PlaybackUpdate : tinyfsm::Event {
struct SetTrack : tinyfsm::Event {
std::variant<std::string, database::TrackId, std::monostate> new_track;
std::optional<uint32_t> seek_to_second;
-
- enum Transition {
- kHardCut,
- kGapless,
- // TODO: kCrossFade
- };
- Transition transition;
};
struct TogglePlayPause : tinyfsm::Event {
@@ -138,17 +131,33 @@ struct OutputModeChanged : tinyfsm::Event {};
namespace internal {
+struct DecodingStarted : tinyfsm::Event {
+ std::shared_ptr<TrackInfo> track;
+};
+
+struct DecodingFailedToStart : tinyfsm::Event {
+ std::shared_ptr<TrackInfo> track;
+};
+
+struct DecodingCancelled : tinyfsm::Event {
+ std::shared_ptr<TrackInfo> track;
+};
+
+struct DecodingFinished : tinyfsm::Event {
+ std::shared_ptr<TrackInfo> track;
+};
+
struct StreamStarted : tinyfsm::Event {
std::shared_ptr<TrackInfo> track;
- IAudioOutput::Format src_format;
- IAudioOutput::Format dst_format;
+ IAudioOutput::Format sink_format;
+ uint32_t cue_at_sample;
};
-struct StreamUpdate : tinyfsm::Event {
- uint32_t samples_sunk;
+struct StreamEnded : tinyfsm::Event {
+ uint32_t cue_at_sample;
};
-struct StreamEnded : tinyfsm::Event {};
+struct StreamHeartbeat : tinyfsm::Event {};
} // namespace internal
diff --git a/src/audio/audio_fsm.cpp b/src/tangara/audio/audio_fsm.cpp
index ffb462f8..71f41938 100644
--- a/src/audio/audio_fsm.cpp
+++ b/src/tangara/audio/audio_fsm.cpp
@@ -4,15 +4,13 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "audio_fsm.hpp"
-#include <stdint.h>
+#include "audio/audio_fsm.hpp"
+#include <cstdint>
#include <future>
#include <memory>
#include <variant>
-#include "audio_sink.hpp"
-#include "bluetooth_types.hpp"
#include "cppbor.h"
#include "cppbor_parse.h"
#include "esp_heap_caps.h"
@@ -20,25 +18,28 @@
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "freertos/projdefs.h"
+#include "tinyfsm.hpp"
-#include "audio_converter.hpp"
-#include "audio_decoder.hpp"
-#include "audio_events.hpp"
-#include "bluetooth.hpp"
-#include "bt_audio_output.hpp"
-#include "event_queue.hpp"
-#include "fatfs_audio_input.hpp"
-#include "future_fetcher.hpp"
-#include "i2s_audio_output.hpp"
-#include "i2s_dac.hpp"
-#include "nvs.hpp"
+#include "audio/audio_decoder.hpp"
+#include "audio/audio_events.hpp"
+#include "audio/audio_sink.hpp"
+#include "audio/bt_audio_output.hpp"
+#include "audio/fatfs_stream_factory.hpp"
+#include "audio/i2s_audio_output.hpp"
+#include "audio/stream_cues.hpp"
+#include "audio/track_queue.hpp"
+#include "database/future_fetcher.hpp"
+#include "database/track.hpp"
+#include "drivers/bluetooth.hpp"
+#include "drivers/bluetooth_types.hpp"
+#include "drivers/i2s_dac.hpp"
+#include "drivers/nvs.hpp"
+#include "drivers/storage.hpp"
+#include "drivers/wm8523.hpp"
+#include "events/event_queue.hpp"
#include "sample.hpp"
-#include "service_locator.hpp"
-#include "system_events.hpp"
-#include "tinyfsm.hpp"
-#include "track.hpp"
-#include "track_queue.hpp"
-#include "wm8523.hpp"
+#include "system_fsm/service_locator.hpp"
+#include "system_fsm/system_events.hpp"
namespace audio {
@@ -46,12 +47,14 @@ namespace audio {
std::shared_ptr<system_fsm::ServiceLocator> AudioState::sServices;
-std::shared_ptr<FatfsAudioInput> AudioState::sFileSource;
+std::shared_ptr<FatfsStreamFactory> AudioState::sStreamFactory;
+
std::unique_ptr<Decoder> AudioState::sDecoder;
-std::shared_ptr<SampleConverter> AudioState::sSampleConverter;
+std::shared_ptr<SampleProcessor> AudioState::sSampleProcessor;
+
+std::shared_ptr<IAudioOutput> AudioState::sOutput;
std::shared_ptr<I2SAudioOutput> AudioState::sI2SOutput;
std::shared_ptr<BluetoothAudioOutput> AudioState::sBtOutput;
-std::shared_ptr<IAudioOutput> AudioState::sOutput;
// Two seconds of samples for two channels, at a representative sample rate.
constexpr size_t kDrainLatencySamples = 48000 * 2 * 2;
@@ -61,30 +64,33 @@ constexpr size_t kDrainBufferSize =
StreamBufferHandle_t AudioState::sDrainBuffer;
std::optional<IAudioOutput::Format> AudioState::sDrainFormat;
-std::shared_ptr<TrackInfo> AudioState::sCurrentTrack;
-uint64_t AudioState::sCurrentSamples;
-bool AudioState::sCurrentTrackIsFromQueue;
+StreamCues AudioState::sStreamCues;
-std::shared_ptr<TrackInfo> AudioState::sNextTrack;
-uint64_t AudioState::sNextTrackCueSamples;
-bool AudioState::sNextTrackIsFromQueue;
-
-bool AudioState::sIsResampling;
bool AudioState::sIsPaused = true;
-auto AudioState::currentPositionSeconds() -> std::optional<uint32_t> {
- if (!sCurrentTrack || !sDrainFormat) {
- return {};
+auto AudioState::emitPlaybackUpdate(bool paused) -> void {
+ std::optional<uint32_t> position;
+ auto current = sStreamCues.current();
+ if (current.first && sDrainFormat) {
+ position = (current.second /
+ (sDrainFormat->num_channels * sDrainFormat->sample_rate)) +
+ current.first->start_offset.value_or(0);
}
- return sCurrentSamples /
- (sDrainFormat->num_channels * sDrainFormat->sample_rate);
+
+ PlaybackUpdate event{
+ .current_track = current.first,
+ .track_position = position,
+ .paused = paused,
+ };
+
+ events::System().Dispatch(event);
+ events::Ui().Dispatch(event);
}
void AudioState::react(const QueueUpdate& ev) {
SetTrack cmd{
.new_track = std::monostate{},
.seek_to_second = {},
- .transition = SetTrack::Transition::kHardCut,
};
auto current = sServices->track_queue().current();
@@ -97,20 +103,13 @@ void AudioState::react(const QueueUpdate& ev) {
if (!ev.current_changed) {
return;
}
- sNextTrackIsFromQueue = true;
- cmd.transition = SetTrack::Transition::kHardCut;
break;
case QueueUpdate::kRepeatingLastTrack:
- sNextTrackIsFromQueue = true;
- cmd.transition = SetTrack::Transition::kGapless;
break;
case QueueUpdate::kTrackFinished:
if (!ev.current_changed) {
cmd.new_track = std::monostate{};
- } else {
- sNextTrackIsFromQueue = true;
}
- cmd.transition = SetTrack::Transition::kGapless;
break;
case QueueUpdate::kDeserialised:
default:
@@ -123,32 +122,9 @@ void AudioState::react(const QueueUpdate& ev) {
}
void AudioState::react(const SetTrack& ev) {
- // Remember the current track if there is one, since we need to preserve some
- // of the state if it turns out this SetTrack event corresponds to seeking
- // within the current track.
- std::string prev_uri;
- bool prev_from_queue = false;
- if (sCurrentTrack) {
- prev_uri = sCurrentTrack->uri;
- prev_from_queue = sCurrentTrackIsFromQueue;
- }
-
- if (ev.transition == SetTrack::Transition::kHardCut) {
- sCurrentTrack.reset();
- sCurrentSamples = 0;
- sCurrentTrackIsFromQueue = false;
- clearDrainBuffer();
- }
-
if (std::holds_alternative<std::monostate>(ev.new_track)) {
ESP_LOGI(kTag, "playback finished, awaiting drain");
- sFileSource->SetPath();
- awaitEmptyDrainBuffer();
- sCurrentTrack.reset();
- sDrainFormat.reset();
- sCurrentSamples = 0;
- sCurrentTrackIsFromQueue = false;
- transit<states::Standby>();
+ sDecoder->open({});
return;
}
@@ -157,96 +133,76 @@ void AudioState::react(const SetTrack& ev) {
auto new_track = ev.new_track;
uint32_t seek_to = ev.seek_to_second.value_or(0);
sServices->bg_worker().Dispatch<void>([=]() {
- std::optional<std::string> path;
+ std::shared_ptr<TaggedStream> stream;
if (std::holds_alternative<database::TrackId>(new_track)) {
- auto db = sServices->database().lock();
- if (db) {
- path = db->getTrackPath(std::get<database::TrackId>(new_track));
- }
+ stream = sStreamFactory->create(std::get<database::TrackId>(new_track),
+ seek_to);
} else if (std::holds_alternative<std::string>(new_track)) {
- path = std::get<std::string>(new_track);
+ stream =
+ sStreamFactory->create(std::get<std::string>(new_track), seek_to);
}
- if (path) {
- if (*path == prev_uri) {
- // This was a seek or replay within the same track; don't forget where
- // the track originally came from.
- sNextTrackIsFromQueue = prev_from_queue;
- }
- sFileSource->SetPath(*path, seek_to);
- } else {
- sFileSource->SetPath();
- }
+ sDecoder->open(stream);
});
}
void AudioState::react(const TogglePlayPause& ev) {
sIsPaused = !ev.set_to.value_or(sIsPaused);
- if (!sIsPaused && is_in_state<states::Standby>() && sCurrentTrack) {
+ if (!sIsPaused && is_in_state<states::Standby>() &&
+ sStreamCues.current().first) {
transit<states::Playback>();
} else if (sIsPaused && is_in_state<states::Playback>()) {
transit<states::Standby>();
}
}
-void AudioState::react(const internal::StreamStarted& ev) {
- sDrainFormat = ev.dst_format;
- sIsResampling = ev.src_format != ev.dst_format;
-
- sNextTrack = ev.track;
- sNextTrackCueSamples = sCurrentSamples + (kDrainLatencySamples / 2);
-
- ESP_LOGI(kTag, "new stream %s %u ch @ %lu hz (resample=%i)",
- ev.track->uri.c_str(), sDrainFormat->num_channels,
- sDrainFormat->sample_rate, sIsResampling);
-}
-
-void AudioState::react(const internal::StreamEnded&) {
- ESP_LOGI(kTag, "stream ended");
-
- if (sCurrentTrackIsFromQueue) {
- sServices->track_queue().finish();
- } else {
- tinyfsm::FsmList<AudioState>::dispatch(SetTrack{
- .new_track = std::monostate{},
- .seek_to_second = {},
- .transition = SetTrack::Transition::kGapless,
- });
- }
+void AudioState::react(const internal::DecodingFinished& ev) {
+ // If we just finished playing whatever's at the front of the queue, then we
+ // need to advanve and start playing the next one ASAP in order to continue
+ // gaplessly.
+ sServices->bg_worker().Dispatch<void>([=]() {
+ auto& queue = sServices->track_queue();
+ auto current = queue.current();
+ if (!current) {
+ return;
+ }
+ auto db = sServices->database().lock();
+ if (!db) {
+ return;
+ }
+ auto path = db->getTrackPath(*current);
+ if (!path) {
+ return;
+ }
+ if (*path == ev.track->uri) {
+ queue.finish();
+ }
+ });
}
-void AudioState::react(const internal::StreamUpdate& ev) {
- sCurrentSamples += ev.samples_sunk;
-
- if (sNextTrack && sCurrentSamples >= sNextTrackCueSamples) {
- ESP_LOGI(kTag, "next track is now sinking");
- sCurrentTrack = sNextTrack;
- sCurrentSamples -= sNextTrackCueSamples;
- sCurrentSamples += sNextTrack->start_offset.value_or(0) *
- (sDrainFormat->num_channels * sDrainFormat->sample_rate);
- sCurrentTrackIsFromQueue = sNextTrackIsFromQueue;
-
- sNextTrack.reset();
- sNextTrackCueSamples = 0;
- sNextTrackIsFromQueue = false;
+void AudioState::react(const internal::StreamStarted& ev) {
+ if (sDrainFormat != ev.sink_format) {
+ sDrainFormat = ev.sink_format;
+ ESP_LOGI(kTag, "sink_format=%u ch @ %lu hz", sDrainFormat->num_channels,
+ sDrainFormat->sample_rate);
}
- if (sCurrentTrack) {
- PlaybackUpdate event{
- .current_track = sCurrentTrack,
- .track_position = currentPositionSeconds(),
- .paused = !is_in_state<states::Playback>(),
- };
- events::System().Dispatch(event);
- events::Ui().Dispatch(event);
- }
+ sStreamCues.addCue(ev.track, ev.cue_at_sample);
- if (sCurrentTrack && !sIsPaused && !is_in_state<states::Playback>()) {
- ESP_LOGI(kTag, "ready to play!");
+ if (!sIsPaused && !is_in_state<states::Playback>()) {
transit<states::Playback>();
+ } else {
+ // Make sure everyone knows we've got a track ready to go, even if we're
+ // not playing it yet. This mostly matters when restoring the queue from
+ // disk after booting.
+ emitPlaybackUpdate(true);
}
}
+void AudioState::react(const internal::StreamEnded& ev) {
+ sStreamCues.addCue({}, ev.cue_at_sample);
+}
+
void AudioState::react(const system_fsm::BluetoothEvent& ev) {
if (ev.event != drivers::bluetooth::Event::kConnectionStateChanged) {
return;
@@ -282,14 +238,6 @@ void AudioState::react(const StepDownVolume& ev) {
}
}
-void AudioState::react(const system_fsm::HasPhonesChanged& ev) {
- if (ev.has_headphones) {
- ESP_LOGI(kTag, "headphones in!");
- } else {
- ESP_LOGI(kTag, "headphones out!");
- }
-}
-
void AudioState::react(const SetVolume& ev) {
if (ev.db.has_value()) {
if (sOutput->SetVolumeDb(ev.db.value())) {
@@ -349,7 +297,7 @@ void AudioState::react(const OutputModeChanged& ev) {
break;
}
sOutput->mode(IAudioOutput::Modes::kOnPaused);
- sSampleConverter->SetOutput(sOutput);
+ sSampleProcessor->SetOutput(sOutput);
// Bluetooth volume isn't 'changed' until we've connected to a device.
if (new_mode == drivers::NvsStorage::Output::kHeadphones) {
@@ -360,43 +308,6 @@ void AudioState::react(const OutputModeChanged& ev) {
}
}
-auto AudioState::clearDrainBuffer() -> void {
- // Tell the decoder to stop adding new samples. This might not take effect
- // immediately, since the decoder might currently be stuck waiting for space
- // to become available in the drain buffer.
- sFileSource->SetPath();
-
- auto mode = sOutput->mode();
- if (mode == IAudioOutput::Modes::kOnPlaying) {
- // If we're currently playing, then the drain buffer will be actively
- // draining on its own. Just keep trying to reset until it works.
- while (xStreamBufferReset(sDrainBuffer) != pdPASS) {
- }
- } else {
- // If we're not currently playing, then we need to actively pull samples
- // out of the drain buffer to unblock the decoder.
- while (!xStreamBufferIsEmpty(sDrainBuffer)) {
- // Read a little to unblock the decoder.
- uint8_t drain[2048];
- xStreamBufferReceive(sDrainBuffer, drain, sizeof(drain), 0);
-
- // Try to quickly discard the rest.
- xStreamBufferReset(sDrainBuffer);
- }
- }
-}
-
-auto AudioState::awaitEmptyDrainBuffer() -> void {
- if (is_in_state<states::Playback>()) {
- for (int i = 0; i < 10 && !xStreamBufferIsEmpty(sDrainBuffer); i++) {
- vTaskDelay(pdMS_TO_TICKS(250));
- }
- }
- if (!xStreamBufferIsEmpty(sDrainBuffer)) {
- clearDrainBuffer();
- }
-}
-
auto AudioState::commitVolume() -> void {
auto mode = sServices->nvs().OutputMode();
auto vol = sOutput->GetVolume();
@@ -427,8 +338,7 @@ void Uninitialised::react(const system_fsm::BootComplete& ev) {
sDrainBuffer = xStreamBufferCreateStatic(
kDrainBufferSize, sizeof(sample::Sample), storage, meta);
- sFileSource.reset(
- new FatfsAudioInput(sServices->tag_parser(), sServices->bg_worker()));
+ sStreamFactory.reset(new FatfsStreamFactory(*sServices));
sI2SOutput.reset(new I2SAudioOutput(sDrainBuffer, sServices->gpios()));
sBtOutput.reset(new BluetoothAudioOutput(sDrainBuffer, sServices->bluetooth(),
sServices->bg_worker()));
@@ -462,10 +372,10 @@ void Uninitialised::react(const system_fsm::BootComplete& ev) {
.left_bias = nvs.AmpLeftBias(),
});
- sSampleConverter.reset(new SampleConverter());
- sSampleConverter->SetOutput(sOutput);
+ sSampleProcessor.reset(new SampleProcessor(sDrainBuffer));
+ sSampleProcessor->SetOutput(sOutput);
- Decoder::Start(sFileSource, sSampleConverter);
+ sDecoder.reset(Decoder::Start(sSampleProcessor));
transit<Standby>();
}
@@ -477,7 +387,8 @@ void Standby::react(const system_fsm::KeyLockChanged& ev) {
if (!ev.locking) {
return;
}
- sServices->bg_worker().Dispatch<void>([this]() {
+ auto current = sStreamCues.current();
+ sServices->bg_worker().Dispatch<void>([=]() {
auto db = sServices->database().lock();
if (!db) {
return;
@@ -490,17 +401,24 @@ void Standby::react(const system_fsm::KeyLockChanged& ev) {
}
db->put(kQueueKey, queue.serialise());
- if (sCurrentTrack) {
+ if (current.first && sDrainFormat) {
+ uint32_t seconds = (current.second / (sDrainFormat->num_channels *
+ sDrainFormat->sample_rate)) +
+ current.first->start_offset.value_or(0);
cppbor::Array current_track{
- cppbor::Tstr{sCurrentTrack->uri},
- cppbor::Uint{currentPositionSeconds().value_or(0)},
+ cppbor::Tstr{current.first->uri},
+ cppbor::Uint{seconds},
};
db->put(kCurrentFileKey, current_track.toString());
}
});
}
-void Standby::react(const system_fsm::StorageMounted& ev) {
+void Standby::react(const system_fsm::SdStateChanged& ev) {
+ auto state = sServices->sd();
+ if (state != drivers::SdState::kMounted) {
+ return;
+ }
sServices->bg_worker().Dispatch<void>([]() {
auto db = sServices->database().lock();
if (!db) {
@@ -524,7 +442,6 @@ void Standby::react(const system_fsm::StorageMounted& ev) {
events::Audio().Dispatch(SetTrack{
.new_track = filename,
.seek_to_second = pos,
- .transition = SetTrack::Transition::kHardCut,
});
}
}
@@ -540,32 +457,47 @@ void Standby::react(const system_fsm::StorageMounted& ev) {
});
}
+static TimerHandle_t sHeartbeatTimer;
+
+static void heartbeat(TimerHandle_t) {
+ events::Audio().Dispatch(internal::StreamHeartbeat{});
+}
+
void Playback::entry() {
ESP_LOGI(kTag, "audio output resumed");
sOutput->mode(IAudioOutput::Modes::kOnPlaying);
+ emitPlaybackUpdate(false);
- PlaybackUpdate event{
- .current_track = sCurrentTrack,
- .track_position = currentPositionSeconds(),
- .paused = false,
- };
-
- events::System().Dispatch(event);
- events::Ui().Dispatch(event);
+ if (!sHeartbeatTimer) {
+ sHeartbeatTimer =
+ xTimerCreate("stream", pdMS_TO_TICKS(250), true, NULL, heartbeat);
+ }
+ xTimerStart(sHeartbeatTimer, portMAX_DELAY);
}
void Playback::exit() {
ESP_LOGI(kTag, "audio output paused");
+ xTimerStop(sHeartbeatTimer, portMAX_DELAY);
sOutput->mode(IAudioOutput::Modes::kOnPaused);
+ emitPlaybackUpdate(true);
+}
- PlaybackUpdate event{
- .current_track = sCurrentTrack,
- .track_position = currentPositionSeconds(),
- .paused = true,
- };
+void Playback::react(const system_fsm::SdStateChanged& ev) {
+ if (sServices->sd() != drivers::SdState::kMounted) {
+ transit<Standby>();
+ }
+}
- events::System().Dispatch(event);
- events::Ui().Dispatch(event);
+void Playback::react(const internal::StreamHeartbeat& ev) {
+ sStreamCues.update(sOutput->samplesUsed());
+
+ if (sStreamCues.hasStream()) {
+ emitPlaybackUpdate(false);
+ } else {
+ // Finished the current stream, and there's nothing upcoming. We must be
+ // finished.
+ transit<Standby>();
+ }
}
} // namespace states
diff --git a/src/audio/include/audio_fsm.hpp b/src/tangara/audio/audio_fsm.hpp
index 60afb321..03aaddcb 100644
--- a/src/audio/include/audio_fsm.hpp
+++ b/src/tangara/audio/audio_fsm.hpp
@@ -11,24 +11,25 @@
#include <memory>
#include <vector>
-#include "audio_sink.hpp"
-#include "service_locator.hpp"
+#include "audio/stream_cues.hpp"
#include "tinyfsm.hpp"
-#include "audio_decoder.hpp"
-#include "audio_events.hpp"
-#include "bt_audio_output.hpp"
-#include "database.hpp"
-#include "display.hpp"
-#include "fatfs_audio_input.hpp"
-#include "gpios.hpp"
-#include "i2s_audio_output.hpp"
-#include "i2s_dac.hpp"
-#include "storage.hpp"
-#include "system_events.hpp"
-#include "tag_parser.hpp"
-#include "track.hpp"
-#include "track_queue.hpp"
+#include "audio/audio_decoder.hpp"
+#include "audio/audio_events.hpp"
+#include "audio/audio_sink.hpp"
+#include "audio/bt_audio_output.hpp"
+#include "audio/fatfs_stream_factory.hpp"
+#include "audio/i2s_audio_output.hpp"
+#include "audio/track_queue.hpp"
+#include "database/database.hpp"
+#include "database/tag_parser.hpp"
+#include "database/track.hpp"
+#include "drivers/display.hpp"
+#include "drivers/gpios.hpp"
+#include "drivers/i2s_dac.hpp"
+#include "drivers/storage.hpp"
+#include "system_fsm/service_locator.hpp"
+#include "system_fsm/system_events.hpp"
namespace audio {
@@ -46,13 +47,13 @@ class AudioState : public tinyfsm::Fsm<AudioState> {
void react(const SetTrack&);
void react(const TogglePlayPause&);
+ void react(const internal::DecodingFinished&);
void react(const internal::StreamStarted&);
- void react(const internal::StreamUpdate&);
void react(const internal::StreamEnded&);
+ virtual void react(const internal::StreamHeartbeat&) {}
void react(const StepUpVolume&);
void react(const StepDownVolume&);
- virtual void react(const system_fsm::HasPhonesChanged&);
void react(const SetVolume&);
void react(const SetVolumeLimit&);
@@ -62,40 +63,28 @@ class AudioState : public tinyfsm::Fsm<AudioState> {
virtual void react(const system_fsm::BootComplete&) {}
virtual void react(const system_fsm::KeyLockChanged&){};
- virtual void react(const system_fsm::StorageMounted&) {}
+ virtual void react(const system_fsm::SdStateChanged&) {}
virtual void react(const system_fsm::BluetoothEvent&);
protected:
- auto clearDrainBuffer() -> void;
- auto awaitEmptyDrainBuffer() -> void;
-
- auto playTrack(database::TrackId id) -> void;
+ auto emitPlaybackUpdate(bool paused) -> void;
auto commitVolume() -> void;
static std::shared_ptr<system_fsm::ServiceLocator> sServices;
- static std::shared_ptr<FatfsAudioInput> sFileSource;
+ static std::shared_ptr<FatfsStreamFactory> sStreamFactory;
static std::unique_ptr<Decoder> sDecoder;
- static std::shared_ptr<SampleConverter> sSampleConverter;
+ static std::shared_ptr<SampleProcessor> sSampleProcessor;
static std::shared_ptr<I2SAudioOutput> sI2SOutput;
static std::shared_ptr<BluetoothAudioOutput> sBtOutput;
static std::shared_ptr<IAudioOutput> sOutput;
static StreamBufferHandle_t sDrainBuffer;
- static std::shared_ptr<TrackInfo> sCurrentTrack;
- static uint64_t sCurrentSamples;
+ static StreamCues sStreamCues;
static std::optional<IAudioOutput::Format> sDrainFormat;
- static bool sCurrentTrackIsFromQueue;
-
- static std::shared_ptr<TrackInfo> sNextTrack;
- static uint64_t sNextTrackCueSamples;
- static bool sNextTrackIsFromQueue;
- static bool sIsResampling;
static bool sIsPaused;
-
- auto currentPositionSeconds() -> std::optional<uint32_t>;
};
namespace states {
@@ -111,7 +100,7 @@ class Uninitialised : public AudioState {
class Standby : public AudioState {
public:
void react(const system_fsm::KeyLockChanged&) override;
- void react(const system_fsm::StorageMounted&) override;
+ void react(const system_fsm::SdStateChanged&) override;
using AudioState::react;
};
@@ -121,6 +110,9 @@ class Playback : public AudioState {
void entry() override;
void exit() override;
+ void react(const system_fsm::SdStateChanged&) override;
+ void react(const internal::StreamHeartbeat&) override;
+
using AudioState::react;
};
diff --git a/src/audio/include/audio_sink.hpp b/src/tangara/audio/audio_sink.hpp
index f31d0d75..0b133a8d 100644
--- a/src/audio/include/audio_sink.hpp
+++ b/src/tangara/audio/audio_sink.hpp
@@ -75,6 +75,7 @@ class IAudioOutput {
virtual auto PrepareFormat(const Format&) -> Format = 0;
virtual auto Configure(const Format& format) -> void = 0;
+ virtual auto samplesUsed() -> uint32_t = 0;
auto stream() -> StreamBufferHandle_t { return stream_; }
diff --git a/src/audio/audio_source.cpp b/src/tangara/audio/audio_source.cpp
index d9e8e04a..4989c470 100644
--- a/src/audio/audio_source.cpp
+++ b/src/tangara/audio/audio_source.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "audio_source.hpp"
+#include "audio/audio_source.hpp"
#include "codec.hpp"
#include "types.hpp"
@@ -14,13 +14,17 @@ TaggedStream::TaggedStream(std::shared_ptr<database::TrackTags> t,
std::unique_ptr<codecs::IStream> w,
std::string filepath,
uint32_t offset)
- : codecs::IStream(w->type()), tags_(t), wrapped_(std::move(w)), filepath_(filepath), offset_(offset) {}
+ : codecs::IStream(w->type()),
+ tags_(t),
+ wrapped_(std::move(w)),
+ filepath_(filepath),
+ offset_(offset) {}
auto TaggedStream::tags() -> std::shared_ptr<database::TrackTags> {
return tags_;
}
-auto TaggedStream::Read(cpp::span<std::byte> dest) -> ssize_t {
+auto TaggedStream::Read(std::span<std::byte> dest) -> ssize_t {
return wrapped_->Read(dest);
}
diff --git a/src/audio/include/audio_source.hpp b/src/tangara/audio/audio_source.hpp
index b38acd7a..2433da46 100644
--- a/src/audio/include/audio_source.hpp
+++ b/src/tangara/audio/audio_source.hpp
@@ -8,7 +8,7 @@
#include <memory>
#include "codec.hpp"
-#include "track.hpp"
+#include "database/track.hpp"
#include "types.hpp"
namespace audio {
@@ -18,12 +18,11 @@ class TaggedStream : public codecs::IStream {
TaggedStream(std::shared_ptr<database::TrackTags>,
std::unique_ptr<codecs::IStream> wrapped,
std::string path,
- uint32_t offset = 0
- );
+ uint32_t offset = 0);
auto tags() -> std::shared_ptr<database::TrackTags>;
- auto Read(cpp::span<std::byte> dest) -> ssize_t override;
+ auto Read(std::span<std::byte> dest) -> ssize_t override;
auto CanSeek() -> bool override;
diff --git a/src/audio/bt_audio_output.cpp b/src/tangara/audio/bt_audio_output.cpp
index 229a38bb..f1b4c26c 100644
--- a/src/audio/bt_audio_output.cpp
+++ b/src/tangara/audio/bt_audio_output.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "bt_audio_output.hpp"
+#include "audio/bt_audio_output.hpp"
#include <algorithm>
#include <cmath>
@@ -18,12 +18,12 @@
#include "freertos/portmacro.h"
#include "freertos/projdefs.h"
-#include "gpios.hpp"
-#include "i2c.hpp"
-#include "i2s_dac.hpp"
+#include "drivers/gpios.hpp"
+#include "drivers/i2c.hpp"
+#include "drivers/i2s_dac.hpp"
+#include "drivers/wm8523.hpp"
#include "result.hpp"
#include "tasks.hpp"
-#include "wm8523.hpp"
[[maybe_unused]] static const char* kTag = "BTOUT";
@@ -121,4 +121,8 @@ auto BluetoothAudioOutput::Configure(const Format& fmt) -> void {
// No configuration necessary; the output format is fixed.
}
+auto BluetoothAudioOutput::samplesUsed() -> uint32_t {
+ return bluetooth_.SamplesUsed();
+}
+
} // namespace audio
diff --git a/src/audio/include/bt_audio_output.hpp b/src/tangara/audio/bt_audio_output.hpp
index cc3b2462..c5681f9a 100644
--- a/src/audio/include/bt_audio_output.hpp
+++ b/src/tangara/audio/bt_audio_output.hpp
@@ -13,10 +13,10 @@
#include "result.hpp"
-#include "audio_sink.hpp"
-#include "bluetooth.hpp"
-#include "gpios.hpp"
-#include "i2s_dac.hpp"
+#include "audio/audio_sink.hpp"
+#include "drivers/bluetooth.hpp"
+#include "drivers/gpios.hpp"
+#include "drivers/i2s_dac.hpp"
#include "tasks.hpp"
namespace audio {
@@ -45,6 +45,8 @@ class BluetoothAudioOutput : public IAudioOutput {
auto PrepareFormat(const Format&) -> Format override;
auto Configure(const Format& format) -> void override;
+ auto samplesUsed() -> uint32_t override;
+
BluetoothAudioOutput(const BluetoothAudioOutput&) = delete;
BluetoothAudioOutput& operator=(const BluetoothAudioOutput&) = delete;
diff --git a/src/audio/fatfs_source.cpp b/src/tangara/audio/fatfs_source.cpp
index 72c3940d..fb6a684d 100644
--- a/src/audio/fatfs_source.cpp
+++ b/src/tangara/audio/fatfs_source.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "fatfs_source.hpp"
+#include "audio/fatfs_source.hpp"
#include <sys/_stdint.h>
#include <cstddef>
@@ -12,13 +12,13 @@
#include <memory>
#include "esp_log.h"
-#include "event_queue.hpp"
+#include "events/event_queue.hpp"
#include "ff.h"
-#include "audio_source.hpp"
+#include "audio/audio_source.hpp"
#include "codec.hpp"
-#include "spi.hpp"
-#include "system_events.hpp"
+#include "drivers/spi.hpp"
+#include "system_fsm/system_events.hpp"
#include "types.hpp"
namespace audio {
@@ -33,7 +33,7 @@ FatfsSource::~FatfsSource() {
f_close(file_.get());
}
-auto FatfsSource::Read(cpp::span<std::byte> dest) -> ssize_t {
+auto FatfsSource::Read(std::span<std::byte> dest) -> ssize_t {
auto lock = drivers::acquire_spi();
if (f_eof(file_.get())) {
return 0;
diff --git a/src/audio/include/fatfs_source.hpp b/src/tangara/audio/fatfs_source.hpp
index 45ab34c6..32650880 100644
--- a/src/audio/include/fatfs_source.hpp
+++ b/src/tangara/audio/fatfs_source.hpp
@@ -13,7 +13,7 @@
#include "codec.hpp"
#include "ff.h"
-#include "audio_source.hpp"
+#include "audio/audio_source.hpp"
namespace audio {
@@ -26,7 +26,7 @@ class FatfsSource : public codecs::IStream {
FatfsSource(codecs::StreamType, std::unique_ptr<FIL> file);
~FatfsSource();
- auto Read(cpp::span<std::byte> dest) -> ssize_t override;
+ auto Read(std::span<std::byte> dest) -> ssize_t override;
auto CanSeek() -> bool override;
diff --git a/src/tangara/audio/fatfs_stream_factory.cpp b/src/tangara/audio/fatfs_stream_factory.cpp
new file mode 100644
index 00000000..db08e68c
--- /dev/null
+++ b/src/tangara/audio/fatfs_stream_factory.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "audio/fatfs_stream_factory.hpp"
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "database/database.hpp"
+#include "esp_log.h"
+#include "ff.h"
+#include "freertos/portmacro.h"
+#include "freertos/projdefs.h"
+
+#include "audio/audio_source.hpp"
+#include "audio/fatfs_source.hpp"
+#include "codec.hpp"
+#include "database/tag_parser.hpp"
+#include "database/track.hpp"
+#include "drivers/spi.hpp"
+#include "system_fsm/service_locator.hpp"
+#include "tasks.hpp"
+#include "types.hpp"
+
+[[maybe_unused]] static const char* kTag = "SRC";
+
+namespace audio {
+
+FatfsStreamFactory::FatfsStreamFactory(system_fsm::ServiceLocator& services)
+ : services_(services) {}
+
+auto FatfsStreamFactory::create(database::TrackId id, uint32_t offset)
+ -> std::shared_ptr<TaggedStream> {
+ auto db = services_.database().lock();
+ if (!db) {
+ return {};
+ }
+ auto path = db->getTrackPath(id);
+ if (!path) {
+ return {};
+ }
+ return create(*path, offset);
+}
+
+auto FatfsStreamFactory::create(std::string path, uint32_t offset)
+ -> std::shared_ptr<TaggedStream> {
+ auto tags = services_.tag_parser().ReadAndParseTags(path);
+ if (!tags) {
+ ESP_LOGE(kTag, "failed to read tags");
+ return {};
+ }
+
+ if (!tags->title()) {
+ tags->title(path);
+ }
+
+ auto stream_type = ContainerToStreamType(tags->encoding());
+ if (!stream_type.has_value()) {
+ ESP_LOGE(kTag, "couldn't match container to stream");
+ return {};
+ }
+
+ std::unique_ptr<FIL> file = std::make_unique<FIL>();
+ FRESULT res;
+
+ {
+ auto lock = drivers::acquire_spi();
+ res = f_open(file.get(), path.c_str(), FA_READ);
+ }
+
+ if (res != FR_OK) {
+ ESP_LOGE(kTag, "failed to open file! res: %i", res);
+ return {};
+ }
+
+ return std::make_shared<TaggedStream>(
+ tags, std::make_unique<FatfsSource>(stream_type.value(), std::move(file)),
+ path, offset);
+}
+
+auto FatfsStreamFactory::ContainerToStreamType(database::Container enc)
+ -> std::optional<codecs::StreamType> {
+ switch (enc) {
+ case database::Container::kMp3:
+ return codecs::StreamType::kMp3;
+ case database::Container::kWav:
+ return codecs::StreamType::kWav;
+ case database::Container::kOgg:
+ return codecs::StreamType::kVorbis;
+ case database::Container::kFlac:
+ return codecs::StreamType::kFlac;
+ case database::Container::kOpus:
+ return codecs::StreamType::kOpus;
+ case database::Container::kUnsupported:
+ default:
+ return {};
+ }
+}
+
+} // namespace audio
diff --git a/src/tangara/audio/fatfs_stream_factory.hpp b/src/tangara/audio/fatfs_stream_factory.hpp
new file mode 100644
index 00000000..858d2131
--- /dev/null
+++ b/src/tangara/audio/fatfs_stream_factory.hpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <cstddef>
+#include <cstdint>
+#include <future>
+#include <memory>
+#include <string>
+
+#include "database/database.hpp"
+#include "database/track.hpp"
+#include "ff.h"
+#include "freertos/portmacro.h"
+
+#include "audio/audio_source.hpp"
+#include "codec.hpp"
+#include "database/future_fetcher.hpp"
+#include "database/tag_parser.hpp"
+#include "system_fsm/service_locator.hpp"
+#include "tasks.hpp"
+#include "types.hpp"
+
+namespace audio {
+
+/*
+ * Utility to create streams that read from files on the sd card.
+ */
+class FatfsStreamFactory {
+ public:
+ explicit FatfsStreamFactory(system_fsm::ServiceLocator&);
+
+ auto create(database::TrackId, uint32_t offset = 0)
+ -> std::shared_ptr<TaggedStream>;
+ auto create(std::string, uint32_t offset = 0)
+ -> std::shared_ptr<TaggedStream>;
+
+ FatfsStreamFactory(const FatfsStreamFactory&) = delete;
+ FatfsStreamFactory& operator=(const FatfsStreamFactory&) = delete;
+
+ private:
+ auto ContainerToStreamType(database::Container)
+ -> std::optional<codecs::StreamType>;
+
+ system_fsm::ServiceLocator& services_;
+};
+
+} // namespace audio
diff --git a/src/audio/i2s_audio_output.cpp b/src/tangara/audio/i2s_audio_output.cpp
index bf1c3e5e..684bfa92 100644
--- a/src/audio/i2s_audio_output.cpp
+++ b/src/tangara/audio/i2s_audio_output.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "i2s_audio_output.hpp"
+#include "audio/i2s_audio_output.hpp"
#include <stdint.h>
#include <algorithm>
@@ -18,12 +18,12 @@
#include "freertos/portmacro.h"
#include "freertos/projdefs.h"
-#include "audio_sink.hpp"
-#include "gpios.hpp"
-#include "i2c.hpp"
-#include "i2s_dac.hpp"
+#include "audio/audio_sink.hpp"
+#include "drivers/gpios.hpp"
+#include "drivers/i2c.hpp"
+#include "drivers/i2s_dac.hpp"
+#include "drivers/wm8523.hpp"
#include "result.hpp"
-#include "wm8523.hpp"
[[maybe_unused]] static const char* kTag = "I2SOUT";
@@ -120,7 +120,7 @@ auto I2SAudioOutput::SetVolumePct(uint_fast8_t val) -> bool {
if (val > 100) {
return false;
}
- uint16_t vol = (val * (max_volume_ - kMinVolume))/100 + kMinVolume;
+ uint16_t vol = (val * (max_volume_ - kMinVolume)) / 100 + kMinVolume;
SetVolume(vol);
return true;
}
@@ -133,7 +133,8 @@ auto I2SAudioOutput::GetVolumeDb() -> int_fast16_t {
}
auto I2SAudioOutput::SetVolumeDb(int_fast16_t val) -> bool {
- SetVolume(val * 4 + static_cast<int>(drivers::wm8523::kLineLevelReferenceVolume) - 2);
+ SetVolume(val * 4 +
+ static_cast<int>(drivers::wm8523::kLineLevelReferenceVolume) - 2);
return true;
}
@@ -229,4 +230,8 @@ auto I2SAudioOutput::Configure(const Format& fmt) -> void {
current_config_ = fmt;
}
+auto I2SAudioOutput::samplesUsed() -> uint32_t {
+ return dac_->SamplesUsed();
+}
+
} // namespace audio
diff --git a/src/audio/include/i2s_audio_output.hpp b/src/tangara/audio/i2s_audio_output.hpp
index 7954257a..bd7d62fb 100644
--- a/src/audio/include/i2s_audio_output.hpp
+++ b/src/tangara/audio/i2s_audio_output.hpp
@@ -11,9 +11,9 @@
#include <memory>
#include <vector>
-#include "audio_sink.hpp"
-#include "gpios.hpp"
-#include "i2s_dac.hpp"
+#include "audio/audio_sink.hpp"
+#include "drivers/gpios.hpp"
+#include "drivers/i2s_dac.hpp"
#include "result.hpp"
namespace audio {
@@ -43,6 +43,8 @@ class I2SAudioOutput : public IAudioOutput {
auto PrepareFormat(const Format&) -> Format override;
auto Configure(const Format& format) -> void override;
+ auto samplesUsed() -> uint32_t override;
+
I2SAudioOutput(const I2SAudioOutput&) = delete;
I2SAudioOutput& operator=(const I2SAudioOutput&) = delete;
diff --git a/src/audio/audio_converter.cpp b/src/tangara/audio/processor.cpp
index eb1cde80..dd96c892 100644
--- a/src/audio/audio_converter.cpp
+++ b/src/tangara/audio/processor.cpp
@@ -4,23 +4,24 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "audio_converter.hpp"
+#include "audio/processor.hpp"
#include <stdint.h>
#include <algorithm>
#include <cmath>
#include <cstdint>
+#include <limits>
-#include "audio_events.hpp"
-#include "audio_sink.hpp"
+#include "audio/audio_events.hpp"
+#include "audio/audio_sink.hpp"
+#include "drivers/i2s_dac.hpp"
#include "esp_heap_caps.h"
#include "esp_log.h"
-#include "event_queue.hpp"
+#include "events/event_queue.hpp"
#include "freertos/portmacro.h"
#include "freertos/projdefs.h"
-#include "i2s_dac.hpp"
-#include "resample.hpp"
+#include "audio/resample.hpp"
#include "sample.hpp"
#include "tasks.hpp"
@@ -32,14 +33,15 @@ static constexpr std::size_t kSourceBufferLength = kSampleBufferLength * 2;
namespace audio {
-SampleConverter::SampleConverter()
+SampleProcessor::SampleProcessor(StreamBufferHandle_t sink)
: commands_(xQueueCreate(1, sizeof(Args))),
resampler_(nullptr),
source_(xStreamBufferCreateWithCaps(kSourceBufferLength,
sizeof(sample::Sample) * 2,
MALLOC_CAP_DMA)),
+ sink_(sink),
leftover_bytes_(0),
- samples_sunk_(0) {
+ samples_written_(0) {
input_buffer_ = {
reinterpret_cast<sample::Sample*>(heap_caps_calloc(
kSampleBufferLength, sizeof(sample::Sample), MALLOC_CAP_DMA)),
@@ -55,47 +57,52 @@ SampleConverter::SampleConverter()
tasks::StartPersistent<tasks::Type::kAudioConverter>([&]() { Main(); });
}
-SampleConverter::~SampleConverter() {
+SampleProcessor::~SampleProcessor() {
vQueueDelete(commands_);
vStreamBufferDelete(source_);
}
-auto SampleConverter::SetOutput(std::shared_ptr<IAudioOutput> output) -> void {
- // FIXME: We should add synchronisation here, but we should be careful about
- // not impacting performance given that the output will change only very
- // rarely (if ever).
- sink_ = output;
+auto SampleProcessor::SetOutput(std::shared_ptr<IAudioOutput> output) -> void {
+ assert(xStreamBufferIsEmpty(sink_));
+ // FIXME: We should add synchronisation here, but we should be careful
+ // about not impacting performance given that the output will change only
+ // very rarely (if ever).
+ output_ = output;
+ samples_written_ = output_->samplesUsed();
}
-auto SampleConverter::beginStream(std::shared_ptr<TrackInfo> track) -> void {
+auto SampleProcessor::beginStream(std::shared_ptr<TrackInfo> track) -> void {
Args args{
.track = new std::shared_ptr<TrackInfo>(track),
.samples_available = 0,
.is_end_of_stream = false,
+ .clear_buffers = false,
};
xQueueSend(commands_, &args, portMAX_DELAY);
}
-auto SampleConverter::continueStream(cpp::span<sample::Sample> input) -> void {
+auto SampleProcessor::continueStream(std::span<sample::Sample> input) -> void {
Args args{
.track = nullptr,
.samples_available = input.size(),
.is_end_of_stream = false,
+ .clear_buffers = false,
};
xQueueSend(commands_, &args, portMAX_DELAY);
xStreamBufferSend(source_, input.data(), input.size_bytes(), portMAX_DELAY);
}
-auto SampleConverter::endStream() -> void {
+auto SampleProcessor::endStream(bool cancelled) -> void {
Args args{
.track = nullptr,
.samples_available = 0,
.is_end_of_stream = true,
+ .clear_buffers = cancelled,
};
xQueueSend(commands_, &args, portMAX_DELAY);
}
-auto SampleConverter::Main() -> void {
+auto SampleProcessor::Main() -> void {
for (;;) {
Args args;
while (!xQueueReceive(commands_, &args, portMAX_DELAY)) {
@@ -109,43 +116,44 @@ auto SampleConverter::Main() -> void {
handleContinueStream(args.samples_available);
}
if (args.is_end_of_stream) {
- handleEndStream();
+ handleEndStream(args.clear_buffers);
}
}
}
-auto SampleConverter::handleBeginStream(std::shared_ptr<TrackInfo> track)
+auto SampleProcessor::handleBeginStream(std::shared_ptr<TrackInfo> track)
-> void {
if (track->format != source_format_) {
- resampler_.reset();
source_format_ = track->format;
+ // The new stream has a different format to the previous stream (or there
+ // was no previous stream).
+ // First, clean up our filters.
+ resampler_.reset();
leftover_bytes_ = 0;
- auto new_target = sink_->PrepareFormat(track->format);
- if (new_target != target_format_) {
- // The new format is different to the old one. Wait for the sink to
- // drain before continuing.
- while (!xStreamBufferIsEmpty(sink_->stream())) {
- ESP_LOGI(kTag, "waiting for sink stream to drain...");
- // TODO(jacqueline): Get the sink drain ISR to notify us of this
- // via semaphore instead of busy-ish waiting.
- vTaskDelay(pdMS_TO_TICKS(10));
- }
-
- sink_->Configure(new_target);
+ // If the output is idle, then we can reconfigure it to the closest format
+ // to our new source.
+ // If the output *wasn't* idle, then we can't reconfigure without an
+ // audible gap in playback. So instead, we simply keep the same target
+ // format and begin resampling.
+ if (xStreamBufferIsEmpty(sink_)) {
+ target_format_ = output_->PrepareFormat(track->format);
+ output_->Configure(target_format_);
}
- target_format_ = new_target;
}
- samples_sunk_ = 0;
+ if (xStreamBufferIsEmpty(sink_)) {
+ samples_written_ = output_->samplesUsed();
+ }
+
events::Audio().Dispatch(internal::StreamStarted{
.track = track,
- .src_format = source_format_,
- .dst_format = target_format_,
+ .sink_format = target_format_,
+ .cue_at_sample = samples_written_,
});
}
-auto SampleConverter::handleContinueStream(size_t samples_available) -> void {
+auto SampleProcessor::handleContinueStream(size_t samples_available) -> void {
// Loop until we finish reading all the bytes indicated. There might be
// leftovers from each iteration, and from this process as a whole,
// depending on the resampling stage.
@@ -182,7 +190,7 @@ auto SampleConverter::handleContinueStream(size_t samples_available) -> void {
}
}
-auto SampleConverter::handleSamples(cpp::span<sample::Sample> input) -> size_t {
+auto SampleProcessor::handleSamples(std::span<sample::Sample> input) -> size_t {
if (source_format_ == target_format_) {
// The happiest possible case: the input format matches the output
// format already.
@@ -192,7 +200,7 @@ auto SampleConverter::handleSamples(cpp::span<sample::Sample> input) -> size_t {
size_t samples_used = 0;
while (samples_used < input.size()) {
- cpp::span<sample::Sample> output_source;
+ std::span<sample::Sample> output_source;
if (source_format_.sample_rate != target_format_.sample_rate) {
if (resampler_ == nullptr) {
ESP_LOGI(kTag, "creating new resampler for %lu -> %lu",
@@ -223,8 +231,8 @@ auto SampleConverter::handleSamples(cpp::span<sample::Sample> input) -> size_t {
return samples_used;
}
-auto SampleConverter::handleEndStream() -> void {
- if (resampler_) {
+auto SampleProcessor::handleEndStream(bool clear_bufs) -> void {
+ if (resampler_ && !clear_bufs) {
size_t read, written;
std::tie(read, written) = resampler_->Process({}, resampled_buffer_, true);
@@ -233,33 +241,31 @@ auto SampleConverter::handleEndStream() -> void {
}
}
- // Send a final update to finish off this stream's samples.
- if (samples_sunk_ > 0) {
- events::Audio().Dispatch(internal::StreamUpdate{
- .samples_sunk = samples_sunk_,
- });
- samples_sunk_ = 0;
+ if (clear_bufs) {
+ assert(xStreamBufferReset(sink_));
+ samples_written_ = output_->samplesUsed();
}
+
+ // FIXME: This discards any leftover samples, but there probably shouldn't be
+ // any leftover samples. Can this be an assert instead?
leftover_bytes_ = 0;
- events::Audio().Dispatch(internal::StreamEnded{});
+ events::Audio().Dispatch(internal::StreamEnded{
+ .cue_at_sample = samples_written_,
+ });
}
-auto SampleConverter::sendToSink(cpp::span<sample::Sample> samples) -> void {
- // Update the number of samples sunk so far *before* actually sinking them,
- // since writing to the stream buffer will block when the buffer gets full.
- samples_sunk_ += samples.size();
- if (samples_sunk_ >=
- target_format_.sample_rate * target_format_.num_channels) {
- events::Audio().Dispatch(internal::StreamUpdate{
- .samples_sunk = samples_sunk_,
- });
- samples_sunk_ = 0;
- }
+auto SampleProcessor::sendToSink(std::span<sample::Sample> samples) -> void {
+ auto data = std::as_bytes(samples);
+ xStreamBufferSend(sink_, data.data(), data.size(), portMAX_DELAY);
- xStreamBufferSend(sink_->stream(),
- reinterpret_cast<std::byte*>(samples.data()),
- samples.size_bytes(), portMAX_DELAY);
+ uint32_t samples_before_overflow =
+ std::numeric_limits<uint32_t>::max() - samples_written_;
+ if (samples_before_overflow < samples.size()) {
+ samples_written_ = samples.size() - samples_before_overflow;
+ } else {
+ samples_written_ += samples.size();
+ }
}
} // namespace audio
diff --git a/src/audio/include/audio_converter.hpp b/src/tangara/audio/processor.hpp
index 232b5d8e..1bd6beff 100644
--- a/src/audio/include/audio_converter.hpp
+++ b/src/tangara/audio/processor.hpp
@@ -10,11 +10,11 @@
#include <cstdint>
#include <memory>
-#include "audio_events.hpp"
-#include "audio_sink.hpp"
-#include "audio_source.hpp"
+#include "audio/audio_events.hpp"
+#include "audio/audio_sink.hpp"
+#include "audio/audio_source.hpp"
+#include "audio/resample.hpp"
#include "codec.hpp"
-#include "resample.hpp"
#include "sample.hpp"
namespace audio {
@@ -25,49 +25,52 @@ namespace audio {
* format of the current output device. The resulting samples are forwarded
* to the output device's sink stream.
*/
-class SampleConverter {
+class SampleProcessor {
public:
- SampleConverter();
- ~SampleConverter();
+ SampleProcessor(StreamBufferHandle_t sink);
+ ~SampleProcessor();
auto SetOutput(std::shared_ptr<IAudioOutput>) -> void;
auto beginStream(std::shared_ptr<TrackInfo>) -> void;
- auto continueStream(cpp::span<sample::Sample>) -> void;
- auto endStream() -> void;
+ auto continueStream(std::span<sample::Sample>) -> void;
+ auto endStream(bool cancelled) -> void;
private:
auto Main() -> void;
auto handleBeginStream(std::shared_ptr<TrackInfo>) -> void;
auto handleContinueStream(size_t samples_available) -> void;
- auto handleEndStream() -> void;
+ auto handleEndStream(bool cancel) -> void;
- auto handleSamples(cpp::span<sample::Sample>) -> size_t;
+ auto handleSamples(std::span<sample::Sample>) -> size_t;
- auto sendToSink(cpp::span<sample::Sample>) -> void;
+ auto sendToSink(std::span<sample::Sample>) -> void;
struct Args {
std::shared_ptr<TrackInfo>* track;
size_t samples_available;
bool is_end_of_stream;
+ bool clear_buffers;
};
QueueHandle_t commands_;
std::unique_ptr<Resampler> resampler_;
StreamBufferHandle_t source_;
- cpp::span<sample::Sample> input_buffer_;
- cpp::span<std::byte> input_buffer_as_bytes_;
+ StreamBufferHandle_t sink_;
- cpp::span<sample::Sample> resampled_buffer_;
+ std::span<sample::Sample> input_buffer_;
+ std::span<std::byte> input_buffer_as_bytes_;
- std::shared_ptr<IAudioOutput> sink_;
+ std::span<sample::Sample> resampled_buffer_;
+
+ std::shared_ptr<IAudioOutput> output_;
IAudioOutput::Format source_format_;
IAudioOutput::Format target_format_;
size_t leftover_bytes_;
- uint32_t samples_sunk_;
+ uint32_t samples_written_;
};
} // namespace audio
diff --git a/src/audio/readahead_source.cpp b/src/tangara/audio/readahead_source.cpp
index fe7ac3bd..602ec0b1 100644
--- a/src/audio/readahead_source.cpp
+++ b/src/tangara/audio/readahead_source.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "readahead_source.hpp"
+#include "audio/readahead_source.hpp"
#include <cstddef>
#include <cstdint>
@@ -14,10 +14,10 @@
#include "esp_log.h"
#include "ff.h"
-#include "audio_source.hpp"
+#include "audio/audio_source.hpp"
#include "codec.hpp"
+#include "drivers/spi.hpp"
#include "freertos/portmacro.h"
-#include "spi.hpp"
#include "tasks.hpp"
#include "types.hpp"
@@ -41,7 +41,7 @@ ReadaheadSource::~ReadaheadSource() {
vStreamBufferDeleteWithCaps(buffer_);
}
-auto ReadaheadSource::Read(cpp::span<std::byte> dest) -> ssize_t {
+auto ReadaheadSource::Read(std::span<std::byte> dest) -> ssize_t {
size_t bytes_written = 0;
// Fill the destination from our buffer, until either the buffer is drained
// or the destination is full.
diff --git a/src/audio/include/readahead_source.hpp b/src/tangara/audio/readahead_source.hpp
index 3e18a989..bbaf9f75 100644
--- a/src/audio/include/readahead_source.hpp
+++ b/src/tangara/audio/readahead_source.hpp
@@ -15,7 +15,7 @@
#include "ff.h"
#include "freertos/stream_buffer.h"
-#include "audio_source.hpp"
+#include "audio/audio_source.hpp"
#include "codec.hpp"
#include "tasks.hpp"
@@ -30,7 +30,7 @@ class ReadaheadSource : public codecs::IStream {
ReadaheadSource(tasks::WorkerPool&, std::unique_ptr<codecs::IStream>);
~ReadaheadSource();
- auto Read(cpp::span<std::byte> dest) -> ssize_t override;
+ auto Read(std::span<std::byte> dest) -> ssize_t override;
auto CanSeek() -> bool override;
diff --git a/src/audio/resample.cpp b/src/tangara/audio/resample.cpp
index a3a34ee7..143ce230 100644
--- a/src/audio/resample.cpp
+++ b/src/tangara/audio/resample.cpp
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "resample.hpp"
+#include "audio/resample.hpp"
#include <algorithm>
#include <cmath>
@@ -38,8 +38,8 @@ Resampler::~Resampler() {
speex_resampler_destroy(resampler_);
}
-auto Resampler::Process(cpp::span<sample::Sample> input,
- cpp::span<sample::Sample> output,
+auto Resampler::Process(std::span<sample::Sample> input,
+ std::span<sample::Sample> output,
bool end_of_data) -> std::pair<size_t, size_t> {
uint32_t samples_used = input.size() / num_channels_;
uint32_t samples_produced = output.size() / num_channels_;
diff --git a/src/audio/include/resample.hpp b/src/tangara/audio/resample.hpp
index a9464cb1..4d48d47f 100644
--- a/src/audio/include/resample.hpp
+++ b/src/tangara/audio/resample.hpp
@@ -7,9 +7,9 @@
#pragma once
#include <cstdint>
+#include <span>
#include <vector>
-#include "span.hpp"
#include "speex/speex_resampler.h"
#include "sample.hpp"
@@ -24,8 +24,8 @@ class Resampler {
~Resampler();
- auto Process(cpp::span<sample::Sample> input,
- cpp::span<sample::Sample> output,
+ auto Process(std::span<sample::Sample> input,
+ std::span<sample::Sample> output,
bool end_of_data) -> std::pair<size_t, size_t>;
private:
@@ -34,4 +34,4 @@ class Resampler {
uint8_t num_channels_;
};
-} // namespace audio \ No newline at end of file
+} // namespace audio
diff --git a/src/tangara/audio/stream_cues.cpp b/src/tangara/audio/stream_cues.cpp
new file mode 100644
index 00000000..7a6a1426
--- /dev/null
+++ b/src/tangara/audio/stream_cues.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2024 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "audio/stream_cues.hpp"
+
+#include <cstdint>
+#include <memory>
+
+namespace audio {
+
+StreamCues::StreamCues() : now_(0) {}
+
+auto StreamCues::update(uint32_t sample) -> void {
+ if (sample < now_) {
+ // The current time must have overflowed. Deal with any cues between now_
+ // and UINT32_MAX, then proceed as normal.
+ while (!upcoming_.empty() && upcoming_.front().start_at > now_) {
+ current_ = upcoming_.front();
+ upcoming_.pop_front();
+ }
+ }
+ now_ = sample;
+
+ while (!upcoming_.empty() && upcoming_.front().start_at <= now_) {
+ current_ = upcoming_.front();
+ upcoming_.pop_front();
+ }
+}
+
+auto StreamCues::addCue(std::shared_ptr<TrackInfo> track, uint32_t sample)
+ -> void {
+ if (sample == now_) {
+ current_ = {track, now_};
+ } else {
+ upcoming_.push_back(Cue{
+ .track = track,
+ .start_at = sample,
+ });
+ }
+}
+
+auto StreamCues::current() -> std::pair<std::shared_ptr<TrackInfo>, uint32_t> {
+ if (!current_) {
+ return {};
+ }
+
+ uint32_t duration;
+ if (now_ < current_->start_at) {
+ // now_ overflowed since this track started.
+ duration = now_ + (UINT32_MAX - current_->start_at);
+ } else {
+ duration = now_ - current_->start_at;
+ }
+
+ return {current_->track, duration};
+}
+
+auto StreamCues::hasStream() -> bool {
+ return current_ || !upcoming_.empty();
+}
+
+} // namespace audio
diff --git a/src/tangara/audio/stream_cues.hpp b/src/tangara/audio/stream_cues.hpp
new file mode 100644
index 00000000..cd0782b0
--- /dev/null
+++ b/src/tangara/audio/stream_cues.hpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2024 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <cstdint>
+#include <deque>
+#include <memory>
+
+#include "audio/audio_events.hpp"
+
+namespace audio {
+
+/*
+ * Utility for tracking which track is currently being played (and how long it
+ * has been playing for) based on counting samples that are put into and taken
+ * out of the audio processor's output buffer.
+ */
+class StreamCues {
+ public:
+ StreamCues();
+
+ /* Updates the current track given the new most recently played sample. */
+ auto update(uint32_t sample) -> void;
+
+ /* Returns the current track, and how long it has been playing for. */
+ auto current() -> std::pair<std::shared_ptr<TrackInfo>, uint32_t>;
+
+ auto hasStream() -> bool;
+
+ auto addCue(std::shared_ptr<TrackInfo>, uint32_t start_at) -> void;
+
+ private:
+ uint32_t now_;
+
+ struct Cue {
+ std::shared_ptr<TrackInfo> track;
+ uint32_t start_at;
+ };
+
+ std::optional<Cue> current_;
+ std::deque<Cue> upcoming_;
+};
+
+} // namespace audio
diff --git a/src/audio/test/CMakeLists.txt b/src/tangara/audio/test/CMakeLists.txt
index 4d580b1c..4d580b1c 100644
--- a/src/audio/test/CMakeLists.txt
+++ b/src/tangara/audio/test/CMakeLists.txt
diff --git a/src/audio/track_queue.cpp b/src/tangara/audio/track_queue.cpp
index dbe283c4..603b0de1 100644
--- a/src/audio/track_queue.cpp
+++ b/src/tangara/audio/track_queue.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "track_queue.hpp"
+#include "audio/track_queue.hpp"
#include <stdint.h>
#include <algorithm>
@@ -18,16 +18,16 @@
#include "MillerShuffle.h"
#include "esp_random.h"
-#include "audio_events.hpp"
-#include "audio_fsm.hpp"
+#include "audio/audio_events.hpp"
+#include "audio/audio_fsm.hpp"
#include "cppbor.h"
#include "cppbor_parse.h"
-#include "database.hpp"
-#include "event_queue.hpp"
+#include "database/database.hpp"
+#include "database/track.hpp"
+#include "events/event_queue.hpp"
#include "memory_resource.hpp"
#include "tasks.hpp"
-#include "track.hpp"
-#include "ui_fsm.hpp"
+#include "ui/ui_fsm.hpp"
namespace audio {
diff --git a/src/audio/include/track_queue.hpp b/src/tangara/audio/track_queue.hpp
index 5b7c9448..427d5f75 100644
--- a/src/audio/include/track_queue.hpp
+++ b/src/tangara/audio/track_queue.hpp
@@ -12,11 +12,11 @@
#include <shared_mutex>
#include <vector>
-#include "audio_events.hpp"
+#include "audio/audio_events.hpp"
#include "cppbor_parse.h"
-#include "database.hpp"
+#include "database/database.hpp"
+#include "database/track.hpp"
#include "tasks.hpp"
-#include "track.hpp"
namespace audio {
diff --git a/src/battery/battery.cpp b/src/tangara/battery/battery.cpp
index debef9e6..2d2fff56 100644
--- a/src/battery/battery.cpp
+++ b/src/tangara/battery/battery.cpp
@@ -4,15 +4,15 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "battery.hpp"
+#include "battery/battery.hpp"
#include <cstdint>
-#include "adc.hpp"
-#include "event_queue.hpp"
+#include "drivers/adc.hpp"
+#include "drivers/samd.hpp"
+#include "events/event_queue.hpp"
#include "freertos/portmacro.h"
-#include "samd.hpp"
-#include "system_events.hpp"
+#include "system_fsm/system_events.hpp"
namespace battery {
diff --git a/src/battery/include/battery.hpp b/src/tangara/battery/battery.hpp
index 314cd373..80b0f2d2 100644
--- a/src/battery/include/battery.hpp
+++ b/src/tangara/battery/battery.hpp
@@ -11,8 +11,8 @@
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
-#include "adc.hpp"
-#include "samd.hpp"
+#include "drivers/adc.hpp"
+#include "drivers/samd.hpp"
namespace battery {
diff --git a/src/database/database.cpp b/src/tangara/database/database.cpp
index 48fb0c63..4064c3ed 100644
--- a/src/database/database.cpp
+++ b/src/tangara/database/database.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "database.hpp"
+#include "database/database.hpp"
#include <stdint.h>
#include <sys/_stdint.h>
@@ -23,10 +23,10 @@
#include "collation.hpp"
#include "cppbor.h"
#include "cppbor_parse.h"
+#include "database/index.hpp"
#include "esp_log.h"
#include "ff.h"
#include "freertos/projdefs.h"
-#include "index.hpp"
#include "komihash.h"
#include "leveldb/cache.h"
#include "leveldb/db.h"
@@ -36,17 +36,17 @@
#include "leveldb/status.h"
#include "leveldb/write_batch.h"
-#include "db_events.hpp"
-#include "env_esp.hpp"
-#include "event_queue.hpp"
-#include "file_gatherer.hpp"
+#include "database/db_events.hpp"
+#include "database/env_esp.hpp"
+#include "database/file_gatherer.hpp"
+#include "database/records.hpp"
+#include "database/tag_parser.hpp"
+#include "database/track.hpp"
+#include "drivers/spi.hpp"
+#include "events/event_queue.hpp"
#include "memory_resource.hpp"
-#include "records.hpp"
#include "result.hpp"
-#include "spi.hpp"
-#include "tag_parser.hpp"
#include "tasks.hpp"
-#include "track.hpp"
namespace database {
@@ -63,8 +63,8 @@ static const char kKeyTrackId[] = "next_track_id";
static std::atomic<bool> sIsDbOpen(false);
-static auto CreateNewDatabase(leveldb::Options& options, locale::ICollator& col)
- -> leveldb::DB* {
+static auto CreateNewDatabase(leveldb::Options& options,
+ locale::ICollator& col) -> leveldb::DB* {
Database::Destroy();
leveldb::DB* db;
options.create_if_missing = true;
diff --git a/src/database/include/database.hpp b/src/tangara/database/database.hpp
index 35b76a13..d2de7c72 100644
--- a/src/database/include/database.hpp
+++ b/src/tangara/database/database.hpp
@@ -19,19 +19,19 @@
#include "collation.hpp"
#include "cppbor.h"
-#include "file_gatherer.hpp"
-#include "index.hpp"
+#include "database/file_gatherer.hpp"
+#include "database/index.hpp"
+#include "database/records.hpp"
+#include "database/tag_parser.hpp"
+#include "database/track.hpp"
#include "leveldb/cache.h"
#include "leveldb/db.h"
#include "leveldb/iterator.h"
#include "leveldb/options.h"
#include "leveldb/slice.h"
#include "memory_resource.hpp"
-#include "records.hpp"
#include "result.hpp"
-#include "tag_parser.hpp"
#include "tasks.hpp"
-#include "track.hpp"
namespace database {
diff --git a/src/database/include/db_events.hpp b/src/tangara/database/db_events.hpp
index a1aefc27..a1aefc27 100644
--- a/src/database/include/db_events.hpp
+++ b/src/tangara/database/db_events.hpp
diff --git a/src/database/env_esp.cpp b/src/tangara/database/env_esp.cpp
index f7a5637a..86a30613 100644
--- a/src/database/env_esp.cpp
+++ b/src/tangara/database/env_esp.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "env_esp.hpp"
+#include "database/env_esp.hpp"
#include <atomic>
#include <cerrno>
@@ -36,12 +36,12 @@
#include "leveldb/slice.h"
#include "leveldb/status.h"
-#include "spi.hpp"
+#include "drivers/spi.hpp"
#include "tasks.hpp"
namespace leveldb {
-tasks::WorkerPool *sBackgroundThread = nullptr;
+tasks::WorkerPool* sBackgroundThread = nullptr;
std::string ErrToStr(FRESULT err) {
switch (err) {
diff --git a/src/database/include/env_esp.hpp b/src/tangara/database/env_esp.hpp
index 472a72a6..472a72a6 100644
--- a/src/database/include/env_esp.hpp
+++ b/src/tangara/database/env_esp.hpp
diff --git a/src/database/file_gatherer.cpp b/src/tangara/database/file_gatherer.cpp
index b7b7271e..75a1af27 100644
--- a/src/database/file_gatherer.cpp
+++ b/src/tangara/database/file_gatherer.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "file_gatherer.hpp"
+#include "database/file_gatherer.hpp"
#include <deque>
#include <functional>
@@ -13,8 +13,8 @@
#include "ff.h"
+#include "drivers/spi.hpp"
#include "memory_resource.hpp"
-#include "spi.hpp"
namespace database {
diff --git a/src/database/include/file_gatherer.hpp b/src/tangara/database/file_gatherer.hpp
index 685bdb2c..38558b9e 100644
--- a/src/database/include/file_gatherer.hpp
+++ b/src/tangara/database/file_gatherer.hpp
@@ -17,20 +17,18 @@ namespace database {
class IFileGatherer {
public:
- virtual ~IFileGatherer(){};
+ virtual ~IFileGatherer() {};
virtual auto FindFiles(
const std::string& root,
- std::function<void(std::string_view, const FILINFO&)> cb)
- -> void = 0;
+ std::function<void(std::string_view, const FILINFO&)> cb) -> void = 0;
};
class FileGathererImpl : public IFileGatherer {
public:
- virtual auto FindFiles(
- const std::string& root,
- std::function<void(std::string_view, const FILINFO&)> cb)
- -> void override;
+ virtual auto FindFiles(const std::string& root,
+ std::function<void(std::string_view, const FILINFO&)>
+ cb) -> void override;
};
} // namespace database
diff --git a/src/database/include/future_fetcher.hpp b/src/tangara/database/future_fetcher.hpp
index e8ce9729..a27101f1 100644
--- a/src/database/include/future_fetcher.hpp
+++ b/src/tangara/database/future_fetcher.hpp
@@ -9,7 +9,7 @@
#include <memory>
#include <utility>
-#include "database.hpp"
+#include "database/database.hpp"
namespace database {
diff --git a/src/database/index.cpp b/src/tangara/database/index.cpp
index 857fbcc5..93a2b1c2 100644
--- a/src/database/index.cpp
+++ b/src/tangara/database/index.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "index.hpp"
+#include "database/index.hpp"
#include <sys/_stdint.h>
#include <cstdint>
@@ -21,8 +21,8 @@
#include "komihash.h"
#include "leveldb/write_batch.h"
-#include "records.hpp"
-#include "track.hpp"
+#include "database/records.hpp"
+#include "database/track.hpp"
namespace database {
@@ -61,11 +61,11 @@ class Indexer {
private:
auto handleLevel(const IndexKey::Header& header,
- cpp::span<const Tag> components) -> void;
+ std::span<const Tag> components) -> void;
auto handleItem(const IndexKey::Header& header,
std::variant<std::pmr::string, uint32_t> item,
- cpp::span<const Tag> components) -> void;
+ std::span<const Tag> components) -> void;
auto missing_value(Tag tag) -> TagValue {
switch (tag) {
@@ -111,7 +111,7 @@ auto Indexer::index() -> std::vector<std::pair<IndexKey, std::string>> {
}
auto Indexer::handleLevel(const IndexKey::Header& header,
- cpp::span<const Tag> components) -> void {
+ std::span<const Tag> components) -> void {
Tag component = components.front();
TagValue value = track_.tags().get(component);
if (std::holds_alternative<std::monostate>(value)) {
@@ -129,7 +129,7 @@ auto Indexer::handleLevel(const IndexKey::Header& header,
} else if constexpr (std::is_same_v<T, uint32_t>) {
handleItem(header, arg, components);
} else if constexpr (std::is_same_v<
- T, cpp::span<const std::pmr::string>>) {
+ T, std::span<const std::pmr::string>>) {
for (const auto& i : arg) {
handleItem(header, i, components);
}
@@ -140,7 +140,7 @@ auto Indexer::handleLevel(const IndexKey::Header& header,
auto Indexer::handleItem(const IndexKey::Header& header,
std::variant<std::pmr::string, uint32_t> item,
- cpp::span<const Tag> components) -> void {
+ std::span<const Tag> components) -> void {
IndexKey key{
.header = header,
.item = {},
@@ -183,8 +183,9 @@ auto Indexer::handleItem(const IndexKey::Header& header,
}
}
-auto Index(locale::ICollator& c, const IndexInfo& i, const Track& t)
- -> std::vector<std::pair<IndexKey, std::string>> {
+auto Index(locale::ICollator& c,
+ const IndexInfo& i,
+ const Track& t) -> std::vector<std::pair<IndexKey, std::string>> {
Indexer indexer{c, t, i};
return indexer.index();
}
diff --git a/src/database/include/index.hpp b/src/tangara/database/index.hpp
index 45dae464..8f78439b 100644
--- a/src/database/include/index.hpp
+++ b/src/tangara/database/index.hpp
@@ -17,9 +17,9 @@
#include "leveldb/db.h"
#include "leveldb/slice.h"
+#include "database/track.hpp"
#include "leveldb/write_batch.h"
#include "memory_resource.hpp"
-#include "track.hpp"
namespace database {
@@ -61,8 +61,9 @@ struct IndexKey {
std::optional<TrackId> track;
};
-auto Index(locale::ICollator&, const IndexInfo&, const Track&)
- -> std::vector<std::pair<IndexKey, std::string>>;
+auto Index(locale::ICollator&,
+ const IndexInfo&,
+ const Track&) -> std::vector<std::pair<IndexKey, std::string>>;
auto ExpandHeader(const IndexKey::Header&,
const std::optional<std::pmr::string>&) -> IndexKey::Header;
diff --git a/src/database/records.cpp b/src/tangara/database/records.cpp
index a1efb568..88ddbd91 100644
--- a/src/database/records.cpp
+++ b/src/tangara/database/records.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "records.hpp"
+#include "database/records.hpp"
#include <stdint.h>
#include <sys/_stdint.h>
@@ -21,10 +21,10 @@
#include "cppbor_parse.h"
#include "esp_log.h"
-#include "index.hpp"
+#include "database/index.hpp"
+#include "database/track.hpp"
#include "komihash.h"
#include "memory_resource.hpp"
-#include "track.hpp"
// As LevelDB is a key-value store, each record in the database consists of a
// key and an optional value.
@@ -248,7 +248,7 @@ auto TrackIdToBytes(TrackId id) -> std::string {
return cppbor::Uint{id}.toString();
}
-auto BytesToTrackId(cpp::span<const char> bytes) -> std::optional<TrackId> {
+auto BytesToTrackId(std::span<const char> bytes) -> std::optional<TrackId> {
auto [res, unused, err] = cppbor::parse(
reinterpret_cast<const uint8_t*>(bytes.data()), bytes.size());
if (!res || res->type() != cppbor::UINT) {
diff --git a/src/database/include/records.hpp b/src/tangara/database/records.hpp
index 87034059..db18fe2f 100644
--- a/src/database/include/records.hpp
+++ b/src/tangara/database/records.hpp
@@ -15,9 +15,9 @@
#include "leveldb/db.h"
#include "leveldb/slice.h"
-#include "index.hpp"
+#include "database/index.hpp"
+#include "database/track.hpp"
#include "memory_resource.hpp"
-#include "track.hpp"
namespace database {
@@ -80,6 +80,6 @@ auto TrackIdToBytes(TrackId id) -> std::string;
* Converts a track id encoded via TrackIdToBytes back into a TrackId. May
* return nullopt if parsing fails.
*/
-auto BytesToTrackId(cpp::span<const char> bytes) -> std::optional<TrackId>;
+auto BytesToTrackId(std::span<const char> bytes) -> std::optional<TrackId>;
} // namespace database
diff --git a/src/database/tag_parser.cpp b/src/tangara/database/tag_parser.cpp
index cbcbdcb5..2df2d90f 100644
--- a/src/database/tag_parser.cpp
+++ b/src/tangara/database/tag_parser.cpp
@@ -4,16 +4,16 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "tag_parser.hpp"
+#include "database/tag_parser.hpp"
#include <cstdint>
#include <cstdlib>
#include <iomanip>
#include <mutex>
+#include "drivers/spi.hpp"
#include "esp_log.h"
#include "ff.h"
-#include "spi.hpp"
#include "tags.h"
#include "memory_resource.hpp"
diff --git a/src/database/include/tag_parser.hpp b/src/tangara/database/tag_parser.hpp
index 966258b5..ccbc0ea9 100644
--- a/src/database/include/tag_parser.hpp
+++ b/src/tangara/database/tag_parser.hpp
@@ -8,8 +8,8 @@
#include <string>
+#include "database/track.hpp"
#include "lru_cache.hpp"
-#include "track.hpp"
namespace database {
diff --git a/src/database/test/CMakeLists.txt b/src/tangara/database/test/CMakeLists.txt
index a9f2cedb..a9f2cedb 100644
--- a/src/database/test/CMakeLists.txt
+++ b/src/tangara/database/test/CMakeLists.txt
diff --git a/src/database/test/test_database.cpp b/src/tangara/database/test/test_database.cpp
index 6aec9bfb..09e19a43 100644
--- a/src/database/test/test_database.cpp
+++ b/src/tangara/database/test/test_database.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "database.hpp"
+#include "database/database.hpp"
#include <stdint.h>
#include <iomanip>
@@ -13,14 +13,14 @@
#include <string>
#include "catch2/catch.hpp"
+#include "database/file_gatherer.hpp"
+#include "database/tag_parser.hpp"
+#include "database/track.hpp"
#include "driver_cache.hpp"
#include "esp_log.h"
-#include "file_gatherer.hpp"
#include "i2c_fixture.hpp"
#include "leveldb/db.h"
#include "spi_fixture.hpp"
-#include "tag_parser.hpp"
-#include "track.hpp"
namespace database {
@@ -28,8 +28,8 @@ class TestBackends : public IFileGatherer, public ITagParser {
public:
std::map<std::pmr::string, TrackTags> tracks;
- auto MakeTrack(const std::pmr::string& path, const std::pmr::string& title)
- -> void {
+ auto MakeTrack(const std::pmr::string& path,
+ const std::pmr::string& title) -> void {
TrackTags tags;
tags.encoding = Encoding::kMp3;
tags.title = title;
@@ -44,8 +44,8 @@ class TestBackends : public IFileGatherer, public ITagParser {
}
}
- auto ReadAndParseTags(const std::pmr::string& path, TrackTags* out)
- -> bool override {
+ auto ReadAndParseTags(const std::pmr::string& path,
+ TrackTags* out) -> bool override {
if (tracks.contains(path)) {
*out = tracks.at(path);
return true;
diff --git a/src/database/test/test_records.cpp b/src/tangara/database/test/test_records.cpp
index 2f59489c..f8eb980f 100644
--- a/src/database/test/test_records.cpp
+++ b/src/tangara/database/test/test_records.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "records.hpp"
+#include "database/records.hpp"
#include <stdint.h>
#include <iomanip>
diff --git a/src/database/track.cpp b/src/tangara/database/track.cpp
index a2bd05d3..5bf8c3e2 100644
--- a/src/database/track.cpp
+++ b/src/tangara/database/track.cpp
@@ -4,11 +4,12 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "track.hpp"
+#include "database/track.hpp"
#include <iomanip>
#include <iostream>
#include <memory_resource>
+#include <span>
#include <sstream>
#include <string>
@@ -16,7 +17,6 @@
#include "komihash.h"
#include "memory_resource.hpp"
-#include "span.hpp"
namespace database {
@@ -55,7 +55,7 @@ auto tagHash(const TagValue& t) -> uint64_t {
} else if constexpr (std::is_same_v<T, uint32_t>) {
return komihash(&arg, sizeof(arg), 0);
} else if constexpr (std::is_same_v<
- T, cpp::span<const std::pmr::string>>) {
+ T, std::span<const std::pmr::string>>) {
komihash_stream_t hash;
komihash_stream_init(&hash, 0);
for (const auto& i : arg) {
@@ -79,7 +79,7 @@ auto tagToString(const TagValue& val) -> std::string {
} else if constexpr (std::is_same_v<T, uint32_t>) {
return std::to_string(arg);
} else if constexpr (std::is_same_v<
- T, cpp::span<const std::pmr::string>>) {
+ T, std::span<const std::pmr::string>>) {
std::ostringstream builder{};
for (const auto& str : arg) {
builder << std::string{str.data(), str.size()} << ",";
@@ -225,7 +225,7 @@ auto TrackTags::albumOrder() const -> uint32_t {
return (disc_.value_or(0) << 16) | track_.value_or(0);
}
-auto TrackTags::genres() const -> cpp::span<const std::pmr::string> {
+auto TrackTags::genres() const -> std::span<const std::pmr::string> {
return genres_;
}
diff --git a/src/database/include/track.hpp b/src/tangara/database/track.hpp
index 76b1c56e..b097ab52 100644
--- a/src/database/include/track.hpp
+++ b/src/tangara/database/track.hpp
@@ -6,12 +6,12 @@
#pragma once
-#include <stdint.h>
-#include <sys/_stdint.h>
+#include <cstdint>
#include <map>
#include <memory>
#include <optional>
+#include <span>
#include <string>
#include <unordered_map>
#include <utility>
@@ -19,7 +19,6 @@
#include "leveldb/db.h"
#include "memory_resource.hpp"
-#include "span.hpp"
namespace database {
@@ -62,7 +61,7 @@ enum class Tag {
using TagValue = std::variant<std::monostate,
std::pmr::string,
uint32_t,
- cpp::span<const std::pmr::string>>;
+ std::span<const std::pmr::string>>;
auto tagName(Tag) -> std::string;
auto tagHash(const TagValue&) -> uint64_t;
@@ -112,7 +111,7 @@ class TrackTags {
auto albumOrder() const -> uint32_t;
- auto genres() const -> cpp::span<const std::pmr::string>;
+ auto genres() const -> std::span<const std::pmr::string>;
auto genres(const std::string_view) -> void;
/*
diff --git a/src/dev_console/console.cpp b/src/tangara/dev_console/console.cpp
index f2b1efea..cee68b49 100644
--- a/src/dev_console/console.cpp
+++ b/src/tangara/dev_console/console.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "console.hpp"
+#include "dev_console/console.hpp"
#include <stdio.h>
#include <string.h>
diff --git a/src/dev_console/include/console.hpp b/src/tangara/dev_console/console.hpp
index fedf3632..fedf3632 100644
--- a/src/dev_console/include/console.hpp
+++ b/src/tangara/dev_console/console.hpp
diff --git a/src/events/event_queue.cpp b/src/tangara/events/event_queue.cpp
index d3a62ef6..e4751398 100644
--- a/src/events/event_queue.cpp
+++ b/src/tangara/events/event_queue.cpp
@@ -4,14 +4,15 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "event_queue.hpp"
+#include "events/event_queue.hpp"
-#include "audio_fsm.hpp"
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "freertos/queue.h"
-#include "system_fsm.hpp"
-#include "ui_fsm.hpp"
+
+#include "audio/audio_fsm.hpp"
+#include "system_fsm/system_fsm.hpp"
+#include "ui/ui_fsm.hpp"
namespace events {
diff --git a/src/events/include/event_queue.hpp b/src/tangara/events/event_queue.hpp
index 78b21d53..aa7f472d 100644
--- a/src/events/include/event_queue.hpp
+++ b/src/tangara/events/event_queue.hpp
@@ -11,14 +11,14 @@
#include <queue>
#include <type_traits>
-#include "audio_fsm.hpp"
+#include "audio/audio_fsm.hpp"
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "freertos/queue.h"
-#include "system_fsm.hpp"
+#include "system_fsm/system_fsm.hpp"
#include "tinyfsm.hpp"
-#include "ui_fsm.hpp"
+#include "ui/ui_fsm.hpp"
namespace events {
diff --git a/src/input/device_factory.cpp b/src/tangara/input/device_factory.cpp
index 65f4d785..8e1c5155 100644
--- a/src/input/device_factory.cpp
+++ b/src/tangara/input/device_factory.cpp
@@ -4,16 +4,16 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "device_factory.hpp"
+#include "input/device_factory.hpp"
#include <memory>
-#include "feedback_haptics.hpp"
-#include "input_device.hpp"
-#include "input_nav_buttons.hpp"
-#include "input_touch_dpad.hpp"
-#include "input_touch_wheel.hpp"
-#include "input_volume_buttons.hpp"
+#include "input/feedback_haptics.hpp"
+#include "input/input_device.hpp"
+#include "input/input_nav_buttons.hpp"
+#include "input/input_touch_dpad.hpp"
+#include "input/input_touch_wheel.hpp"
+#include "input/input_volume_buttons.hpp"
namespace input {
diff --git a/src/input/include/device_factory.hpp b/src/tangara/input/device_factory.hpp
index dd9c7133..5044d025 100644
--- a/src/input/include/device_factory.hpp
+++ b/src/tangara/input/device_factory.hpp
@@ -9,11 +9,11 @@
#include <cstdint>
#include <memory>
-#include "feedback_device.hpp"
-#include "input_device.hpp"
-#include "input_touch_wheel.hpp"
-#include "nvs.hpp"
-#include "service_locator.hpp"
+#include "input/feedback_device.hpp"
+#include "input/input_device.hpp"
+#include "input/input_touch_wheel.hpp"
+#include "drivers/nvs.hpp"
+#include "system_fsm/service_locator.hpp"
namespace input {
diff --git a/src/input/include/feedback_device.hpp b/src/tangara/input/feedback_device.hpp
index 4faeeafd..4faeeafd 100644
--- a/src/input/include/feedback_device.hpp
+++ b/src/tangara/input/feedback_device.hpp
diff --git a/src/input/feedback_haptics.cpp b/src/tangara/input/feedback_haptics.cpp
index 5e83d0d6..e690eb9f 100644
--- a/src/input/feedback_haptics.cpp
+++ b/src/tangara/input/feedback_haptics.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "feedback_haptics.hpp"
+#include "input/feedback_haptics.hpp"
#include <cstdint>
@@ -13,7 +13,7 @@
#include "core/lv_event.h"
#include "esp_log.h"
-#include "haptics.hpp"
+#include "drivers/haptics.hpp"
namespace input {
diff --git a/src/input/include/feedback_haptics.hpp b/src/tangara/input/feedback_haptics.hpp
index a307a429..bde5f345 100644
--- a/src/input/include/feedback_haptics.hpp
+++ b/src/tangara/input/feedback_haptics.hpp
@@ -8,8 +8,8 @@
#include <cstdint>
-#include "feedback_device.hpp"
-#include "haptics.hpp"
+#include "drivers/haptics.hpp"
+#include "input/feedback_device.hpp"
namespace input {
diff --git a/src/input/include/input_device.hpp b/src/tangara/input/input_device.hpp
index 59456351..e3d17c6c 100644
--- a/src/input/include/input_device.hpp
+++ b/src/tangara/input/input_device.hpp
@@ -11,8 +11,8 @@
#include <vector>
#include "hal/lv_hal_indev.h"
-#include "input_hook.hpp"
-#include "property.hpp"
+#include "input/input_hook.hpp"
+#include "lua/property.hpp"
namespace input {
@@ -29,7 +29,9 @@ class IInputDevice {
virtual auto read(lv_indev_data_t* data) -> void = 0;
virtual auto name() -> std::string = 0;
- virtual auto hooks() -> std::vector<TriggerHooks> { return {}; }
+ virtual auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> {
+ return {};
+ }
};
} // namespace input
diff --git a/src/input/input_hook.cpp b/src/tangara/input/input_hook.cpp
index 1bb92196..95ff8f2c 100644
--- a/src/input/input_hook.cpp
+++ b/src/tangara/input/input_hook.cpp
@@ -4,13 +4,15 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "input_hook.hpp"
+#include "input/input_hook.hpp"
#include <functional>
#include <optional>
+
#include "hal/lv_hal_indev.h"
-#include "input_trigger.hpp"
-#include "lua.h"
+#include "lua.hpp"
+
+#include "input/input_trigger.hpp"
namespace input {
@@ -39,10 +41,12 @@ auto Hook::callback() -> std::optional<HookCallback> {
TriggerHooks::TriggerHooks(std::string name,
std::optional<HookCallback> click,
+ std::optional<HookCallback> double_click,
std::optional<HookCallback> long_press,
std::optional<HookCallback> repeat)
: name_(name),
click_("click", click),
+ double_click_("double_click", double_click),
long_press_("long_press", long_press),
repeat_("repeat", repeat) {}
@@ -51,6 +55,9 @@ auto TriggerHooks::update(bool pressed, lv_indev_data_t* d) -> void {
case Trigger::State::kClick:
click_.invoke(d);
break;
+ case Trigger::State::kDoubleClick:
+ double_click_.invoke(d);
+ break;
case Trigger::State::kLongPress:
long_press_.invoke(d);
break;
@@ -85,23 +92,12 @@ auto TriggerHooks::name() const -> const std::string& {
return name_;
}
-auto TriggerHooks::pushHooks(lua_State* L) -> void {
- lua_newtable(L);
-
- auto add_trigger = [&](Hook& h) {
- lua_pushlstring(L, h.name().data(), h.name().size());
- auto cb = h.callback();
- if (cb) {
- lua_pushlstring(L, cb->name.data(), cb->name.size());
- } else {
- lua_pushnil(L);
- }
- lua_rawset(L, -3);
- };
-
- add_trigger(click_);
- add_trigger(long_press_);
- add_trigger(repeat_);
+auto TriggerHooks::hooks() -> std::vector<std::reference_wrapper<Hook>> {
+ return {click_, long_press_, repeat_};
+}
+
+auto TriggerHooks::cancel() -> void {
+ trigger_.cancel();
}
} // namespace input
diff --git a/src/input/include/input_hook.hpp b/src/tangara/input/input_hook.hpp
index 81eb80d9..06fcb037 100644
--- a/src/input/include/input_hook.hpp
+++ b/src/tangara/input/input_hook.hpp
@@ -13,7 +13,7 @@
#include "hal/lv_hal_indev.h"
#include "lua.hpp"
-#include "input_trigger.hpp"
+#include "input/input_trigger.hpp"
namespace input {
@@ -32,6 +32,10 @@ class Hook {
auto name() const -> const std::string& { return name_; }
auto callback() -> std::optional<HookCallback>;
+ // Not copyable or movable.
+ Hook(const Hook&) = delete;
+ Hook& operator=(const Hook&) = delete;
+
private:
std::string name_;
std::optional<HookCallback> default_;
@@ -41,9 +45,10 @@ class Hook {
class TriggerHooks {
public:
TriggerHooks(std::string name, std::optional<HookCallback> cb)
- : TriggerHooks(name, cb, cb, cb) {}
+ : TriggerHooks(name, cb, cb, cb, cb) {}
TriggerHooks(std::string name,
std::optional<HookCallback> click,
+ std::optional<HookCallback> double_click,
std::optional<HookCallback> long_press,
std::optional<HookCallback> repeat);
@@ -51,13 +56,20 @@ class TriggerHooks {
auto override(Trigger::State, std::optional<HookCallback>) -> void;
auto name() const -> const std::string&;
- auto pushHooks(lua_State*) -> void;
+ auto hooks() -> std::vector<std::reference_wrapper<Hook>>;
+
+ auto cancel() -> void;
+
+ // Not copyable or movable.
+ TriggerHooks(const TriggerHooks&) = delete;
+ TriggerHooks& operator=(const TriggerHooks&) = delete;
private:
std::string name_;
Trigger trigger_;
Hook click_;
+ Hook double_click_;
Hook long_press_;
Hook repeat_;
};
diff --git a/src/input/input_hook_actions.cpp b/src/tangara/input/input_hook_actions.cpp
index 26075c4c..bc3760ac 100644
--- a/src/input/input_hook_actions.cpp
+++ b/src/tangara/input/input_hook_actions.cpp
@@ -4,14 +4,14 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "input_hook_actions.hpp"
+#include "input/input_hook_actions.hpp"
#include <cstdint>
#include "hal/lv_hal_indev.h"
-#include "event_queue.hpp"
-#include "ui_events.hpp"
+#include "events/event_queue.hpp"
+#include "ui/ui_events.hpp"
namespace input {
namespace actions {
diff --git a/src/input/include/input_hook_actions.hpp b/src/tangara/input/input_hook_actions.hpp
index 105bd10d..71a560bc 100644
--- a/src/input/include/input_hook_actions.hpp
+++ b/src/tangara/input/input_hook_actions.hpp
@@ -7,7 +7,7 @@
#pragma once
#include "hal/lv_hal_indev.h"
-#include "input_hook.hpp"
+#include "input/input_hook.hpp"
namespace input {
namespace actions {
diff --git a/src/input/input_nav_buttons.cpp b/src/tangara/input/input_nav_buttons.cpp
index 7e579a16..ba1f4b74 100644
--- a/src/input/input_nav_buttons.cpp
+++ b/src/tangara/input/input_nav_buttons.cpp
@@ -4,19 +4,19 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "input_nav_buttons.hpp"
+#include "input/input_nav_buttons.hpp"
-#include "event_queue.hpp"
-#include "gpios.hpp"
+#include "drivers/gpios.hpp"
+#include "events/event_queue.hpp"
#include "hal/lv_hal_indev.h"
-#include "input_hook_actions.hpp"
+#include "input/input_hook_actions.hpp"
namespace input {
NavButtons::NavButtons(drivers::IGpios& gpios)
: gpios_(gpios),
- up_("upper", actions::scrollUp(), actions::select(), {}),
- down_("lower", actions::scrollDown(), actions::select(), {}) {}
+ up_("upper", {}, actions::scrollUp(), actions::select(), {}),
+ down_("lower", {}, actions::scrollDown(), actions::select(), {}) {}
auto NavButtons::read(lv_indev_data_t* data) -> void {
up_.update(!gpios_.Get(drivers::IGpios::Pin::kKeyUp), data);
@@ -27,7 +27,8 @@ auto NavButtons::name() -> std::string {
return "buttons";
}
-auto NavButtons::hooks() -> std::vector<TriggerHooks> {
+auto NavButtons::triggers()
+ -> std::vector<std::reference_wrapper<TriggerHooks>> {
return {up_, down_};
}
diff --git a/src/input/include/input_nav_buttons.hpp b/src/tangara/input/input_nav_buttons.hpp
index 4e4952c9..ce2312a0 100644
--- a/src/input/include/input_nav_buttons.hpp
+++ b/src/tangara/input/input_nav_buttons.hpp
@@ -8,14 +8,14 @@
#include <cstdint>
-#include "gpios.hpp"
+#include "drivers/gpios.hpp"
#include "hal/lv_hal_indev.h"
-#include "haptics.hpp"
-#include "input_device.hpp"
-#include "input_hook.hpp"
-#include "input_trigger.hpp"
-#include "touchwheel.hpp"
+#include "drivers/haptics.hpp"
+#include "input/input_device.hpp"
+#include "input/input_hook.hpp"
+#include "input/input_trigger.hpp"
+#include "drivers/touchwheel.hpp"
namespace input {
@@ -26,7 +26,7 @@ class NavButtons : public IInputDevice {
auto read(lv_indev_data_t* data) -> void override;
auto name() -> std::string override;
- auto hooks() -> std::vector<TriggerHooks> override;
+ auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override;
private:
drivers::IGpios& gpios_;
diff --git a/src/input/input_touch_dpad.cpp b/src/tangara/input/input_touch_dpad.cpp
index f0805993..5d8e2dab 100644
--- a/src/input/input_touch_dpad.cpp
+++ b/src/tangara/input/input_touch_dpad.cpp
@@ -4,28 +4,29 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "input_touch_dpad.hpp"
+#include "input/input_touch_dpad.hpp"
#include <cstdint>
#include "hal/lv_hal_indev.h"
-#include "event_queue.hpp"
-#include "haptics.hpp"
-#include "input_device.hpp"
-#include "input_hook_actions.hpp"
-#include "input_touch_dpad.hpp"
-#include "touchwheel.hpp"
+#include "drivers/haptics.hpp"
+#include "drivers/touchwheel.hpp"
+
+#include "events/event_queue.hpp"
+#include "input/input_device.hpp"
+#include "input/input_hook_actions.hpp"
+#include "input/input_touch_dpad.hpp"
namespace input {
TouchDPad::TouchDPad(drivers::TouchWheel& wheel)
: wheel_(wheel),
- centre_("centre", actions::select(), {}, {}),
- up_("up", actions::scrollUp()),
- right_("right", {}, {}, {}),
- down_("down", actions::scrollDown()),
- left_("left", actions::goBack()) {}
+ centre_("centre", actions::select(), {}, {}, {}),
+ up_("up", actions::scrollUp(), {}, {}, actions::scrollUp()),
+ right_("right", actions::select(), {}, {}, {}),
+ down_("down", actions::scrollDown(), {}, {}, actions::scrollDown()),
+ left_("left", actions::goBack(), {}, {}, {}) {}
auto TouchDPad::read(lv_indev_data_t* data) -> void {
wheel_.Update();
@@ -55,7 +56,8 @@ auto TouchDPad::name() -> std::string {
return "dpad";
}
-auto TouchDPad::hooks() -> std::vector<TriggerHooks> {
+auto TouchDPad::triggers()
+ -> std::vector<std::reference_wrapper<TriggerHooks>> {
return {centre_, up_, right_, down_, left_};
}
diff --git a/src/input/include/input_touch_dpad.hpp b/src/tangara/input/input_touch_dpad.hpp
index 691e3243..8fb14832 100644
--- a/src/input/include/input_touch_dpad.hpp
+++ b/src/tangara/input/input_touch_dpad.hpp
@@ -10,11 +10,11 @@
#include "hal/lv_hal_indev.h"
-#include "haptics.hpp"
-#include "input_device.hpp"
-#include "input_hook.hpp"
-#include "input_trigger.hpp"
-#include "touchwheel.hpp"
+#include "drivers/haptics.hpp"
+#include "input/input_device.hpp"
+#include "input/input_hook.hpp"
+#include "input/input_trigger.hpp"
+#include "drivers/touchwheel.hpp"
namespace input {
@@ -25,7 +25,7 @@ class TouchDPad : public IInputDevice {
auto read(lv_indev_data_t* data) -> void override;
auto name() -> std::string override;
- auto hooks() -> std::vector<TriggerHooks> override;
+ auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override;
private:
drivers::TouchWheel& wheel_;
diff --git a/src/input/input_touch_wheel.cpp b/src/tangara/input/input_touch_wheel.cpp
index 121b1ee5..75159320 100644
--- a/src/input/input_touch_wheel.cpp
+++ b/src/tangara/input/input_touch_wheel.cpp
@@ -4,22 +4,22 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "input_touch_wheel.hpp"
+#include "input/input_touch_wheel.hpp"
#include <cstdint>
#include <variant>
#include "hal/lv_hal_indev.h"
-#include "event_queue.hpp"
-#include "haptics.hpp"
-#include "input_device.hpp"
-#include "input_hook_actions.hpp"
-#include "input_trigger.hpp"
-#include "nvs.hpp"
-#include "property.hpp"
-#include "touchwheel.hpp"
-#include "ui_events.hpp"
+#include "drivers/haptics.hpp"
+#include "drivers/nvs.hpp"
+#include "drivers/touchwheel.hpp"
+#include "events/event_queue.hpp"
+#include "input/input_device.hpp"
+#include "input/input_hook_actions.hpp"
+#include "input/input_trigger.hpp"
+#include "lua/property.hpp"
+#include "ui/ui_events.hpp"
namespace input {
@@ -39,11 +39,11 @@ TouchWheel::TouchWheel(drivers::NvsStorage& nvs, drivers::TouchWheel& wheel)
threshold_ = calculateThreshold(int_val);
return true;
}),
- centre_("centre", actions::select(), {}, {}),
- up_("up", {}, actions::scrollToTop(), actions::scrollUp()),
- right_("right", {}, {}, {}),
- down_("down", {}, actions::scrollToBottom(), actions::scrollDown()),
- left_("left", {}, actions::goBack(), {}),
+ centre_("centre", actions::select(), {}, {}, {}),
+ up_("up", {}, {}, actions::scrollToTop(), {}),
+ right_("right", {}),
+ down_("down", {}, {}, actions::scrollToBottom(), {}),
+ left_("left", {}, {}, actions::goBack(), {}),
is_scrolling_(false),
threshold_(calculateThreshold(nvs.ScrollSensitivity())),
is_first_read_(true),
@@ -68,31 +68,39 @@ auto TouchWheel::read(lv_indev_data_t* data) -> void {
data->enc_diff = 0;
}
- centre_.update(!is_scrolling_ && wheel_data.is_button_touched, data);
+ centre_.update(wheel_data.is_button_touched && !wheel_data.is_wheel_touched,
+ data);
// If the user is touching the wheel but not scrolling, then they may be
// clicking on one of the wheel's cardinal directions.
- bool pressing = wheel_data.is_wheel_touched && !is_scrolling_;
-
- up_.update(pressing && drivers::TouchWheel::isAngleWithin(
- wheel_data.wheel_position, 0, 32),
- data);
- right_.update(pressing && drivers::TouchWheel::isAngleWithin(
- wheel_data.wheel_position, 192, 32),
- data);
- down_.update(pressing && drivers::TouchWheel::isAngleWithin(
- wheel_data.wheel_position, 128, 32),
- data);
- left_.update(pressing && drivers::TouchWheel::isAngleWithin(
- wheel_data.wheel_position, 64, 32),
+ if (is_scrolling_) {
+ up_.cancel();
+ right_.cancel();
+ down_.cancel();
+ left_.cancel();
+ } else {
+ bool pressing = wheel_data.is_wheel_touched;
+ up_.update(pressing && drivers::TouchWheel::isAngleWithin(
+ wheel_data.wheel_position, 0, 32),
data);
+ right_.update(pressing && drivers::TouchWheel::isAngleWithin(
+ wheel_data.wheel_position, 192, 32),
+ data);
+ down_.update(pressing && drivers::TouchWheel::isAngleWithin(
+ wheel_data.wheel_position, 128, 32),
+ data);
+ left_.update(pressing && drivers::TouchWheel::isAngleWithin(
+ wheel_data.wheel_position, 64, 32),
+ data);
+ }
}
auto TouchWheel::name() -> std::string {
return "wheel";
}
-auto TouchWheel::hooks() -> std::vector<TriggerHooks> {
+auto TouchWheel::triggers()
+ -> std::vector<std::reference_wrapper<TriggerHooks>> {
return {centre_, up_, right_, down_, left_};
}
diff --git a/src/input/include/input_touch_wheel.hpp b/src/tangara/input/input_touch_wheel.hpp
index 1f116da9..d023873a 100644
--- a/src/input/include/input_touch_wheel.hpp
+++ b/src/tangara/input/input_touch_wheel.hpp
@@ -11,13 +11,13 @@
#include "hal/lv_hal_indev.h"
-#include "haptics.hpp"
-#include "input_device.hpp"
-#include "input_hook.hpp"
-#include "input_trigger.hpp"
-#include "nvs.hpp"
-#include "property.hpp"
-#include "touchwheel.hpp"
+#include "drivers/haptics.hpp"
+#include "input/input_device.hpp"
+#include "input/input_hook.hpp"
+#include "input/input_trigger.hpp"
+#include "lua/property.hpp"
+#include "drivers/nvs.hpp"
+#include "drivers/touchwheel.hpp"
namespace input {
@@ -28,7 +28,7 @@ class TouchWheel : public IInputDevice {
auto read(lv_indev_data_t* data) -> void override;
auto name() -> std::string override;
- auto hooks() -> std::vector<TriggerHooks> override;
+ auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override;
auto sensitivity() -> lua::Property&;
diff --git a/src/input/input_trigger.cpp b/src/tangara/input/input_trigger.cpp
index 9485ecb4..eb67bcca 100644
--- a/src/input/input_trigger.cpp
+++ b/src/tangara/input/input_trigger.cpp
@@ -4,38 +4,54 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "input_trigger.hpp"
-#include <sys/_stdint.h>
+#include "input/input_trigger.hpp"
#include <cstdint>
-#include "esp_log.h"
+
#include "esp_timer.h"
namespace input {
-Trigger::Trigger() : touch_time_ms_(), times_fired_(0) {}
+Trigger::Trigger()
+ : touch_time_ms_(),
+ was_pressed_(false),
+ was_double_click_(false),
+ times_long_pressed_(0) {}
auto Trigger::update(bool is_pressed) -> State {
// Bail out early if we're in a steady-state of not pressed.
- if (!is_pressed && !touch_time_ms_) {
+ if (!is_pressed && !was_pressed_) {
+ was_double_click_ = false;
+ times_long_pressed_ = 0;
return State::kNone;
}
uint64_t now_ms = esp_timer_get_time() / 1000;
- // Initial press of this key: record the current time, and report that we
- // haven't triggered yet.
- if (is_pressed && !touch_time_ms_) {
+ // This key wasn't being pressed, but now it is.
+ if (is_pressed && !was_pressed_) {
+ // Is this a double click?
+ if (now_ms - *touch_time_ms_ < kDoubleClickDelayMs) {
+ // Don't update touch_time_ms_, since we don't want triple clicks to
+ // register as double clicks.
+ was_double_click_ = true;
+ was_pressed_ = true;
+ return State::kDoubleClick;
+ }
+ // Not a double click; update our accounting info and wait for the next
+ // call.
touch_time_ms_ = now_ms;
- times_fired_ = 0;
+ was_double_click_ = false;
+ times_long_pressed_ = 0;
+ was_pressed_ = true;
return State::kNone;
}
// The key was released. If there were no long-press events fired during the
// press, then this was a standard click.
- if (!is_pressed && touch_time_ms_) {
- touch_time_ms_.reset();
- if (times_fired_ == 0) {
+ if (!is_pressed && was_pressed_) {
+ was_pressed_ = false;
+ if (!was_double_click_ && times_long_pressed_ == 0) {
return State::kClick;
} else {
return State::kNone;
@@ -43,10 +59,10 @@ auto Trigger::update(bool is_pressed) -> State {
}
// Now the more complicated case: the user is continuing to press the button.
- if (times_fired_ == 0) {
+ if (times_long_pressed_ == 0) {
// We haven't fired yet, so we wait for the long-press event.
if (now_ms - *touch_time_ms_ >= kLongPressDelayMs) {
- times_fired_++;
+ times_long_pressed_++;
return State::kLongPress;
}
} else {
@@ -60,8 +76,8 @@ auto Trigger::update(bool is_pressed) -> State {
// kRepeatDelayMs since the long-press event.
uint16_t expected_times_fired =
1 + (time_since_long_press / kRepeatDelayMs);
- if (times_fired_ < expected_times_fired) {
- times_fired_++;
+ if (times_long_pressed_ < expected_times_fired) {
+ times_long_pressed_++;
return State::kRepeatPress;
}
}
@@ -69,4 +85,11 @@ auto Trigger::update(bool is_pressed) -> State {
return State::kNone;
}
+auto Trigger::cancel() -> void {
+ touch_time_ms_.reset();
+ was_pressed_ = false;
+ was_double_click_ = false;
+ times_long_pressed_ = 0;
+}
+
} // namespace input
diff --git a/src/input/include/input_trigger.hpp b/src/tangara/input/input_trigger.hpp
index 599b796b..1b0e681d 100644
--- a/src/input/include/input_trigger.hpp
+++ b/src/tangara/input/input_trigger.hpp
@@ -13,6 +13,7 @@
namespace input {
+const uint16_t kDoubleClickDelayMs = 500;
const uint16_t kLongPressDelayMs = LV_INDEV_DEF_LONG_PRESS_TIME;
const uint16_t kRepeatDelayMs = LV_INDEV_DEF_LONG_PRESS_REP_TIME;
@@ -21,6 +22,7 @@ class Trigger {
enum class State {
kNone,
kClick,
+ kDoubleClick,
kLongPress,
kRepeatPress,
};
@@ -28,10 +30,14 @@ class Trigger {
Trigger();
auto update(bool is_pressed) -> State;
+ auto cancel() -> void;
private:
std::optional<uint64_t> touch_time_ms_;
- uint16_t times_fired_;
+ bool was_pressed_;
+
+ bool was_double_click_;
+ uint16_t times_long_pressed_;
};
} // namespace input
diff --git a/src/input/input_volume_buttons.cpp b/src/tangara/input/input_volume_buttons.cpp
index 607f81f1..3c3fb2a3 100644
--- a/src/input/input_volume_buttons.cpp
+++ b/src/tangara/input/input_volume_buttons.cpp
@@ -4,10 +4,10 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "input_volume_buttons.hpp"
-#include "event_queue.hpp"
-#include "gpios.hpp"
-#include "input_hook_actions.hpp"
+#include "input/input_volume_buttons.hpp"
+#include "drivers/gpios.hpp"
+#include "events/event_queue.hpp"
+#include "input/input_hook_actions.hpp"
namespace input {
@@ -25,7 +25,8 @@ auto VolumeButtons::name() -> std::string {
return "buttons";
}
-auto VolumeButtons::hooks() -> std::vector<TriggerHooks> {
+auto VolumeButtons::triggers()
+ -> std::vector<std::reference_wrapper<TriggerHooks>> {
return {up_, down_};
}
diff --git a/src/input/include/input_volume_buttons.hpp b/src/tangara/input/input_volume_buttons.hpp
index a684aa48..22a8acf2 100644
--- a/src/input/include/input_volume_buttons.hpp
+++ b/src/tangara/input/input_volume_buttons.hpp
@@ -8,13 +8,13 @@
#include <cstdint>
-#include "gpios.hpp"
+#include "drivers/gpios.hpp"
#include "hal/lv_hal_indev.h"
-#include "haptics.hpp"
-#include "input_device.hpp"
-#include "input_hook.hpp"
-#include "touchwheel.hpp"
+#include "drivers/haptics.hpp"
+#include "input/input_device.hpp"
+#include "input/input_hook.hpp"
+#include "drivers/touchwheel.hpp"
namespace input {
@@ -25,7 +25,7 @@ class VolumeButtons : public IInputDevice {
auto read(lv_indev_data_t* data) -> void override;
auto name() -> std::string override;
- auto hooks() -> std::vector<TriggerHooks> override;
+ auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override;
private:
drivers::IGpios& gpios_;
diff --git a/src/tangara/input/lvgl_input_driver.cpp b/src/tangara/input/lvgl_input_driver.cpp
new file mode 100644
index 00000000..8d10bb13
--- /dev/null
+++ b/src/tangara/input/lvgl_input_driver.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "input/lvgl_input_driver.hpp"
+
+#include <cstdint>
+#include <memory>
+#include <variant>
+
+#include "lua.hpp"
+#include "lvgl.h"
+
+#include "drivers/nvs.hpp"
+
+#include "input/device_factory.hpp"
+#include "input/feedback_haptics.hpp"
+#include "input/input_hook.hpp"
+#include "input/input_touch_wheel.hpp"
+#include "input/input_trigger.hpp"
+#include "input/input_volume_buttons.hpp"
+#include "lua/lua_thread.hpp"
+#include "lua/property.hpp"
+
+[[maybe_unused]] static constexpr char kTag[] = "input";
+
+static constexpr char kLuaTriggerMetatableName[] = "input_trigger";
+static constexpr char kLuaOverrideText[] = "lua_callback";
+
+namespace input {
+
+static void read_cb(lv_indev_drv_t* drv, lv_indev_data_t* data) {
+ LvglInputDriver* instance =
+ reinterpret_cast<LvglInputDriver*>(drv->user_data);
+ instance->read(data);
+}
+
+static void feedback_cb(lv_indev_drv_t* drv, uint8_t event) {
+ LvglInputDriver* instance =
+ reinterpret_cast<LvglInputDriver*>(drv->user_data);
+ instance->feedback(event);
+}
+
+auto intToMode(int raw) -> std::optional<drivers::NvsStorage::InputModes> {
+ switch (raw) {
+ case 0:
+ return drivers::NvsStorage::InputModes::kButtonsOnly;
+ case 1:
+ return drivers::NvsStorage::InputModes::kButtonsWithWheel;
+ case 2:
+ return drivers::NvsStorage::InputModes::kDirectionalWheel;
+ case 3:
+ return drivers::NvsStorage::InputModes::kRotatingWheel;
+ default:
+ return {};
+ }
+}
+
+LvglInputDriver::LvglInputDriver(drivers::NvsStorage& nvs,
+ DeviceFactory& factory)
+ : nvs_(nvs),
+ factory_(factory),
+ mode_(static_cast<int>(nvs.PrimaryInput()),
+ [&](const lua::LuaValue& val) {
+ if (!std::holds_alternative<int>(val)) {
+ return false;
+ }
+ auto mode = intToMode(std::get<int>(val));
+ if (!mode) {
+ return false;
+ }
+ nvs.PrimaryInput(*mode);
+ inputs_ = factory.createInputs(*mode);
+ return true;
+ }),
+ driver_(),
+ registration_(nullptr),
+ inputs_(factory.createInputs(nvs.PrimaryInput())),
+ feedbacks_(factory.createFeedbacks()),
+ is_locked_(false) {
+ lv_indev_drv_init(&driver_);
+ driver_.type = LV_INDEV_TYPE_ENCODER;
+ driver_.read_cb = read_cb;
+ driver_.feedback_cb = feedback_cb;
+ driver_.user_data = this;
+ driver_.long_press_time = kLongPressDelayMs;
+ driver_.long_press_repeat_time = kRepeatDelayMs;
+
+ registration_ = lv_indev_drv_register(&driver_);
+}
+
+auto LvglInputDriver::read(lv_indev_data_t* data) -> void {
+ // TODO: we should pass lock state on to the individual devices, since they
+ // may wish to either ignore the lock state, or power down until unlock.
+ if (is_locked_) {
+ return;
+ }
+ for (auto&& device : inputs_) {
+ device->read(data);
+ }
+}
+
+auto LvglInputDriver::feedback(uint8_t event) -> void {
+ if (is_locked_) {
+ return;
+ }
+ for (auto&& device : feedbacks_) {
+ device->feedback(event);
+ }
+}
+
+LvglInputDriver::LuaTrigger::LuaTrigger(LvglInputDriver& driver,
+ IInputDevice& dev,
+ TriggerHooks& trigger)
+ : driver_(&driver), device_(dev.name()), trigger_(trigger.name()) {
+ for (auto& hook : trigger.hooks()) {
+ auto cb = hook.get().callback();
+ if (cb) {
+ hooks_[hook.get().name()] = hook.get().callback()->name;
+ } else {
+ hooks_[hook.get().name()] = "";
+ }
+ }
+}
+
+auto LvglInputDriver::LuaTrigger::get(lua_State* L, int idx) -> LuaTrigger& {
+ return **reinterpret_cast<LuaTrigger**>(
+ luaL_checkudata(L, idx, kLuaTriggerMetatableName));
+}
+
+auto LvglInputDriver::LuaTrigger::luaGc(lua_State* L) -> int {
+ LuaTrigger& trigger = LuaTrigger::get(L, 1);
+ delete &trigger;
+ return 0;
+}
+
+auto LvglInputDriver::LuaTrigger::luaToString(lua_State* L) -> int {
+ LuaTrigger& trigger = LuaTrigger::get(L, 1);
+ std::stringstream out;
+ out << "{ ";
+ for (const auto& hook : trigger.hooks_) {
+ if (!hook.second.empty()) {
+ out << hook.first << "=" << hook.second << " ";
+ }
+ }
+ out << "}";
+ lua_pushlstring(L, out.str().data(), out.str().size());
+ return 1;
+}
+
+auto LvglInputDriver::LuaTrigger::luaNewIndex(lua_State* L) -> int {
+ LuaTrigger& trigger = LuaTrigger::get(L, 1);
+ luaL_checktype(L, 3, LUA_TFUNCTION);
+
+ size_t len = 0;
+ const char* str = luaL_checklstring(L, 2, &len);
+ if (!str) {
+ return 0;
+ }
+ OverrideSelector selector{
+ .device_name = trigger.device_,
+ .trigger_name = trigger.trigger_,
+ .hook_name = std::string{str, len},
+ };
+ for (const auto& hook : trigger.hooks_) {
+ if (hook.first == selector.hook_name) {
+ trigger.driver_->setOverride(L, selector);
+ trigger.hooks_[hook.first] = kLuaOverrideText;
+ return 0;
+ }
+ }
+ return 0;
+}
+
+auto LvglInputDriver::pushHooks(lua_State* L) -> int {
+ if (luaL_getmetatable(L, kLuaTriggerMetatableName) == LUA_TNIL) {
+ luaL_newmetatable(L, kLuaTriggerMetatableName);
+ luaL_setfuncs(L, LuaTrigger::kFuncs, 0);
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 1);
+
+ lua_newtable(L);
+
+ for (auto& dev : inputs_) {
+ lua_pushlstring(L, dev->name().data(), dev->name().size());
+ lua_newtable(L);
+
+ for (auto& trigger : dev->triggers()) {
+ lua_pushlstring(L, trigger.get().name().data(),
+ trigger.get().name().size());
+ LuaTrigger** lua_obj = reinterpret_cast<LuaTrigger**>(
+ lua_newuserdatauv(L, sizeof(LuaTrigger*), 0));
+ *lua_obj = new LuaTrigger(*this, *dev, trigger);
+ luaL_setmetatable(L, kLuaTriggerMetatableName);
+ lua_rawset(L, -3);
+ }
+
+ lua_rawset(L, -3);
+ }
+
+ return 1;
+}
+
+auto LvglInputDriver::setOverride(lua_State* L,
+ const OverrideSelector& selector) -> void {
+ if (overrides_.contains(selector)) {
+ LuaOverride& prev = overrides_[selector];
+ luaL_unref(prev.L, LUA_REGISTRYINDEX, prev.ref);
+ }
+
+ int ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ LuaOverride override{
+ .L = L,
+ .ref = ref,
+ };
+ overrides_[selector] = override;
+ applyOverride(selector, override);
+}
+
+auto LvglInputDriver::applyOverride(const OverrideSelector& selector,
+ LuaOverride& override) -> void {
+ // In general, this algorithm is a very slow approach. We could do better
+ // by maintaing maps from [device|trigger|hook]_name to the relevant
+ // trigger, but in practice I expect maybe like 5 overrides total ever,
+ // spread across 2 devices with 2 or 5 hooks each. So it's not that big a
+ // deal. Don't worry about it!!
+
+ // Look for a matching device.
+ for (auto& device : inputs_) {
+ if (device->name() != selector.device_name) {
+ continue;
+ }
+ // Look for a matching trigger
+ for (auto& trigger : device->triggers()) {
+ if (trigger.get().name() != selector.trigger_name) {
+ continue;
+ }
+ // Look for a matching hook
+ for (auto& hook : trigger.get().hooks()) {
+ if (hook.get().name() != selector.hook_name) {
+ continue;
+ }
+ // We found the target! Apply the override.
+ auto lua_callback = [=](lv_indev_data_t* d) {
+ lua_rawgeti(override.L, LUA_REGISTRYINDEX, override.ref);
+ lua::CallProtected(override.L, 0, 0);
+ };
+ hook.get().override(
+ HookCallback{.name = kLuaOverrideText, .fn = lua_callback});
+ }
+ }
+ }
+}
+
+} // namespace input
diff --git a/src/tangara/input/lvgl_input_driver.hpp b/src/tangara/input/lvgl_input_driver.hpp
new file mode 100644
index 00000000..8ede1855
--- /dev/null
+++ b/src/tangara/input/lvgl_input_driver.hpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <deque>
+#include <memory>
+#include <set>
+
+#include "core/lv_group.h"
+#include "drivers/gpios.hpp"
+#include "hal/lv_hal_indev.h"
+#include "input/device_factory.hpp"
+#include "input/feedback_device.hpp"
+
+#include "input/input_device.hpp"
+#include "input/input_hook.hpp"
+#include "lua/lua_thread.hpp"
+#include "lua/property.hpp"
+#include "drivers/nvs.hpp"
+#include "drivers/touchwheel.hpp"
+
+namespace input {
+
+/*
+ * Implementation of an LVGL input device. This class composes multiple
+ * IInputDevice and IFeedbackDevice instances together into a single LVGL
+ * device.
+ */
+class LvglInputDriver {
+ public:
+ LvglInputDriver(drivers::NvsStorage& nvs, DeviceFactory&);
+
+ auto mode() -> lua::Property& { return mode_; }
+
+ auto read(lv_indev_data_t* data) -> void;
+ auto feedback(uint8_t) -> void;
+
+ auto registration() -> lv_indev_t* { return registration_; }
+ auto lock(bool l) -> void { is_locked_ = l; }
+
+ auto pushHooks(lua_State* L) -> int;
+
+ private:
+ drivers::NvsStorage& nvs_;
+ DeviceFactory& factory_;
+
+ lua::Property mode_;
+ lv_indev_drv_t driver_;
+ lv_indev_t* registration_;
+
+ std::vector<std::shared_ptr<IInputDevice>> inputs_;
+ std::vector<std::shared_ptr<IFeedbackDevice>> feedbacks_;
+
+ /*
+ * Key for identifying which device, trigger, and specific hook are being
+ * overriden by Lua.
+ */
+ struct OverrideSelector {
+ std::string device_name;
+ std::string trigger_name;
+ std::string hook_name;
+
+ friend bool operator<(const OverrideSelector& l,
+ const OverrideSelector& r) {
+ return std::tie(l.device_name, l.trigger_name, l.hook_name) <
+ std::tie(r.device_name, r.trigger_name, r.hook_name);
+ }
+ };
+
+ /* Userdata object for tracking the Lua mirror of a TriggerHooks object. */
+ class LuaTrigger {
+ public:
+ LuaTrigger(LvglInputDriver&, IInputDevice&, TriggerHooks&);
+
+ static auto get(lua_State*, int idx) -> LuaTrigger&;
+ static auto luaGc(lua_State*) -> int;
+ static auto luaToString(lua_State*) -> int;
+ static auto luaNewIndex(lua_State*) -> int;
+
+ static constexpr struct luaL_Reg kFuncs[] = {{"__gc", luaGc},
+ {"__tostring", luaToString},
+ {"__newindex", luaNewIndex},
+ {NULL, NULL}};
+
+ private:
+ LvglInputDriver* driver_;
+
+ std::string device_;
+ std::string trigger_;
+ std::map<std::string, std::string> hooks_;
+ };
+
+ /* A hook override implemented as a lua callback */
+ struct LuaOverride {
+ lua_State* L;
+ int ref;
+ };
+
+ std::map<OverrideSelector, LuaOverride> overrides_;
+
+ auto setOverride(lua_State* L, const OverrideSelector&) -> void;
+ auto applyOverride(const OverrideSelector&, LuaOverride&) -> void;
+
+ bool is_locked_;
+};
+
+} // namespace input
diff --git a/src/lua/bridge.cpp b/src/tangara/lua/bridge.cpp
index a84eb0c1..07c299a7 100644
--- a/src/lua/bridge.cpp
+++ b/src/tangara/lua/bridge.cpp
@@ -4,34 +4,33 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "bridge.hpp"
-#include <sys/_stdint.h>
+#include "lua/bridge.hpp"
+#include <cstdint>
#include <memory>
#include <string>
-#include "database.hpp"
+#include "database/database.hpp"
+#include "database/index.hpp"
#include "esp_log.h"
-#include "index.hpp"
#include "lauxlib.h"
#include "lua.h"
#include "lua.hpp"
-#include "lua_controls.hpp"
-#include "lua_database.hpp"
-#include "lua_filesystem.hpp"
-#include "lua_queue.hpp"
-#include "lua_screen.hpp"
-#include "lua_version.hpp"
-#include "lua_theme.hpp"
+#include "lua/lua_controls.hpp"
+#include "lua/lua_database.hpp"
+#include "lua/lua_queue.hpp"
+#include "lua/lua_screen.hpp"
+#include "lua/lua_theme.hpp"
+#include "lua/lua_version.hpp"
#include "lvgl.h"
#include "font/lv_font_loader.h"
#include "luavgl.h"
-#include "event_queue.hpp"
-#include "property.hpp"
-#include "service_locator.hpp"
-#include "ui_events.hpp"
+#include "events/event_queue.hpp"
+#include "lua/property.hpp"
+#include "system_fsm/service_locator.hpp"
+#include "ui/ui_events.hpp"
extern "C" {
int luaopen_linenoise(lua_State* L);
@@ -47,8 +46,9 @@ namespace lua {
static constexpr char kBridgeKey[] = "bridge";
-static auto make_font_cb(const char* name, int size, int weight)
- -> const lv_font_t* {
+static auto make_font_cb(const char* name,
+ int size,
+ int weight) -> const lv_font_t* {
if (std::string{"fusion"} == name) {
if (size == 12) {
return &font_fusion_12;
@@ -85,7 +85,6 @@ auto Bridge::installBaseModules(lua_State* L) -> void {
RegisterControlsModule(L);
RegisterDatabaseModule(L);
- RegisterFileSystemModule(L);
RegisterQueueModule(L);
RegisterVersionModule(L);
RegisterThemeModule(L);
diff --git a/src/lua/include/bridge.hpp b/src/tangara/lua/bridge.hpp
index 64f14e0e..b4cfe503 100644
--- a/src/lua/include/bridge.hpp
+++ b/src/tangara/lua/bridge.hpp
@@ -10,9 +10,9 @@
#include <string>
#include "lua.hpp"
+#include "lua/property.hpp"
#include "lvgl.h"
-#include "property.hpp"
-#include "service_locator.hpp"
+#include "system_fsm/service_locator.hpp"
namespace lua {
@@ -39,9 +39,8 @@ class Bridge {
auto installPropertyModule(
lua_State* L,
const std::string&,
- std::vector<
- std::pair<std::string, std::variant<LuaFunction, Property*>>>&)
- -> void;
+ std::vector<std::pair<std::string,
+ std::variant<LuaFunction, Property*>>>&) -> void;
Bridge(const Bridge&) = delete;
Bridge& operator=(const Bridge&) = delete;
diff --git a/src/lua/file_iterator.cpp b/src/tangara/lua/file_iterator.cpp
index 194859a6..194859a6 100644
--- a/src/lua/file_iterator.cpp
+++ b/src/tangara/lua/file_iterator.cpp
diff --git a/src/lua/include/file_iterator.hpp b/src/tangara/lua/file_iterator.hpp
index 82d6f397..82d6f397 100644
--- a/src/lua/include/file_iterator.hpp
+++ b/src/tangara/lua/file_iterator.hpp
diff --git a/src/lua/lua_controls.cpp b/src/tangara/lua/lua_controls.cpp
index 2da0ed11..baf40891 100644
--- a/src/lua/lua_controls.cpp
+++ b/src/tangara/lua/lua_controls.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "lua_controls.hpp"
+#include "lua/lua_controls.hpp"
#include <memory>
#include <string>
@@ -16,8 +16,8 @@
#include "lua.h"
#include "lvgl.h"
-#include "nvs.hpp"
-#include "ui_events.hpp"
+#include "drivers/nvs.hpp"
+#include "ui/ui_events.hpp"
namespace lua {
diff --git a/src/lua/include/lua_controls.hpp b/src/tangara/lua/lua_controls.hpp
index 18ad261d..18ad261d 100644
--- a/src/lua/include/lua_controls.hpp
+++ b/src/tangara/lua/lua_controls.hpp
diff --git a/src/lua/lua_database.cpp b/src/tangara/lua/lua_database.cpp
index d0612fdd..1afb01f0 100644
--- a/src/lua/lua_database.cpp
+++ b/src/tangara/lua/lua_database.cpp
@@ -4,30 +4,30 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "lua_database.hpp"
+#include "lua/lua_database.hpp"
#include <memory>
#include <string>
#include <type_traits>
#include <variant>
-#include "bridge.hpp"
#include "lua.hpp"
+#include "lua/bridge.hpp"
#include "esp_log.h"
#include "lauxlib.h"
#include "lua.h"
-#include "lua_thread.hpp"
+#include "lua/lua_thread.hpp"
#include "lvgl.h"
-#include "database.hpp"
-#include "event_queue.hpp"
-#include "index.hpp"
-#include "property.hpp"
-#include "records.hpp"
-#include "service_locator.hpp"
-#include "track.hpp"
-#include "ui_events.hpp"
+#include "database/database.hpp"
+#include "database/index.hpp"
+#include "database/records.hpp"
+#include "database/track.hpp"
+#include "events/event_queue.hpp"
+#include "lua/property.hpp"
+#include "system_fsm/service_locator.hpp"
+#include "ui/ui_events.hpp"
namespace lua {
@@ -152,8 +152,8 @@ auto db_check_iterator(lua_State* L, int stack_pos) -> database::Iterator* {
return it;
}
-static auto push_iterator(lua_State* state, const database::Iterator& it)
- -> void {
+static auto push_iterator(lua_State* state,
+ const database::Iterator& it) -> void {
database::Iterator** data = reinterpret_cast<database::Iterator**>(
lua_newuserdata(state, sizeof(uintptr_t)));
*data = new database::Iterator(it);
@@ -198,12 +198,10 @@ static auto db_iterator_gc(lua_State* state) -> int {
return 0;
}
-static const struct luaL_Reg kDbIteratorFuncs[] = {{"next", db_iterate},
- {"prev", db_iterate_prev},
- {"clone", db_iterator_clone},
- {"__call", db_iterate},
- {"__gc", db_iterator_gc},
- {NULL, NULL}};
+static const struct luaL_Reg kDbIteratorFuncs[] = {
+ {"next", db_iterate}, {"prev", db_iterate_prev},
+ {"clone", db_iterator_clone}, {"__call", db_iterate},
+ {"__gc", db_iterator_gc}, {NULL, NULL}};
static auto record_text(lua_State* state) -> int {
LuaRecord* data = reinterpret_cast<LuaRecord*>(
diff --git a/src/lua/include/lua_database.hpp b/src/tangara/lua/lua_database.hpp
index b0d2acbd..328004ef 100644
--- a/src/lua/include/lua_database.hpp
+++ b/src/tangara/lua/lua_database.hpp
@@ -8,7 +8,7 @@
#include "lua.hpp"
-#include "database.hpp"
+#include "database/database.hpp"
namespace lua {
diff --git a/src/lua/lua_filesystem.cpp b/src/tangara/lua/lua_filesystem.cpp
index f0dbaf9a..f0dbaf9a 100644
--- a/src/lua/lua_filesystem.cpp
+++ b/src/tangara/lua/lua_filesystem.cpp
diff --git a/src/lua/include/lua_filesystem.hpp b/src/tangara/lua/lua_filesystem.hpp
index 2a829405..2a829405 100644
--- a/src/lua/include/lua_filesystem.hpp
+++ b/src/tangara/lua/lua_filesystem.hpp
diff --git a/src/lua/lua_queue.cpp b/src/tangara/lua/lua_queue.cpp
index dfb820c2..bc393aa5 100644
--- a/src/lua/lua_queue.cpp
+++ b/src/tangara/lua/lua_queue.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "lua_database.hpp"
+#include "lua/lua_database.hpp"
#include <memory>
#include <string>
@@ -16,15 +16,15 @@
#include "lua.h"
#include "lvgl.h"
-#include "bridge.hpp"
-#include "database.hpp"
-#include "event_queue.hpp"
-#include "index.hpp"
-#include "property.hpp"
-#include "service_locator.hpp"
-#include "track.hpp"
-#include "track_queue.hpp"
-#include "ui_events.hpp"
+#include "audio/track_queue.hpp"
+#include "database/database.hpp"
+#include "database/index.hpp"
+#include "database/track.hpp"
+#include "events/event_queue.hpp"
+#include "lua/bridge.hpp"
+#include "lua/property.hpp"
+#include "system_fsm/service_locator.hpp"
+#include "ui/ui_events.hpp"
namespace lua {
diff --git a/src/lua/include/lua_queue.hpp b/src/tangara/lua/lua_queue.hpp
index c5cfe04d..c5cfe04d 100644
--- a/src/lua/include/lua_queue.hpp
+++ b/src/tangara/lua/lua_queue.hpp
diff --git a/src/lua/include/lua_registry.hpp b/src/tangara/lua/lua_registry.hpp
index abc5063e..e556b6eb 100644
--- a/src/lua/include/lua_registry.hpp
+++ b/src/tangara/lua/lua_registry.hpp
@@ -11,9 +11,9 @@
#include "lua.hpp"
-#include "bridge.hpp"
-#include "lua_thread.hpp"
-#include "service_locator.hpp"
+#include "lua/bridge.hpp"
+#include "lua/lua_thread.hpp"
+#include "system_fsm/service_locator.hpp"
namespace lua {
diff --git a/src/lua/lua_screen.cpp b/src/tangara/lua/lua_screen.cpp
index f17f6b1a..8d87eebd 100644
--- a/src/lua/lua_screen.cpp
+++ b/src/tangara/lua/lua_screen.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "lua_screen.hpp"
+#include "lua/lua_screen.hpp"
#include <memory>
#include <string>
@@ -16,15 +16,15 @@
#include "lua.h"
#include "lvgl.h"
-#include "bridge.hpp"
-#include "database.hpp"
-#include "event_queue.hpp"
-#include "index.hpp"
-#include "property.hpp"
-#include "service_locator.hpp"
-#include "track.hpp"
-#include "track_queue.hpp"
-#include "ui_events.hpp"
+#include "audio/track_queue.hpp"
+#include "database/database.hpp"
+#include "database/index.hpp"
+#include "database/track.hpp"
+#include "events/event_queue.hpp"
+#include "lua/bridge.hpp"
+#include "lua/property.hpp"
+#include "system_fsm/service_locator.hpp"
+#include "ui/ui_events.hpp"
namespace lua {
diff --git a/src/lua/include/lua_screen.hpp b/src/tangara/lua/lua_screen.hpp
index 1c3bed1a..1c3bed1a 100644
--- a/src/lua/include/lua_screen.hpp
+++ b/src/tangara/lua/lua_screen.hpp
diff --git a/src/lua/lua_theme.cpp b/src/tangara/lua/lua_theme.cpp
index 72434d97..5edde104 100644
--- a/src/lua/lua_theme.cpp
+++ b/src/tangara/lua/lua_theme.cpp
@@ -5,20 +5,20 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "lua_version.hpp"
+#include "lua/lua_version.hpp"
#include <string>
-#include "bridge.hpp"
#include "lua.hpp"
+#include "lua/bridge.hpp"
#include "esp_app_desc.h"
#include "esp_log.h"
#include "lauxlib.h"
#include "lua.h"
-#include "lua_thread.hpp"
+#include "lua/lua_thread.hpp"
#include "luavgl.h"
-#include "themes.hpp"
+#include "ui/themes.hpp"
namespace lua {
@@ -35,7 +35,7 @@ static auto set_style(lua_State* L) -> int {
static auto set_theme(lua_State* L) -> int {
std::string class_name;
luaL_checktype(L, -1, LUA_TTABLE);
- lua_pushnil(L); /* first key */
+ lua_pushnil(L); /* first key */
while (lua_next(L, -2) != 0) {
/* uses 'key' (at index -2) and 'value' (at index -1) */
if (lua_type(L, -2) == LUA_TSTRING) {
@@ -43,11 +43,11 @@ static auto set_theme(lua_State* L) -> int {
}
if (lua_type(L, -1) == LUA_TTABLE) {
// Nesting
- lua_pushnil(L); // First key
+ lua_pushnil(L); // First key
while (lua_next(L, -2) != 0) {
// Nesting the second
int selector = -1;
- lua_pushnil(L); // First key
+ lua_pushnil(L); // First key
while (lua_next(L, -2) != 0) {
int idx = lua_tointeger(L, -2);
if (idx == 1) {
@@ -60,12 +60,13 @@ static auto set_theme(lua_State* L) -> int {
ESP_LOGI("lua_theme", "Style was null or malformed");
return 0;
} else {
- ui::themes::Theme::instance()->AddStyle(class_name, selector, style);
+ ui::themes::Theme::instance()->AddStyle(class_name, selector,
+ style);
}
}
- lua_pop(L, 1);
+ lua_pop(L, 1);
}
- lua_pop(L, 1);
+ lua_pop(L, 1);
}
}
/* removes 'value'; keeps 'key' for next iteration */
@@ -74,7 +75,9 @@ static auto set_theme(lua_State* L) -> int {
return 0;
}
-static const struct luaL_Reg kThemeFuncs[] = {{"set", set_theme}, {"set_style", set_style}, {NULL, NULL}};
+static const struct luaL_Reg kThemeFuncs[] = {{"set", set_theme},
+ {"set_style", set_style},
+ {NULL, NULL}};
static auto lua_theme(lua_State* L) -> int {
luaL_newlib(L, kThemeFuncs);
diff --git a/src/lua/include/lua_theme.hpp b/src/tangara/lua/lua_theme.hpp
index fed710e0..fed710e0 100644
--- a/src/lua/include/lua_theme.hpp
+++ b/src/tangara/lua/lua_theme.hpp
diff --git a/src/lua/lua_thread.cpp b/src/tangara/lua/lua_thread.cpp
index dd72e41d..77a46b45 100644
--- a/src/lua/lua_thread.cpp
+++ b/src/tangara/lua/lua_thread.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "lua_thread.hpp"
+#include "lua/lua_thread.hpp"
#include <iostream>
#include <memory>
@@ -13,11 +13,11 @@
#include "esp_log.h"
#include "lua.hpp"
-#include "bridge.hpp"
-#include "event_queue.hpp"
+#include "events/event_queue.hpp"
+#include "lua/bridge.hpp"
#include "memory_resource.hpp"
-#include "service_locator.hpp"
-#include "ui_events.hpp"
+#include "system_fsm/service_locator.hpp"
+#include "ui/ui_events.hpp"
namespace lua {
@@ -42,8 +42,10 @@ class Allocator {
size_t total_allocated_;
};
-static auto lua_alloc(void* ud, void* ptr, size_t osize, size_t nsize)
- -> void* {
+static auto lua_alloc(void* ud,
+ void* ptr,
+ size_t osize,
+ size_t nsize) -> void* {
Allocator* instance = reinterpret_cast<Allocator*>(ud);
return instance->alloc(ptr, osize, nsize);
}
diff --git a/src/lua/include/lua_thread.hpp b/src/tangara/lua/lua_thread.hpp
index 384de61d..d7602c1e 100644
--- a/src/lua/include/lua_thread.hpp
+++ b/src/tangara/lua/lua_thread.hpp
@@ -11,7 +11,7 @@
#include "lua.hpp"
-#include "service_locator.hpp"
+#include "system_fsm/service_locator.hpp"
namespace lua {
diff --git a/src/lua/lua_version.cpp b/src/tangara/lua/lua_version.cpp
index e5f06bb5..b85a30a5 100644
--- a/src/lua/lua_version.cpp
+++ b/src/tangara/lua/lua_version.cpp
@@ -5,18 +5,18 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "lua_version.hpp"
+#include "lua/lua_version.hpp"
#include <string>
-#include "bridge.hpp"
#include "lua.hpp"
+#include "lua/bridge.hpp"
#include "esp_app_desc.h"
#include "esp_log.h"
#include "lauxlib.h"
#include "lua.h"
-#include "lua_thread.hpp"
+#include "lua/lua_thread.hpp"
namespace lua {
diff --git a/src/lua/include/lua_version.hpp b/src/tangara/lua/lua_version.hpp
index 4ba4be94..4ba4be94 100644
--- a/src/lua/include/lua_version.hpp
+++ b/src/tangara/lua/lua_version.hpp
diff --git a/src/lua/property.cpp b/src/tangara/lua/property.cpp
index 634a6a26..2b93809d 100644
--- a/src/lua/property.cpp
+++ b/src/tangara/lua/property.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "property.hpp"
+#include "lua/property.hpp"
#include <sys/_stdint.h>
#include <cmath>
@@ -14,15 +14,15 @@
#include <string>
#include <variant>
-#include "bluetooth_types.hpp"
+#include "database/track.hpp"
+#include "drivers/bluetooth_types.hpp"
#include "lauxlib.h"
#include "lua.h"
#include "lua.hpp"
-#include "lua_thread.hpp"
+#include "lua/lua_thread.hpp"
#include "lvgl.h"
#include "memory_resource.hpp"
-#include "service_locator.hpp"
-#include "track.hpp"
+#include "system_fsm/service_locator.hpp"
#include "types.hpp"
namespace lua {
@@ -247,7 +247,7 @@ static auto pushTagValue(lua_State* L, const database::TagValue& val) -> void {
if constexpr (std::is_same_v<T, std::pmr::string>) {
lua_pushlstring(L, arg.data(), arg.size());
} else if constexpr (std::is_same_v<
- T, cpp::span<const std::pmr::string>>) {
+ T, std::span<const std::pmr::string>>) {
lua_createtable(L, 0, arg.size());
for (const auto& i : arg) {
lua_pushlstring(L, i.data(), i.size());
diff --git a/src/lua/include/property.hpp b/src/tangara/lua/property.hpp
index 724261be..9f925766 100644
--- a/src/lua/include/property.hpp
+++ b/src/tangara/lua/property.hpp
@@ -10,11 +10,11 @@
#include <memory>
#include <string>
-#include "audio_events.hpp"
-#include "bluetooth_types.hpp"
+#include "audio/audio_events.hpp"
+#include "drivers/bluetooth_types.hpp"
#include "lua.hpp"
#include "lvgl.h"
-#include "service_locator.hpp"
+#include "system_fsm/service_locator.hpp"
namespace lua {
diff --git a/src/lua/registry.cpp b/src/tangara/lua/registry.cpp
index a6487858..d33594a3 100644
--- a/src/lua/registry.cpp
+++ b/src/tangara/lua/registry.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "lua_registry.hpp"
+#include "lua/lua_registry.hpp"
#include <iostream>
#include <memory>
@@ -13,11 +13,11 @@
#include "esp_log.h"
#include "lua.hpp"
-#include "bridge.hpp"
-#include "event_queue.hpp"
+#include "events/event_queue.hpp"
+#include "lua/bridge.hpp"
#include "memory_resource.hpp"
-#include "service_locator.hpp"
-#include "ui_events.hpp"
+#include "system_fsm/service_locator.hpp"
+#include "ui/ui_events.hpp"
namespace lua {
diff --git a/src/system_fsm/booting.cpp b/src/tangara/system_fsm/booting.cpp
index 44700cc4..58f14706 100644
--- a/src/system_fsm/booting.cpp
+++ b/src/tangara/system_fsm/booting.cpp
@@ -5,9 +5,9 @@
*/
#include "collation.hpp"
-#include "haptics.hpp"
-#include "spiffs.hpp"
-#include "system_fsm.hpp"
+#include "drivers/haptics.hpp"
+#include "drivers/spiffs.hpp"
+#include "system_fsm/system_fsm.hpp"
#include <stdint.h>
#include <memory>
@@ -20,25 +20,25 @@
#include "freertos/projdefs.h"
#include "freertos/timers.h"
-#include "adc.hpp"
-#include "audio_fsm.hpp"
-#include "battery.hpp"
-#include "bluetooth.hpp"
-#include "bluetooth_types.hpp"
-#include "display_init.hpp"
-#include "event_queue.hpp"
-#include "gpios.hpp"
-#include "i2c.hpp"
-#include "nvs.hpp"
-#include "samd.hpp"
-#include "service_locator.hpp"
-#include "spi.hpp"
-#include "system_events.hpp"
-#include "tag_parser.hpp"
+#include "audio/audio_fsm.hpp"
+#include "audio/track_queue.hpp"
+#include "battery/battery.hpp"
+#include "database/tag_parser.hpp"
+#include "drivers/adc.hpp"
+#include "drivers/bluetooth.hpp"
+#include "drivers/bluetooth_types.hpp"
+#include "drivers/display_init.hpp"
+#include "drivers/gpios.hpp"
+#include "drivers/i2c.hpp"
+#include "drivers/nvs.hpp"
+#include "drivers/samd.hpp"
+#include "drivers/spi.hpp"
+#include "drivers/touchwheel.hpp"
+#include "events/event_queue.hpp"
+#include "system_fsm/service_locator.hpp"
+#include "system_fsm/system_events.hpp"
#include "tasks.hpp"
-#include "touchwheel.hpp"
-#include "track_queue.hpp"
-#include "ui_fsm.hpp"
+#include "ui/ui_fsm.hpp"
namespace system_fsm {
namespace states {
diff --git a/src/system_fsm/idle.cpp b/src/tangara/system_fsm/idle.cpp
index e28864b3..e499693d 100644
--- a/src/system_fsm/idle.cpp
+++ b/src/tangara/system_fsm/idle.cpp
@@ -4,20 +4,20 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "app_console.hpp"
-#include "file_gatherer.hpp"
+#include "app_console/app_console.hpp"
+#include "database/file_gatherer.hpp"
+#include "drivers/gpios.hpp"
#include "freertos/portmacro.h"
#include "freertos/projdefs.h"
-#include "gpios.hpp"
#include "result.hpp"
-#include "audio_fsm.hpp"
-#include "event_queue.hpp"
-#include "samd.hpp"
-#include "storage.hpp"
-#include "system_events.hpp"
-#include "system_fsm.hpp"
-#include "ui_fsm.hpp"
+#include "audio/audio_fsm.hpp"
+#include "drivers/samd.hpp"
+#include "drivers/storage.hpp"
+#include "events/event_queue.hpp"
+#include "system_fsm/system_events.hpp"
+#include "system_fsm/system_fsm.hpp"
+#include "ui/ui_fsm.hpp"
namespace system_fsm {
namespace states {
diff --git a/src/system_fsm/running.cpp b/src/tangara/system_fsm/running.cpp
index 796c96dc..ac36ec64 100644
--- a/src/system_fsm/running.cpp
+++ b/src/tangara/system_fsm/running.cpp
@@ -4,23 +4,24 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "app_console.hpp"
-#include "audio_events.hpp"
-#include "database.hpp"
-#include "db_events.hpp"
+#include "app_console/app_console.hpp"
+#include "audio/audio_events.hpp"
+#include "database/database.hpp"
+#include "database/db_events.hpp"
+#include "database/file_gatherer.hpp"
+#include "drivers/gpios.hpp"
+#include "drivers/spi.hpp"
#include "ff.h"
-#include "file_gatherer.hpp"
#include "freertos/portmacro.h"
#include "freertos/projdefs.h"
-#include "gpios.hpp"
#include "result.hpp"
-#include "audio_fsm.hpp"
-#include "event_queue.hpp"
-#include "storage.hpp"
-#include "system_events.hpp"
-#include "system_fsm.hpp"
-#include "ui_fsm.hpp"
+#include "audio/audio_fsm.hpp"
+#include "drivers/storage.hpp"
+#include "events/event_queue.hpp"
+#include "system_fsm/system_events.hpp"
+#include "system_fsm/system_fsm.hpp"
+#include "ui/ui_fsm.hpp"
namespace system_fsm {
namespace states {
@@ -42,11 +43,7 @@ void Running::entry() {
sUnmountTimer = xTimerCreate("unmount_timeout", kTicksBeforeUnmount, false,
NULL, timer_callback);
}
- // Only mount our storage immediately if we know it's not currently in use
- // by the SAMD.
- if (!sServices->samd().UsbMassStorage()) {
- mountStorage();
- }
+ mountStorage();
}
void Running::exit() {
@@ -80,10 +77,28 @@ void Running::react(const SdDetectChanged& ev) {
if (ev.has_sd_card && !sStorage) {
mountStorage();
}
+
// Don't automatically unmount, since this event seems to occasionally happen
// supriously. FIXME: Why?
- // (It doesn't matter too much; by the time we get this event the SD card has
- // already been disconnected electrically.)
+ // Instead, check whether or not the card has actually gone away.
+ if (sStorage) {
+ FRESULT res;
+ FF_DIR dir;
+ {
+ auto lock = drivers::acquire_spi();
+ res = f_opendir(&dir, "/");
+ }
+
+ if (res != FR_OK) {
+ ESP_LOGW(kTag, "sd card ejected unsafely!");
+ unmountStorage();
+ }
+
+ {
+ auto lock = drivers::acquire_spi();
+ f_closedir(&dir);
+ }
+ }
}
void Running::react(const SamdUsbMscChanged& ev) {
@@ -134,25 +149,37 @@ auto Running::checkIdle() -> void {
}
}
-auto Running::mountStorage() -> bool {
+auto Running::updateSdState(drivers::SdState state) -> void {
+ sServices->sd(state);
+ events::Ui().Dispatch(SdStateChanged{});
+ events::Audio().Dispatch(SdStateChanged{});
+ events::System().Dispatch(SdStateChanged{});
+}
+
+auto Running::mountStorage() -> void {
+ // Only mount our storage if we know it's not currently in use by the SAMD.
+ if (sServices->samd().UsbMassStorage()) {
+ updateSdState(drivers::SdState::kNotMounted);
+ return;
+ }
+
ESP_LOGI(kTag, "mounting sd card");
auto storage_res = drivers::SdStorage::Create(sServices->gpios());
if (storage_res.has_error()) {
ESP_LOGW(kTag, "failed to mount!");
switch (storage_res.error()) {
case drivers::SdStorage::FAILED_TO_MOUNT:
- sServices->sd(drivers::SdState::kNotFormatted);
+ updateSdState(drivers::SdState::kNotFormatted);
break;
case drivers::SdStorage::FAILED_TO_READ:
default:
- sServices->sd(drivers::SdState::kNotPresent);
+ updateSdState(drivers::SdState::kNotPresent);
break;
}
- return false;
+ return;
}
sStorage.reset(storage_res.value());
- sServices->sd(drivers::SdState::kMounted);
ESP_LOGI(kTag, "opening database");
sFileGatherer = new database::FileGathererImpl();
@@ -161,16 +188,14 @@ auto Running::mountStorage() -> bool {
sServices->collator(), sServices->bg_worker());
if (database_res.has_error()) {
unmountStorage();
- return false;
+ return;
}
sServices->database(
std::unique_ptr<database::Database>{database_res.value()});
ESP_LOGI(kTag, "storage loaded okay");
- events::Ui().Dispatch(StorageMounted{});
- events::Audio().Dispatch(StorageMounted{});
- events::System().Dispatch(StorageMounted{});
+ updateSdState(drivers::SdState::kMounted);
// Tell the database to refresh so that we pick up any changes from the newly
// mounted card.
@@ -183,14 +208,13 @@ auto Running::mountStorage() -> bool {
db->updateIndexes();
});
}
-
- return true;
}
auto Running::unmountStorage() -> void {
ESP_LOGW(kTag, "unmounting storage");
sServices->database({});
sStorage.reset();
+ updateSdState(drivers::SdState::kNotMounted);
}
} // namespace states
diff --git a/src/system_fsm/service_locator.cpp b/src/tangara/system_fsm/service_locator.cpp
index d8dcf44a..ef719930 100644
--- a/src/system_fsm/service_locator.cpp
+++ b/src/tangara/system_fsm/service_locator.cpp
@@ -4,13 +4,13 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "service_locator.hpp"
+#include "system_fsm/service_locator.hpp"
#include <memory>
-#include "nvs.hpp"
-#include "storage.hpp"
-#include "touchwheel.hpp"
+#include "drivers/nvs.hpp"
+#include "drivers/storage.hpp"
+#include "drivers/touchwheel.hpp"
namespace system_fsm {
diff --git a/src/system_fsm/include/service_locator.hpp b/src/tangara/system_fsm/service_locator.hpp
index 5978578c..5b2205eb 100644
--- a/src/system_fsm/include/service_locator.hpp
+++ b/src/tangara/system_fsm/service_locator.hpp
@@ -8,19 +8,19 @@
#include <memory>
-#include "battery.hpp"
-#include "bluetooth.hpp"
+#include "audio/track_queue.hpp"
+#include "battery/battery.hpp"
+#include "drivers/bluetooth.hpp"
#include "collation.hpp"
-#include "database.hpp"
-#include "gpios.hpp"
-#include "haptics.hpp"
-#include "nvs.hpp"
-#include "samd.hpp"
-#include "storage.hpp"
-#include "tag_parser.hpp"
+#include "database/database.hpp"
+#include "database/tag_parser.hpp"
+#include "drivers/gpios.hpp"
+#include "drivers/haptics.hpp"
+#include "drivers/nvs.hpp"
+#include "drivers/samd.hpp"
+#include "drivers/storage.hpp"
#include "tasks.hpp"
-#include "touchwheel.hpp"
-#include "track_queue.hpp"
+#include "drivers/touchwheel.hpp"
namespace system_fsm {
diff --git a/src/system_fsm/include/system_events.hpp b/src/tangara/system_fsm/system_events.hpp
index 22e3b6bd..3452e58e 100644
--- a/src/system_fsm/include/system_events.hpp
+++ b/src/tangara/system_fsm/system_events.hpp
@@ -8,13 +8,14 @@
#include <memory>
-#include "battery.hpp"
-#include "bluetooth_types.hpp"
-#include "database.hpp"
+#include "battery/battery.hpp"
+#include "database/database.hpp"
+#include "drivers/bluetooth_types.hpp"
+#include "drivers/haptics.hpp"
+#include "drivers/samd.hpp"
+#include "drivers/storage.hpp"
#include "ff.h"
-#include "haptics.hpp"
-#include "samd.hpp"
-#include "service_locator.hpp"
+#include "system_fsm/service_locator.hpp"
#include "tinyfsm.hpp"
namespace system_fsm {
@@ -38,10 +39,7 @@ struct FatalError : tinyfsm::Event {};
struct OnIdle : tinyfsm::Event {};
-/*
- * Sent by SysState when the system storage has been successfully mounted.
- */
-struct StorageMounted : tinyfsm::Event {};
+struct SdStateChanged : tinyfsm::Event {};
struct StorageError : tinyfsm::Event {
FRESULT error;
diff --git a/src/system_fsm/system_fsm.cpp b/src/tangara/system_fsm/system_fsm.cpp
index 59d41c73..2e819569 100644
--- a/src/system_fsm/system_fsm.cpp
+++ b/src/tangara/system_fsm/system_fsm.cpp
@@ -4,15 +4,15 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "system_fsm.hpp"
-#include "audio_fsm.hpp"
+#include "system_fsm/system_fsm.hpp"
+#include "audio/audio_fsm.hpp"
+#include "audio/track_queue.hpp"
+#include "database/tag_parser.hpp"
#include "driver/gpio.h"
-#include "event_queue.hpp"
-#include "gpios.hpp"
-#include "service_locator.hpp"
-#include "system_events.hpp"
-#include "tag_parser.hpp"
-#include "track_queue.hpp"
+#include "drivers/gpios.hpp"
+#include "events/event_queue.hpp"
+#include "system_fsm/service_locator.hpp"
+#include "system_fsm/system_events.hpp"
[[maybe_unused]] static const char kTag[] = "system";
diff --git a/src/system_fsm/include/system_fsm.hpp b/src/tangara/system_fsm/system_fsm.hpp
index f01afb3f..5c4157cd 100644
--- a/src/system_fsm/include/system_fsm.hpp
+++ b/src/tangara/system_fsm/system_fsm.hpp
@@ -8,27 +8,26 @@
#include <memory>
-#include "app_console.hpp"
-#include "audio_events.hpp"
-#include "battery.hpp"
-#include "bluetooth.hpp"
-#include "database.hpp"
-#include "db_events.hpp"
-#include "display.hpp"
-#include "gpios.hpp"
-#include "nvs.hpp"
-#include "samd.hpp"
-#include "service_locator.hpp"
-#include "storage.hpp"
-#include "tag_parser.hpp"
-#include "tinyfsm.hpp"
-#include "touchwheel.hpp"
-
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
-#include "system_events.hpp"
-#include "track_queue.hpp"
+#include "app_console/app_console.hpp"
+#include "audio/audio_events.hpp"
+#include "audio/track_queue.hpp"
+#include "battery/battery.hpp"
+#include "database/database.hpp"
+#include "database/db_events.hpp"
+#include "database/tag_parser.hpp"
+#include "drivers/bluetooth.hpp"
+#include "drivers/display.hpp"
+#include "drivers/gpios.hpp"
+#include "drivers/nvs.hpp"
+#include "drivers/samd.hpp"
+#include "drivers/storage.hpp"
+#include "drivers/touchwheel.hpp"
+#include "system_fsm/service_locator.hpp"
+#include "system_fsm/system_events.hpp"
+#include "tinyfsm.hpp"
namespace system_fsm {
@@ -56,7 +55,6 @@ class SystemState : public tinyfsm::Fsm<SystemState> {
virtual void react(const DisplayReady&) {}
virtual void react(const BootComplete&) {}
- virtual void react(const StorageMounted&) {}
virtual void react(const StorageError&) {}
virtual void react(const KeyLockChanged&) {}
virtual void react(const SdDetectChanged&) {}
@@ -111,7 +109,8 @@ class Running : public SystemState {
private:
auto checkIdle() -> void;
- auto mountStorage() -> bool;
+ auto updateSdState(drivers::SdState) -> void;
+ auto mountStorage() -> void;
auto unmountStorage() -> void;
bool storage_mounted_;
diff --git a/src/ui/include/fonts.hpp b/src/tangara/ui/fonts.hpp
index e4a8e139..e4a8e139 100644
--- a/src/ui/include/fonts.hpp
+++ b/src/tangara/ui/fonts.hpp
diff --git a/src/ui/lvgl_task.cpp b/src/tangara/ui/lvgl_task.cpp
index 51da0179..448aa261 100644
--- a/src/ui/lvgl_task.cpp
+++ b/src/tangara/ui/lvgl_task.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "lvgl_task.hpp"
+#include "ui/lvgl_task.hpp"
#include <dirent.h>
#include <stdint.h>
@@ -20,8 +20,9 @@
#include "core/lv_obj.h"
#include "core/lv_obj_pos.h"
#include "core/lv_obj_tree.h"
+#include "drivers/touchwheel.hpp"
#include "esp_log.h"
-#include "event_queue.hpp"
+#include "events/event_queue.hpp"
#include "extra/themes/basic/lv_theme_basic.h"
#include "font/lv_font.h"
#include "freertos/portmacro.h"
@@ -30,21 +31,20 @@
#include "hal/gpio_types.h"
#include "hal/lv_hal_indev.h"
#include "hal/spi_types.h"
+#include "input/lvgl_input_driver.hpp"
#include "lua.h"
#include "lv_api_map.h"
#include "lvgl/lvgl.h"
-#include "lvgl_input_driver.hpp"
#include "misc/lv_color.h"
#include "misc/lv_style.h"
#include "misc/lv_timer.h"
-#include "modal.hpp"
#include "tasks.hpp"
-#include "touchwheel.hpp"
-#include "ui_fsm.hpp"
+#include "ui/modal.hpp"
+#include "ui/ui_fsm.hpp"
#include "widgets/lv_label.h"
-#include "display.hpp"
-#include "gpios.hpp"
+#include "drivers/display.hpp"
+#include "drivers/gpios.hpp"
namespace ui {
diff --git a/src/ui/include/lvgl_task.hpp b/src/tangara/ui/lvgl_task.hpp
index 8efcbf35..fcf00ab1 100644
--- a/src/ui/include/lvgl_task.hpp
+++ b/src/tangara/ui/lvgl_task.hpp
@@ -14,11 +14,11 @@
#include "freertos/task.h"
#include "freertos/timers.h"
-#include "display.hpp"
-#include "lvgl_input_driver.hpp"
-#include "screen.hpp"
-#include "themes.hpp"
-#include "touchwheel.hpp"
+#include "drivers/display.hpp"
+#include "input/lvgl_input_driver.hpp"
+#include "drivers/touchwheel.hpp"
+#include "ui/screen.hpp"
+#include "ui/themes.hpp"
namespace ui {
diff --git a/src/ui/modal.cpp b/src/tangara/ui/modal.cpp
index ec541914..4f5a2432 100644
--- a/src/ui/modal.cpp
+++ b/src/tangara/ui/modal.cpp
@@ -5,7 +5,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "modal.hpp"
+#include "ui/modal.hpp"
#include "misc/lv_color.h"
@@ -14,17 +14,17 @@
#include "core/lv_group.h"
#include "core/lv_obj_pos.h"
-#include "event_queue.hpp"
+#include "database/index.hpp"
+#include "events/event_queue.hpp"
#include "extra/widgets/list/lv_list.h"
#include "extra/widgets/menu/lv_menu.h"
#include "extra/widgets/spinner/lv_spinner.h"
#include "hal/lv_hal_disp.h"
-#include "index.hpp"
#include "misc/lv_area.h"
-#include "screen.hpp"
-#include "themes.hpp"
-#include "ui_events.hpp"
-#include "ui_fsm.hpp"
+#include "ui/screen.hpp"
+#include "ui/themes.hpp"
+#include "ui/ui_events.hpp"
+#include "ui/ui_fsm.hpp"
#include "widgets/lv_label.h"
namespace ui {
diff --git a/src/ui/include/modal.hpp b/src/tangara/ui/modal.hpp
index 6b7e792e..bd5209a7 100644
--- a/src/ui/include/modal.hpp
+++ b/src/tangara/ui/modal.hpp
@@ -13,7 +13,7 @@
#include "core/lv_obj_tree.h"
#include "lvgl.h"
-#include "screen.hpp"
+#include "ui/screen.hpp"
namespace ui {
diff --git a/src/ui/screen.cpp b/src/tangara/ui/screen.cpp
index a39aaf7e..8357cfbd 100644
--- a/src/ui/screen.cpp
+++ b/src/tangara/ui/screen.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "screen.hpp"
+#include "ui/screen.hpp"
#include <memory>
diff --git a/src/ui/include/screen.hpp b/src/tangara/ui/screen.hpp
index 40284fda..40284fda 100644
--- a/src/ui/include/screen.hpp
+++ b/src/tangara/ui/screen.hpp
diff --git a/src/ui/screen_lua.cpp b/src/tangara/ui/screen_lua.cpp
index 685e43cb..c6cda7ae 100644
--- a/src/ui/screen_lua.cpp
+++ b/src/tangara/ui/screen_lua.cpp
@@ -4,15 +4,15 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "screen_lua.hpp"
+#include "ui/screen_lua.hpp"
#include "core/lv_obj_tree.h"
#include "lua.h"
#include "lua.hpp"
-#include "property.hpp"
-#include "themes.hpp"
+#include "lua/property.hpp"
+#include "ui/themes.hpp"
-#include "lua_thread.hpp"
+#include "lua/lua_thread.hpp"
#include "luavgl.h"
namespace ui {
diff --git a/src/ui/include/screen_lua.hpp b/src/tangara/ui/screen_lua.hpp
index 8a463bad..d6bc20a2 100644
--- a/src/ui/include/screen_lua.hpp
+++ b/src/tangara/ui/screen_lua.hpp
@@ -8,8 +8,8 @@
#include "lua.hpp"
-#include "property.hpp"
-#include "screen.hpp"
+#include "lua/property.hpp"
+#include "ui/screen.hpp"
namespace ui {
namespace screens {
diff --git a/src/ui/screen_splash.cpp b/src/tangara/ui/screen_splash.cpp
index 48cfef88..651f00dd 100644
--- a/src/ui/screen_splash.cpp
+++ b/src/tangara/ui/screen_splash.cpp
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "screen_splash.hpp"
+#include "ui/screen_splash.hpp"
#include "core/lv_obj.h"
#include "core/lv_obj_style.h"
diff --git a/src/ui/include/screen_splash.hpp b/src/tangara/ui/screen_splash.hpp
index 6e746345..e56c915f 100644
--- a/src/ui/include/screen_splash.hpp
+++ b/src/tangara/ui/screen_splash.hpp
@@ -10,7 +10,7 @@
#include "lvgl.h"
-#include "screen.hpp"
+#include "ui/screen.hpp"
namespace ui {
namespace screens {
diff --git a/src/ui/themes.cpp b/src/tangara/ui/themes.cpp
index b13f226a..44638c55 100644
--- a/src/ui/themes.cpp
+++ b/src/tangara/ui/themes.cpp
@@ -1,4 +1,4 @@
-#include "themes.hpp"
+#include "ui/themes.hpp"
#include "core/lv_obj.h"
#include "core/lv_obj_style.h"
#include "core/lv_obj_tree.h"
@@ -35,9 +35,9 @@ void Theme::Apply(void) {
void Theme::Callback(lv_obj_t* obj) {
// Find and apply base styles
if (auto search = style_map.find("base"); search != style_map.end()) {
- for (const auto& pair : search->second) {
- lv_obj_add_style(obj, pair.second, pair.first);
- }
+ for (const auto& pair : search->second) {
+ lv_obj_add_style(obj, pair.second, pair.first);
+ }
}
// Determine class name
@@ -60,19 +60,18 @@ void Theme::Callback(lv_obj_t* obj) {
// Apply all styles from class
if (auto search = style_map.find(class_name); search != style_map.end()) {
- for (const auto& pair : search->second) {
- lv_obj_add_style(obj, pair.second, pair.first);
- }
+ for (const auto& pair : search->second) {
+ lv_obj_add_style(obj, pair.second, pair.first);
+ }
}
-
}
void Theme::ApplyStyle(lv_obj_t* obj, std::string style_key) {
if (auto search = style_map.find(style_key); search != style_map.end()) {
- for (const auto& pair : search->second) {
- lv_obj_remove_style(obj, pair.second, pair.first);
- lv_obj_add_style(obj, pair.second, pair.first);
- }
+ for (const auto& pair : search->second) {
+ lv_obj_remove_style(obj, pair.second, pair.first);
+ lv_obj_add_style(obj, pair.second, pair.first);
+ }
}
}
@@ -85,7 +84,7 @@ void Theme::AddStyle(std::string key, int selector, lv_style_t* style) {
style_map.try_emplace(key, std::vector<std::pair<int, lv_style_t*>>{});
if (auto search = style_map.find(key); search != style_map.end()) {
// Key exists
- auto &vec = search->second;
+ auto& vec = search->second;
// Add it to the list
vec.push_back(std::make_pair(selector, style));
}
diff --git a/src/ui/include/themes.hpp b/src/tangara/ui/themes.hpp
index 09b9cdce..fd576478 100644
--- a/src/ui/include/themes.hpp
+++ b/src/tangara/ui/themes.hpp
@@ -1,7 +1,7 @@
#pragma once
-#include <string>
#include <map>
+#include <string>
#include <vector>
#include "lvgl.h"
@@ -32,7 +32,6 @@ class Theme {
Theme();
std::map<std::string, std::vector<std::pair<int, lv_style_t*>>> style_map;
lv_theme_t theme_;
-
};
} // namespace themes
} // namespace ui
diff --git a/src/ui/include/ui_events.hpp b/src/tangara/ui/ui_events.hpp
index 3d794edc..cb446cd2 100644
--- a/src/ui/include/ui_events.hpp
+++ b/src/tangara/ui/ui_events.hpp
@@ -7,12 +7,12 @@
#pragma once
#include <memory>
-#include "database.hpp"
-#include "gpios.hpp"
-#include "index.hpp"
-#include "nvs.hpp"
-#include "screen.hpp"
+#include "database/database.hpp"
+#include "database/index.hpp"
+#include "drivers/gpios.hpp"
+#include "drivers/nvs.hpp"
#include "tinyfsm.hpp"
+#include "ui/screen.hpp"
namespace ui {
diff --git a/src/ui/ui_fsm.cpp b/src/tangara/ui/ui_fsm.cpp
index 1cbf1be4..17d6c511 100644
--- a/src/ui/ui_fsm.cpp
+++ b/src/tangara/ui/ui_fsm.cpp
@@ -4,63 +4,63 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
-#include "ui_fsm.hpp"
+#include "ui/ui_fsm.hpp"
#include <memory>
#include <memory_resource>
#include <variant>
-#include "bluetooth_types.hpp"
-#include "db_events.hpp"
-#include "device_factory.hpp"
-#include "display_init.hpp"
-#include "esp_spp_api.h"
-#include "feedback_haptics.hpp"
-#include "freertos/portmacro.h"
-#include "freertos/projdefs.h"
-#include "input_device.hpp"
-#include "input_touch_wheel.hpp"
-#include "input_volume_buttons.hpp"
-#include "lua.h"
-#include "lua.hpp"
+#include "FreeRTOSConfig.h"
+#include "lvgl.h"
-#include "audio_fsm.hpp"
-#include "battery.hpp"
#include "core/lv_group.h"
#include "core/lv_obj.h"
#include "core/lv_obj_tree.h"
-#include "database.hpp"
#include "esp_heap_caps.h"
+#include "esp_spp_api.h"
#include "esp_timer.h"
-#include "haptics.hpp"
-#include "lauxlib.h"
-#include "lua_thread.hpp"
+#include "freertos/portmacro.h"
+#include "freertos/projdefs.h"
+#include "lua.hpp"
#include "luavgl.h"
-#include "lvgl_input_driver.hpp"
-#include "memory_resource.hpp"
#include "misc/lv_gc.h"
-
-#include "audio_events.hpp"
-#include "display.hpp"
-#include "event_queue.hpp"
-#include "gpios.hpp"
-#include "lua_registry.hpp"
-#include "lvgl_task.hpp"
-#include "nvs.hpp"
-#include "property.hpp"
-#include "samd.hpp"
-#include "screen.hpp"
-#include "screen_lua.hpp"
-#include "screen_splash.hpp"
-#include "spiffs.hpp"
-#include "storage.hpp"
-#include "system_events.hpp"
#include "tinyfsm.hpp"
-#include "touchwheel.hpp"
-#include "track_queue.hpp"
-#include "ui_events.hpp"
#include "widgets/lv_label.h"
+#include "audio/audio_events.hpp"
+#include "audio/audio_fsm.hpp"
+#include "audio/track_queue.hpp"
+#include "battery/battery.hpp"
+#include "database/database.hpp"
+#include "database/db_events.hpp"
+#include "drivers/bluetooth_types.hpp"
+#include "drivers/display.hpp"
+#include "drivers/display_init.hpp"
+#include "drivers/gpios.hpp"
+#include "drivers/haptics.hpp"
+#include "drivers/nvs.hpp"
+#include "drivers/samd.hpp"
+#include "drivers/spiffs.hpp"
+#include "drivers/storage.hpp"
+#include "drivers/touchwheel.hpp"
+#include "events/event_queue.hpp"
+#include "input/device_factory.hpp"
+#include "input/feedback_haptics.hpp"
+#include "input/input_device.hpp"
+#include "input/input_touch_wheel.hpp"
+#include "input/input_volume_buttons.hpp"
+#include "input/lvgl_input_driver.hpp"
+#include "lua/lua_registry.hpp"
+#include "lua/lua_thread.hpp"
+#include "lua/property.hpp"
+#include "memory_resource.hpp"
+#include "system_fsm/system_events.hpp"
+#include "ui/lvgl_task.hpp"
+#include "ui/screen.hpp"
+#include "ui/screen_lua.hpp"
+#include "ui/screen_splash.hpp"
+#include "ui/ui_events.hpp"
+
namespace ui {
[[maybe_unused]] static constexpr char kTag[] = "ui_fsm";
@@ -253,6 +253,8 @@ lua::Property UiState::sDatabaseAutoUpdate{
return true;
}};
+lua::Property UiState::sSdMounted{false};
+
lua::Property UiState::sUsbMassStorageEnabled{
false, [](const lua::LuaValue& val) {
if (!std::holds_alternative<bool>(val)) {
@@ -334,6 +336,10 @@ void UiState::react(const system_fsm::SamdUsbStatusChanged& ev) {
drivers::Samd::UsbStatus::kAttachedBusy);
}
+void UiState::react(const system_fsm::SdStateChanged&) {
+ sSdMounted.setDirect(sServices->sd() == drivers::SdState::kMounted);
+}
+
void UiState::react(const database::event::UpdateStarted&) {
sDatabaseUpdating.setDirect(true);
}
@@ -444,7 +450,8 @@ void Splash::react(const system_fsm::BootComplete& ev) {
sTask->input(sInput);
}
-void Splash::react(const system_fsm::StorageMounted&) {
+void Splash::react(const system_fsm::SdStateChanged& ev) {
+ UiState::react(ev);
transit<Lua>();
}
@@ -517,6 +524,7 @@ void Lua::entry() {
{
{"push", [&](lua_State* s) { return PushLuaScreen(s); }},
{"pop", [&](lua_State* s) { return PopLuaScreen(s); }},
+ {"reset", [&](lua_State* s) { return ResetLuaScreen(s); }},
});
registry.AddPropertyModule(
"alerts", {
@@ -533,6 +541,9 @@ void Lua::entry() {
{"updating", &sDatabaseUpdating},
{"auto_update", &sDatabaseAutoUpdate},
});
+ registry.AddPropertyModule("sd_card", {
+ {"mounted", &sSdMounted},
+ });
registry.AddPropertyModule("usb",
{
{"msc_enabled", &sUsbMassStorageEnabled},
@@ -547,7 +558,9 @@ void Lua::entry() {
sBluetoothDevices.setDirect(bt.KnownDevices());
sCurrentScreen.reset();
- sLua->RunScript("/sdcard/config.lua");
+ if (sServices->sd() == drivers::SdState::kMounted) {
+ sLua->RunScript("/sdcard/config.lua");
+ }
sLua->RunScript("/lua/main.lua");
}
}
@@ -587,16 +600,6 @@ auto Lua::PushLuaScreen(lua_State* s) -> int {
return 0;
}
-auto Lua::QueueNext(lua_State*) -> int {
- sServices->track_queue().next();
- return 0;
-}
-
-auto Lua::QueuePrevious(lua_State*) -> int {
- sServices->track_queue().previous();
- return 0;
-}
-
auto Lua::PopLuaScreen(lua_State* s) -> int {
if (!sCurrentScreen->canPop()) {
return 0;
@@ -607,6 +610,30 @@ auto Lua::PopLuaScreen(lua_State* s) -> int {
return 0;
}
+auto Lua::ResetLuaScreen(lua_State* s) -> int {
+ if (sCurrentScreen) {
+ if (!sCurrentScreen->canPop()) {
+ ESP_LOGW(kTag, "ignoring reset as popping is blocked");
+ return 0;
+ }
+ sCurrentScreen->onHidden();
+ }
+ while (!sScreens.empty()) {
+ sScreens.pop();
+ }
+ return PushLuaScreen(s);
+}
+
+auto Lua::QueueNext(lua_State*) -> int {
+ sServices->track_queue().next();
+ return 0;
+}
+
+auto Lua::QueuePrevious(lua_State*) -> int {
+ sServices->track_queue().previous();
+ return 0;
+}
+
auto Lua::Ticks(lua_State* s) -> int {
lua_pushinteger(s, esp_timer_get_time() / 1000);
return 1;
diff --git a/src/ui/include/ui_fsm.hpp b/src/tangara/ui/ui_fsm.hpp
index 325aea8f..af8d75fb 100644
--- a/src/ui/include/ui_fsm.hpp
+++ b/src/tangara/ui/ui_fsm.hpp
@@ -10,30 +10,31 @@
#include <memory>
#include <stack>
-#include "audio_events.hpp"
-#include "battery.hpp"
-#include "db_events.hpp"
-#include "device_factory.hpp"
-#include "display.hpp"
-#include "feedback_haptics.hpp"
-#include "gpios.hpp"
-#include "input_touch_wheel.hpp"
-#include "input_volume_buttons.hpp"
-#include "lua_thread.hpp"
-#include "lvgl_input_driver.hpp"
-#include "lvgl_task.hpp"
-#include "modal.hpp"
-#include "nvs.hpp"
-#include "property.hpp"
-#include "screen.hpp"
-#include "service_locator.hpp"
-#include "storage.hpp"
-#include "system_events.hpp"
#include "tinyfsm.hpp"
-#include "touchwheel.hpp"
-#include "track.hpp"
-#include "track_queue.hpp"
-#include "ui_events.hpp"
+
+#include "audio/audio_events.hpp"
+#include "audio/track_queue.hpp"
+#include "battery/battery.hpp"
+#include "database/db_events.hpp"
+#include "database/track.hpp"
+#include "drivers/display.hpp"
+#include "drivers/gpios.hpp"
+#include "drivers/nvs.hpp"
+#include "drivers/storage.hpp"
+#include "drivers/touchwheel.hpp"
+#include "input/device_factory.hpp"
+#include "input/feedback_haptics.hpp"
+#include "input/input_touch_wheel.hpp"
+#include "input/input_volume_buttons.hpp"
+#include "input/lvgl_input_driver.hpp"
+#include "lua/lua_thread.hpp"
+#include "lua/property.hpp"
+#include "system_fsm/service_locator.hpp"
+#include "system_fsm/system_events.hpp"
+#include "ui/lvgl_task.hpp"
+#include "ui/modal.hpp"
+#include "ui/screen.hpp"
+#include "ui/ui_events.hpp"
namespace ui {
@@ -57,7 +58,7 @@ class UiState : public tinyfsm::Fsm<UiState> {
virtual void react(const DumpLuaStack&) {}
virtual void react(const internal::BackPressed&) {}
virtual void react(const system_fsm::BootComplete&) {}
- virtual void react(const system_fsm::StorageMounted&) {}
+ virtual void react(const system_fsm::SdStateChanged&);
void react(const system_fsm::BatteryStateChanged&);
void react(const audio::PlaybackUpdate&);
@@ -136,6 +137,8 @@ class UiState : public tinyfsm::Fsm<UiState> {
static lua::Property sDatabaseUpdating;
static lua::Property sDatabaseAutoUpdate;
+ static lua::Property sSdMounted;
+
static lua::Property sUsbMassStorageEnabled;
static lua::Property sUsbMassStorageBusy;
};
@@ -147,7 +150,7 @@ class Splash : public UiState {
void exit() override;
void react(const system_fsm::BootComplete&) override;
- void react(const system_fsm::StorageMounted&) override;
+ void react(const system_fsm::SdStateChanged&) override;
using UiState::react;
};
@@ -166,6 +169,7 @@ class Lua : public UiState {
private:
auto PushLuaScreen(lua_State*) -> int;
auto PopLuaScreen(lua_State*) -> int;
+ auto ResetLuaScreen(lua_State*) -> int;
auto ShowAlert(lua_State*) -> int;
auto HideAlert(lua_State*) -> int;
diff --git a/src/tasks/CMakeLists.txt b/src/tasks/CMakeLists.txt
index 0fdacf78..814c9943 100644
--- a/src/tasks/CMakeLists.txt
+++ b/src/tasks/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright 2023 jacqueline <me@jacqueline.id.au>
#
# SPDX-License-Identifier: GPL-3.0-only
-idf_component_register(SRCS "tasks.cpp" INCLUDE_DIRS "." REQUIRES "span" "memory")
+idf_component_register(SRCS "tasks.cpp" INCLUDE_DIRS "." REQUIRES "memory")
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/tasks/tasks.cpp b/src/tasks/tasks.cpp
index aa382655..d3937c68 100644
--- a/src/tasks/tasks.cpp
+++ b/src/tasks/tasks.cpp
@@ -33,12 +33,12 @@ auto Name<Type::kAudioConverter>() -> std::pmr::string {
}
template <Type t>
-auto AllocateStack() -> cpp::span<StackType_t>;
+auto AllocateStack() -> std::span<StackType_t>;
// Decoders often require a very large amount of stack space, since they aren't
// usually written with embedded use cases in mind.
template <>
-auto AllocateStack<Type::kAudioDecoder>() -> cpp::span<StackType_t> {
+auto AllocateStack<Type::kAudioDecoder>() -> std::span<StackType_t> {
constexpr std::size_t size = 20 * 1024;
static StackType_t sStack[size];
return {sStack, size};
@@ -46,14 +46,14 @@ auto AllocateStack<Type::kAudioDecoder>() -> cpp::span<StackType_t> {
// LVGL requires only a relatively small stack. Lua's stack is allocated
// separately.
template <>
-auto AllocateStack<Type::kUi>() -> cpp::span<StackType_t> {
+auto AllocateStack<Type::kUi>() -> std::span<StackType_t> {
constexpr std::size_t size = 14 * 1024;
static StackType_t sStack[size];
return {sStack, size};
}
template <>
// PCM conversion and resampling uses a very small amount of stack.
-auto AllocateStack<Type::kAudioConverter>() -> cpp::span<StackType_t> {
+auto AllocateStack<Type::kAudioConverter>() -> std::span<StackType_t> {
constexpr std::size_t size = 4 * 1024;
static StackType_t sStack[size];
return {sStack, size};
@@ -63,7 +63,7 @@ auto AllocateStack<Type::kAudioConverter>() -> cpp::span<StackType_t> {
// cases, where large stack usage isn't so much of a concern. It therefore uses
// an eye-wateringly large amount of stack.
template <>
-auto AllocateStack<Type::kBackgroundWorker>() -> cpp::span<StackType_t> {
+auto AllocateStack<Type::kBackgroundWorker>() -> std::span<StackType_t> {
std::size_t size = 64 * 1024;
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM)),
size};
diff --git a/src/tasks/tasks.hpp b/src/tasks/tasks.hpp
index 47f26837..566b5706 100644
--- a/src/tasks/tasks.hpp
+++ b/src/tasks/tasks.hpp
@@ -11,6 +11,7 @@
#include <future>
#include <memory>
#include <memory_resource>
+#include <span>
#include <string>
#include "esp_heap_caps.h"
@@ -19,7 +20,6 @@
#include "freertos/projdefs.h"
#include "freertos/queue.h"
#include "freertos/task.h"
-#include "span.hpp"
namespace tasks {
@@ -46,7 +46,7 @@ enum class Type {
template <Type t>
auto Name() -> std::pmr::string;
template <Type t>
-auto AllocateStack() -> cpp::span<StackType_t>;
+auto AllocateStack() -> std::span<StackType_t>;
template <Type t>
auto Priority() -> UBaseType_t;
@@ -56,7 +56,7 @@ template <Type t>
auto StartPersistent(const std::function<void(void)>& fn) -> void {
StaticTask_t* task_buffer = static_cast<StaticTask_t*>(heap_caps_malloc(
sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT));
- cpp::span<StackType_t> stack = AllocateStack<t>();
+ std::span<StackType_t> stack = AllocateStack<t>();
xTaskCreateStatic(&PersistentMain, Name<t>().c_str(), stack.size(),
new std::function<void(void)>(fn), Priority<t>(),
stack.data(), task_buffer);
@@ -67,7 +67,7 @@ auto StartPersistent(BaseType_t core, const std::function<void(void)>& fn)
-> void {
StaticTask_t* task_buffer = static_cast<StaticTask_t*>(heap_caps_malloc(
sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT));
- cpp::span<StackType_t> stack = AllocateStack<t>();
+ std::span<StackType_t> stack = AllocateStack<t>();
xTaskCreateStaticPinnedToCore(&PersistentMain, Name<t>().c_str(),
stack.size(), new std::function<void(void)>(fn),
Priority<t>(), stack.data(), task_buffer, core);
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt
deleted file mode 100644
index 3814e9d5..00000000
--- a/src/ui/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2023 jacqueline <me@jacqueline.id.au>
-#
-# SPDX-License-Identifier: GPL-3.0-only
-
-idf_component_register(
- SRCS "lvgl_task.cpp" "ui_fsm.cpp" "screen_splash.cpp" "themes.cpp"
- "screen.cpp" "modal.cpp" "screen_lua.cpp" "splash.c" "font_fusion_12.c"
- "font_fusion_10.c"
- INCLUDE_DIRS "include"
- REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer" "battery" "lua" "luavgl" "esp_app_format" "input")
-target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/ui/font_symbols.c b/src/ui/font_symbols.c
deleted file mode 100644
index abc380eb..00000000
--- a/src/ui/font_symbols.c
+++ /dev/null
@@ -1,425 +0,0 @@
-/*******************************************************************************
- * Size: 12 px
- * Bpp: 1
- * Opts: --font fonts/font-awesome/FontAwesome5-Solid+Brands+Regular.woff -r 0xf244,0xf243,0xf242,0xf241,0xf240 -r 0xf104,0xf0d7 -r 61441,61448,61451,61452,61452,61453,61457,61459,61461,61465 -r 61468,61473,61478,61479,61480,61502,61512,61515,61516,61517 -r 61521,61522,61523,61524,61543,61544,61550,61552,61553,61556 -r 61559,61560,61561,61563,61587,61589,61636,61637,61639,61671 -r 61674,61683,61724,61732,61787,61931,62016,62017,62018,62019 -r 62020,62087,62099,62212,62189,62810,63426,63650 --size 12 --bpp 1 --format lvgl -o font_symbols.c
- ******************************************************************************/
-
-#ifdef LV_LVGL_H_INCLUDE_SIMPLE
-#include "lvgl.h"
-#else
-#include "lvgl/lvgl.h"
-#endif
-
-#ifndef FONT_SYMBOLS
-#define FONT_SYMBOLS 1
-#endif
-
-#if FONT_SYMBOLS
-
-/*-----------------
- * BITMAPS
- *----------------*/
-
-/*Store the image of the glyphs*/
-static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
- /* U+F001 "" */
- 0x0, 0x70, 0x3f, 0x1f, 0xf1, 0xfb, 0x1c, 0x31,
- 0x83, 0x18, 0x31, 0x83, 0x19, 0xf7, 0x9f, 0xf8,
- 0x47, 0x0,
-
- /* U+F008 "" */
- 0xbf, 0xde, 0x7, 0xa0, 0x5e, 0x7, 0xbf, 0xde,
- 0x7, 0xa0, 0x5e, 0x7, 0xbf, 0xd0,
-
- /* U+F00B "" */
- 0xf7, 0xf7, 0xbf, 0xfd, 0xfe, 0x0, 0xf, 0x7f,
- 0x7b, 0xff, 0xdf, 0xc0, 0x0, 0xf7, 0xf7, 0xbf,
- 0xfd, 0xfc,
-
- /* U+F00C "" */
- 0x0, 0x20, 0x7, 0x0, 0xe4, 0x1c, 0xe3, 0x87,
- 0x70, 0x3e, 0x1, 0xc0, 0x8, 0x0,
-
- /* U+F00D "" */
- 0xc3, 0xe7, 0x7e, 0x3c, 0x3c, 0x7e, 0xe7, 0xc3,
-
- /* U+F011 "" */
- 0x6, 0x2, 0x64, 0x76, 0xe6, 0x66, 0xc6, 0x3c,
- 0x63, 0xc6, 0x3c, 0x3, 0x60, 0x67, 0xe, 0x3f,
- 0xc0, 0xf0,
-
- /* U+F013 "" */
- 0xe, 0x4, 0xf0, 0x7f, 0xef, 0xfe, 0x71, 0xe7,
- 0xc, 0x71, 0xef, 0xfe, 0x7f, 0xe4, 0xf0, 0xe,
- 0x0,
-
- /* U+F015 "" */
- 0x3, 0x30, 0x1e, 0xc1, 0xcf, 0xc, 0xcc, 0x6f,
- 0xdb, 0x7f, 0xb3, 0xff, 0xf, 0x3c, 0x3c, 0xf0,
- 0xf3, 0xc0,
-
- /* U+F019 "" */
- 0xe, 0x0, 0xe0, 0xe, 0x0, 0xe0, 0x3f, 0xc3,
- 0xf8, 0x1f, 0x0, 0xe0, 0xf5, 0xff, 0xff, 0xff,
- 0x5f, 0xff,
-
- /* U+F01C "" */
- 0x1f, 0xe0, 0xc0, 0xc6, 0x1, 0x90, 0x2, 0xf8,
- 0x7f, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
-
- /* U+F021 "" */
- 0x0, 0x31, 0xf3, 0x71, 0xfc, 0x7, 0xc3, 0xf0,
- 0x0, 0x0, 0x0, 0x0, 0xfc, 0x3e, 0x3, 0xf8,
- 0xec, 0xf8, 0xc0, 0x0,
-
- /* U+F026 "" */
- 0xc, 0x7f, 0xff, 0xff, 0xf1, 0xc3,
-
- /* U+F027 "" */
- 0xc, 0xe, 0x3f, 0x7f, 0x9f, 0xdf, 0xe0, 0x70,
- 0x18,
-
- /* U+F028 "" */
- 0x0, 0x60, 0x1, 0x83, 0x34, 0x38, 0xdf, 0xda,
- 0xfe, 0x57, 0xf6, 0xbf, 0x8d, 0x1c, 0xd0, 0x61,
- 0x80, 0x18,
-
- /* U+F03E "" */
- 0xff, 0xf9, 0xff, 0x9f, 0xf9, 0xef, 0xfc, 0x7d,
- 0x83, 0xc0, 0x38, 0x3, 0xff, 0xf0,
-
- /* U+F048 "" */
- 0xc3, 0xc7, 0xcf, 0xdf, 0xff, 0xff, 0xdf, 0xcf,
- 0xc7, 0xc3,
-
- /* U+F04B "" */
- 0x0, 0x1c, 0x3, 0xe0, 0x7f, 0xf, 0xf9, 0xff,
- 0xbf, 0xff, 0xfe, 0xff, 0x9f, 0xc3, 0xe0, 0x70,
- 0x0, 0x0,
-
- /* U+F04C "" */
- 0xfb, 0xff, 0x7f, 0xef, 0xfd, 0xff, 0xbf, 0xf7,
- 0xfe, 0xff, 0xdf, 0xfb, 0xff, 0x7c,
-
- /* U+F04D "" */
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
-
- /* U+F051 "" */
- 0xc3, 0xe3, 0xf3, 0xfb, 0xff, 0xff, 0xfb, 0xf3,
- 0xe3, 0xc3,
-
- /* U+F052 "" */
- 0xc, 0x3, 0xc0, 0x7c, 0x1f, 0xc7, 0xfd, 0xff,
- 0xbf, 0xf0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x80,
-
- /* U+F053 "" */
- 0xc, 0x73, 0x9c, 0xe3, 0x87, 0xe, 0x1c, 0x30,
-
- /* U+F054 "" */
- 0x83, 0x87, 0xe, 0x1c, 0x73, 0x9c, 0xe2, 0x0,
-
- /* U+F067 "" */
- 0xe, 0x1, 0xc0, 0x38, 0x7, 0xf, 0xff, 0xff,
- 0xc3, 0x80, 0x70, 0xe, 0x1, 0xc0,
-
- /* U+F068 "" */
- 0xff, 0xff, 0xfc,
-
- /* U+F06E "" */
- 0xf, 0x81, 0xc7, 0x1c, 0x1d, 0xc6, 0x7e, 0xfb,
- 0xf7, 0xdd, 0xdd, 0xc7, 0x1c, 0xf, 0x80,
-
- /* U+F070 "" */
- 0x0, 0x1, 0xc0, 0x1, 0xdf, 0x0, 0xe3, 0x80,
- 0xdb, 0x84, 0xfb, 0x9c, 0x77, 0x3c, 0x6e, 0x38,
- 0x78, 0x38, 0x70, 0x1e, 0x30, 0x0, 0x30, 0x0,
- 0x0,
-
- /* U+F071 "" */
- 0x3, 0x0, 0x1c, 0x0, 0xf8, 0x3, 0xf0, 0x1c,
- 0xc0, 0x73, 0x83, 0xcf, 0x1f, 0xfc, 0x7c, 0xfb,
- 0xf3, 0xef, 0xff, 0x80,
-
- /* U+F074 "" */
- 0x0, 0x0, 0x6, 0xe1, 0xff, 0x3f, 0x17, 0x60,
- 0xe4, 0x1f, 0x6f, 0xbf, 0xf1, 0xf0, 0x6, 0x0,
- 0x40,
-
- /* U+F077 "" */
- 0x0, 0x3, 0x1, 0xe0, 0xcc, 0x61, 0xb0, 0x30,
- 0x0,
-
- /* U+F078 "" */
- 0x0, 0x30, 0x36, 0x18, 0xcc, 0x1e, 0x3, 0x0,
- 0x0,
-
- /* U+F079 "" */
- 0x30, 0x0, 0xf7, 0xf3, 0xf0, 0x65, 0xa0, 0xc3,
- 0x1, 0x86, 0xb, 0x4c, 0x1f, 0x9f, 0xde, 0x0,
- 0x18,
-
- /* U+F07B "" */
- 0x78, 0xf, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0,
-
- /* U+F093 "" */
- 0x6, 0x0, 0xf0, 0x1f, 0x83, 0xfc, 0x7, 0x0,
- 0x70, 0x7, 0x0, 0x70, 0xf7, 0xff, 0xff, 0xff,
- 0x5f, 0xff,
-
- /* U+F095 "" */
- 0x0, 0x0, 0xf, 0x0, 0xf0, 0x1f, 0x0, 0xf0,
- 0x6, 0x0, 0xe0, 0x1c, 0x73, 0xcf, 0xf8, 0xfe,
- 0xf, 0xc0, 0x40, 0x0,
-
- /* U+F0C4 "" */
- 0x70, 0x5b, 0x3f, 0x6f, 0x3f, 0xc1, 0xf0, 0x3e,
- 0x1f, 0xe6, 0xde, 0xd9, 0xee, 0x8,
-
- /* U+F0C5 "" */
- 0x1f, 0x43, 0xef, 0x7f, 0xef, 0xfd, 0xff, 0xbf,
- 0xf7, 0xfe, 0xff, 0xdf, 0xf8, 0x3, 0xfc, 0x0,
-
- /* U+F0C7 "" */
- 0xff, 0x98, 0x1b, 0x3, 0xe0, 0x7c, 0xf, 0xff,
- 0xfe, 0x7f, 0x8f, 0xf9, 0xff, 0xfc,
-
- /* U+F0D7 "" */
- 0xfe, 0xf8, 0xe0, 0x80,
-
- /* U+F0E7 "" */
- 0x78, 0x78, 0xf8, 0xf0, 0xff, 0xfe, 0xfc, 0x1c,
- 0x18, 0x18, 0x10, 0x30,
-
- /* U+F0EA "" */
- 0x18, 0x3b, 0x8e, 0xe3, 0xf8, 0xe0, 0x3b, 0xae,
- 0xe7, 0xbf, 0xef, 0xfb, 0xf0, 0xfc, 0x3f,
-
- /* U+F0F3 "" */
- 0x4, 0x0, 0x80, 0x7c, 0x1f, 0xc3, 0xf8, 0x7f,
- 0x1f, 0xf3, 0xfe, 0x7f, 0xdf, 0xfc, 0x0, 0x7,
- 0x0,
-
- /* U+F104 "" */
- 0x17, 0xec, 0xe7, 0x10,
-
- /* U+F11C "" */
- 0xff, 0xff, 0x52, 0xbd, 0x4a, 0xff, 0xff, 0xeb,
- 0x5f, 0xff, 0xfd, 0x2, 0xf4, 0xb, 0xff, 0xfc,
-
- /* U+F124 "" */
- 0x0, 0x0, 0xf, 0x3, 0xf0, 0xfe, 0x3f, 0xef,
- 0xfc, 0xff, 0xc0, 0x78, 0x7, 0x80, 0x78, 0x7,
- 0x0, 0x70, 0x2, 0x0,
-
- /* U+F15B "" */
- 0xfa, 0x7d, 0xbe, 0xff, 0xf, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0,
-
- /* U+F1EB "" */
- 0x7, 0xc0, 0x7f, 0xf1, 0xe0, 0xf7, 0x0, 0x70,
- 0x7c, 0x3, 0xfe, 0x6, 0xc, 0x0, 0x0, 0x3,
- 0x80, 0x7, 0x0, 0xe, 0x0,
-
- /* U+F240 "" */
- 0xff, 0xff, 0x80, 0x1f, 0x7f, 0xfe, 0xff, 0xbd,
- 0xff, 0xf8, 0x1, 0xff, 0xff, 0x80,
-
- /* U+F241 "" */
- 0xff, 0xff, 0x80, 0x1f, 0x7f, 0x3e, 0xfe, 0x3d,
- 0xfc, 0xf8, 0x1, 0xff, 0xff, 0x80,
-
- /* U+F242 "" */
- 0xff, 0xff, 0x80, 0x1f, 0x78, 0x3e, 0xf0, 0x3d,
- 0xe0, 0xf8, 0x1, 0xff, 0xff, 0x80,
-
- /* U+F243 "" */
- 0xff, 0xff, 0x80, 0x1f, 0x60, 0x3e, 0xc0, 0x3d,
- 0x80, 0xf8, 0x1, 0xff, 0xff, 0x80,
-
- /* U+F244 "" */
- 0xff, 0xff, 0x80, 0x1f, 0x0, 0x3e, 0x0, 0x3c,
- 0x0, 0xf8, 0x1, 0xff, 0xff, 0x80,
-
- /* U+F287 "" */
- 0x0, 0xc0, 0x7, 0x80, 0x10, 0x7, 0x20, 0x6f,
- 0xff, 0xfc, 0x41, 0x80, 0x40, 0x0, 0xb8, 0x0,
- 0xf0,
-
- /* U+F293 "" */
- 0x3e, 0x3b, 0x9c, 0xdb, 0x7c, 0xbf, 0x1f, 0x9f,
- 0x87, 0xd5, 0xf9, 0x9d, 0xc7, 0xc0,
-
- /* U+F2ED "" */
- 0xe, 0x1f, 0xfc, 0x0, 0x0, 0x7, 0xfc, 0xd5,
- 0x9a, 0xb3, 0x56, 0x6a, 0xcd, 0x59, 0xab, 0x3f,
- 0xe0,
-
- /* U+F304 "" */
- 0x0, 0x40, 0xe, 0x0, 0xf0, 0x37, 0x7, 0xa0,
- 0xfc, 0x1f, 0x83, 0xf0, 0x7e, 0xf, 0xc0, 0xf8,
- 0xf, 0x0, 0x80, 0x0,
-
- /* U+F55A "" */
- 0xf, 0xfe, 0x3f, 0xfc, 0xfb, 0x3b, 0xf0, 0xff,
- 0xf3, 0xef, 0xc3, 0xcf, 0xb7, 0x8f, 0xff, 0xf,
- 0xfe,
-
- /* U+F7C2 "" */
- 0x1f, 0x9a, 0xbe, 0xaf, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8,
-
- /* U+F8A2 "" */
- 0x0, 0x0, 0x3, 0x30, 0x37, 0x3, 0xff, 0xff,
- 0xff, 0x70, 0x3, 0x0
-};
-
-
-/*---------------------
- * GLYPH DESCRIPTION
- *--------------------*/
-
-static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
- {.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */,
- {.bitmap_index = 0, .adv_w = 192, .box_w = 12, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
- {.bitmap_index = 18, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 32, .adv_w = 192, .box_w = 13, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 50, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 64, .adv_w = 132, .box_w = 8, .box_h = 8, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 72, .adv_w = 192, .box_w = 12, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 90, .adv_w = 192, .box_w = 12, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 107, .adv_w = 216, .box_w = 14, .box_h = 10, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 125, .adv_w = 192, .box_w = 12, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
- {.bitmap_index = 143, .adv_w = 216, .box_w = 14, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 159, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
- {.bitmap_index = 179, .adv_w = 96, .box_w = 6, .box_h = 8, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 185, .adv_w = 144, .box_w = 9, .box_h = 8, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 194, .adv_w = 216, .box_w = 13, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 212, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 226, .adv_w = 168, .box_w = 8, .box_h = 10, .ofs_x = 1, .ofs_y = -1},
- {.bitmap_index = 236, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
- {.bitmap_index = 254, .adv_w = 168, .box_w = 11, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 268, .adv_w = 168, .box_w = 11, .box_h = 10, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 282, .adv_w = 168, .box_w = 8, .box_h = 10, .ofs_x = 1, .ofs_y = -1},
- {.bitmap_index = 292, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 308, .adv_w = 120, .box_w = 6, .box_h = 10, .ofs_x = 1, .ofs_y = -1},
- {.bitmap_index = 316, .adv_w = 120, .box_w = 6, .box_h = 10, .ofs_x = 1, .ofs_y = -1},
- {.bitmap_index = 324, .adv_w = 168, .box_w = 11, .box_h = 10, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 338, .adv_w = 168, .box_w = 11, .box_h = 2, .ofs_x = 0, .ofs_y = 3},
- {.bitmap_index = 341, .adv_w = 216, .box_w = 13, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 356, .adv_w = 240, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
- {.bitmap_index = 381, .adv_w = 216, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 401, .adv_w = 192, .box_w = 12, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 418, .adv_w = 168, .box_w = 10, .box_h = 7, .ofs_x = 0, .ofs_y = 1},
- {.bitmap_index = 427, .adv_w = 168, .box_w = 10, .box_h = 7, .ofs_x = 0, .ofs_y = 1},
- {.bitmap_index = 436, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 453, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 467, .adv_w = 192, .box_w = 12, .box_h = 12, .ofs_x = 0, .ofs_y = -2},
- {.bitmap_index = 485, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
- {.bitmap_index = 505, .adv_w = 168, .box_w = 11, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 519, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 535, .adv_w = 168, .box_w = 11, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 549, .adv_w = 120, .box_w = 7, .box_h = 4, .ofs_x = 0, .ofs_y = 2},
- {.bitmap_index = 553, .adv_w = 120, .box_w = 8, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 565, .adv_w = 168, .box_w = 10, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 580, .adv_w = 168, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 597, .adv_w = 96, .box_w = 4, .box_h = 7, .ofs_x = 1, .ofs_y = 1},
- {.bitmap_index = 601, .adv_w = 216, .box_w = 14, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 617, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
- {.bitmap_index = 637, .adv_w = 144, .box_w = 9, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 651, .adv_w = 240, .box_w = 15, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 672, .adv_w = 240, .box_w = 15, .box_h = 7, .ofs_x = 0, .ofs_y = 1},
- {.bitmap_index = 686, .adv_w = 240, .box_w = 15, .box_h = 7, .ofs_x = 0, .ofs_y = 1},
- {.bitmap_index = 700, .adv_w = 240, .box_w = 15, .box_h = 7, .ofs_x = 0, .ofs_y = 1},
- {.bitmap_index = 714, .adv_w = 240, .box_w = 15, .box_h = 7, .ofs_x = 0, .ofs_y = 1},
- {.bitmap_index = 728, .adv_w = 240, .box_w = 15, .box_h = 7, .ofs_x = 0, .ofs_y = 1},
- {.bitmap_index = 742, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 759, .adv_w = 168, .box_w = 9, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
- {.bitmap_index = 773, .adv_w = 168, .box_w = 11, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 790, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
- {.bitmap_index = 810, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 827, .adv_w = 144, .box_w = 10, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 841, .adv_w = 193, .box_w = 12, .box_h = 8, .ofs_x = 0, .ofs_y = 1}
-};
-
-/*---------------------
- * CHARACTER MAPPING
- *--------------------*/
-
-static const uint16_t unicode_list_0[] = {
- 0x0, 0x7, 0xa, 0xb, 0xc, 0x10, 0x12, 0x14,
- 0x18, 0x1b, 0x20, 0x25, 0x26, 0x27, 0x3d, 0x47,
- 0x4a, 0x4b, 0x4c, 0x50, 0x51, 0x52, 0x53, 0x66,
- 0x67, 0x6d, 0x6f, 0x70, 0x73, 0x76, 0x77, 0x78,
- 0x7a, 0x92, 0x94, 0xc3, 0xc4, 0xc6, 0xd6, 0xe6,
- 0xe9, 0xf2, 0x103, 0x11b, 0x123, 0x15a, 0x1ea, 0x23f,
- 0x240, 0x241, 0x242, 0x243, 0x286, 0x292, 0x2ec, 0x303,
- 0x559, 0x7c1, 0x8a1
-};
-
-/*Collect the unicode lists and glyph_id offsets*/
-static const lv_font_fmt_txt_cmap_t cmaps[] =
-{
- {
- .range_start = 61441, .range_length = 2210, .glyph_id_start = 1,
- .unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 59, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
- }
-};
-
-
-
-/*--------------------
- * ALL CUSTOM DATA
- *--------------------*/
-
-#if LV_VERSION_CHECK(8, 0, 0)
-/*Store all the custom data of the font*/
-static lv_font_fmt_txt_glyph_cache_t cache;
-static const lv_font_fmt_txt_dsc_t font_dsc = {
-#else
-static lv_font_fmt_txt_dsc_t font_dsc = {
-#endif
- .glyph_bitmap = glyph_bitmap,
- .glyph_dsc = glyph_dsc,
- .cmaps = cmaps,
- .kern_dsc = NULL,
- .kern_scale = 0,
- .cmap_num = 1,
- .bpp = 1,
- .kern_classes = 0,
- .bitmap_format = 0,
-#if LV_VERSION_CHECK(8, 0, 0)
- .cache = &cache
-#endif
-};
-
-
-/*-----------------
- * PUBLIC FONT
- *----------------*/
-
-/*Initialize a public general font descriptor*/
-#if LV_VERSION_CHECK(8, 0, 0)
-const lv_font_t font_symbols = {
-#else
-lv_font_t font_symbols = {
-#endif
- .get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/
- .get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/
- .line_height = 13, /*The maximum line height required by the font*/
- .base_line = 2, /*Baseline measured from the bottom of the line*/
-#if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0)
- .subpx = LV_FONT_SUBPX_NONE,
-#endif
-#if LV_VERSION_CHECK(7, 4, 0) || LVGL_VERSION_MAJOR >= 8
- .underline_position = -4,
- .underline_thickness = 1,
-#endif
- .dsc = &font_dsc /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */
-};
-
-
-
-#endif /*#if FONT_SYMBOLS*/
-
diff --git a/src/ui/icons/battery_20.c b/src/ui/icons/battery_20.c
deleted file mode 100644
index 3be6b614..00000000
--- a/src/ui/icons/battery_20.c
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifdef __has_include
- #if __has_include("lvgl.h")
- #ifndef LV_LVGL_H_INCLUDE_SIMPLE
- #define LV_LVGL_H_INCLUDE_SIMPLE
- #endif
- #endif
-#endif
-
-#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
- #include "lvgl.h"
-#else
- #include "lvgl/lvgl.h"
-#endif
-
-
-#ifndef LV_ATTRIBUTE_MEM_ALIGN
-#define LV_ATTRIBUTE_MEM_ALIGN
-#endif
-
-#ifndef LV_ATTRIBUTE_IMG_BATTERY_20
-#define LV_ATTRIBUTE_IMG_BATTERY_20
-#endif
-
-static const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_20 uint8_t battery_20_map[] = {
- 0x00, 0x00, 0x00, 0xff, /*Color of index 0*/
- 0xfd, 0xfe, 0xfd, 0xff, /*Color of index 1*/
- 0x26, 0xc1, 0x38, 0xff, /*Color of index 2*/
- 0x01, 0xbe, 0x37, 0xff, /*Color of index 3*/
-
- 0x55, 0x00, 0x55,
- 0x54, 0x00, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0xaa, 0x15,
- 0x54, 0xff, 0x15,
- 0x54, 0x00, 0x15,
-};
-
-const lv_img_dsc_t kIconBattery20 = {
- .header.cf = LV_IMG_CF_INDEXED_2BIT,
- .header.always_zero = 0,
- .header.reserved = 0,
- .header.w = 12,
- .header.h = 12,
- .data_size = 52,
- .data = battery_20_map,
-};
diff --git a/src/ui/icons/battery_40.c b/src/ui/icons/battery_40.c
deleted file mode 100644
index 4a6ead0c..00000000
--- a/src/ui/icons/battery_40.c
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifdef __has_include
- #if __has_include("lvgl.h")
- #ifndef LV_LVGL_H_INCLUDE_SIMPLE
- #define LV_LVGL_H_INCLUDE_SIMPLE
- #endif
- #endif
-#endif
-
-#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
- #include "lvgl.h"
-#else
- #include "lvgl/lvgl.h"
-#endif
-
-
-#ifndef LV_ATTRIBUTE_MEM_ALIGN
-#define LV_ATTRIBUTE_MEM_ALIGN
-#endif
-
-#ifndef LV_ATTRIBUTE_IMG_BATTERY_40
-#define LV_ATTRIBUTE_IMG_BATTERY_40
-#endif
-
-static const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_40 uint8_t battery_40_map[] = {
- 0x00, 0x00, 0x00, 0xff, /*Color of index 0*/
- 0xfd, 0xfe, 0xfd, 0xff, /*Color of index 1*/
- 0x26, 0xc1, 0x38, 0xff, /*Color of index 2*/
- 0x01, 0xbe, 0x37, 0xff, /*Color of index 3*/
-
- 0x55, 0x00, 0x55,
- 0x54, 0x00, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0xaa, 0x15,
- 0x54, 0xff, 0x15,
- 0x54, 0xff, 0x15,
- 0x54, 0x00, 0x15,
-};
-
-const lv_img_dsc_t kIconBattery40 = {
- .header.cf = LV_IMG_CF_INDEXED_2BIT,
- .header.always_zero = 0,
- .header.reserved = 0,
- .header.w = 12,
- .header.h = 12,
- .data_size = 52,
- .data = battery_40_map,
-};
diff --git a/src/ui/icons/battery_60.c b/src/ui/icons/battery_60.c
deleted file mode 100644
index 4695cb73..00000000
--- a/src/ui/icons/battery_60.c
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifdef __has_include
- #if __has_include("lvgl.h")
- #ifndef LV_LVGL_H_INCLUDE_SIMPLE
- #define LV_LVGL_H_INCLUDE_SIMPLE
- #endif
- #endif
-#endif
-
-#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
- #include "lvgl.h"
-#else
- #include "lvgl/lvgl.h"
-#endif
-
-
-#ifndef LV_ATTRIBUTE_MEM_ALIGN
-#define LV_ATTRIBUTE_MEM_ALIGN
-#endif
-
-#ifndef LV_ATTRIBUTE_IMG_BATTERY_60
-#define LV_ATTRIBUTE_IMG_BATTERY_60
-#endif
-
-static const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_60 uint8_t battery_60_map[] = {
- 0x00, 0x00, 0x00, 0xff, /*Color of index 0*/
- 0xfd, 0xfe, 0xfd, 0xff, /*Color of index 1*/
- 0x26, 0xc1, 0x38, 0xff, /*Color of index 2*/
- 0x01, 0xbe, 0x37, 0xff, /*Color of index 3*/
-
- 0x55, 0x00, 0x55,
- 0x54, 0x00, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0xaa, 0x15,
- 0x54, 0xaa, 0x15,
- 0x54, 0xaa, 0x15,
- 0x54, 0xff, 0x15,
- 0x54, 0xff, 0x15,
- 0x54, 0x00, 0x15,
-};
-
-const lv_img_dsc_t kIconBattery60 = {
- .header.cf = LV_IMG_CF_INDEXED_2BIT,
- .header.always_zero = 0,
- .header.reserved = 0,
- .header.w = 12,
- .header.h = 12,
- .data_size = 52,
- .data = battery_60_map,
-};
diff --git a/src/ui/icons/battery_80.c b/src/ui/icons/battery_80.c
deleted file mode 100644
index e0b60dfe..00000000
--- a/src/ui/icons/battery_80.c
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifdef __has_include
- #if __has_include("lvgl.h")
- #ifndef LV_LVGL_H_INCLUDE_SIMPLE
- #define LV_LVGL_H_INCLUDE_SIMPLE
- #endif
- #endif
-#endif
-
-#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
- #include "lvgl.h"
-#else
- #include "lvgl/lvgl.h"
-#endif
-
-
-#ifndef LV_ATTRIBUTE_MEM_ALIGN
-#define LV_ATTRIBUTE_MEM_ALIGN
-#endif
-
-#ifndef LV_ATTRIBUTE_IMG_BATTERY_80
-#define LV_ATTRIBUTE_IMG_BATTERY_80
-#endif
-
-static const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_80 uint8_t battery_80_map[] = {
- 0x00, 0x00, 0x00, 0xff, /*Color of index 0*/
- 0xfd, 0xfe, 0xfd, 0xff, /*Color of index 1*/
- 0x26, 0xc1, 0x38, 0xff, /*Color of index 2*/
- 0x01, 0xbe, 0x37, 0xff, /*Color of index 3*/
-
- 0x55, 0x00, 0x55,
- 0x54, 0x00, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0x55, 0x15,
- 0x54, 0xaa, 0x15,
- 0x54, 0xaa, 0x15,
- 0x54, 0xaa, 0x15,
- 0x54, 0xaa, 0x15,
- 0x54, 0xaa, 0x15,
- 0x54, 0xff, 0x15,
- 0x54, 0xff, 0x15,
- 0x54, 0x00, 0x15,
-};
-
-const lv_img_dsc_t kIconBattery80 = {
- .header.cf = LV_IMG_CF_INDEXED_2BIT,
- .header.always_zero = 0,
- .header.reserved = 0,
- .header.w = 12,
- .header.h = 12,
- .data_size = 52,
- .data = battery_80_map,
-};
diff --git a/src/ui/icons/battery_empty.c b/src/ui/icons/battery_empty.c
deleted file mode 100644
index 26f84863..00000000
--- a/src/ui/icons/battery_empty.c
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifdef __has_include
- #if __has_include("lvgl.h")
- #ifndef LV_LVGL_H_INCLUDE_SIMPLE
- #define LV_LVGL_H_INCLUDE_SIMPLE
- #endif
- #endif
-#endif
-
-#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
- #include "lvgl.h"
-#else
- #include "lvgl/lvgl.h"
-#endif
-
-
-#ifndef LV_ATTRIBUTE_MEM_ALIGN
-#define LV_ATTRIBUTE_MEM_ALIGN
-#endif
-
-#ifndef LV_ATTRIBUTE_IMG_BATTERY_EMPTY
-#define LV_ATTRIBUTE_IMG_BATTERY_EMPTY
-#endif
-
-const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_EMPTY uint8_t battery_empty_map[] = {
- 0xfd, 0xfd, 0xfd, 0xff, /*Color of index 0*/
- 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/
- 0x26, 0x2c, 0xfa, 0xff, /*Color of index 2*/
- 0x00, 0x00, 0x00, 0x00, /*Color of index 3*/
-
- 0x00, 0x55, 0x00,
- 0x01, 0x55, 0x40,
- 0x01, 0x00, 0x40,
- 0x01, 0x00, 0x40,
- 0x01, 0x00, 0x40,
- 0x01, 0x00, 0x40,
- 0x01, 0x00, 0x40,
- 0x01, 0x00, 0x40,
- 0x01, 0x00, 0x40,
- 0x01, 0xaa, 0x40,
- 0x01, 0xaa, 0x40,
- 0x01, 0x55, 0x40,
-};
-
-const lv_img_dsc_t kIconBatteryEmpty = {
- .header.cf = LV_IMG_CF_INDEXED_2BIT,
- .header.always_zero = 0,
- .header.reserved = 0,
- .header.w = 12,
- .header.h = 12,
- .data_size = 52,
- .data = battery_empty_map,
-};
diff --git a/src/ui/icons/battery_full.c b/src/ui/icons/battery_full.c
deleted file mode 100644
index 1e3b17e3..00000000
--- a/src/ui/icons/battery_full.c
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifdef __has_include
- #if __has_include("lvgl.h")
- #ifndef LV_LVGL_H_INCLUDE_SIMPLE
- #define LV_LVGL_H_INCLUDE_SIMPLE
- #endif
- #endif
-#endif
-
-#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
- #include "lvgl.h"
-#else
- #include "lvgl/lvgl.h"
-#endif
-
-
-#ifndef LV_ATTRIBUTE_MEM_ALIGN
-#define LV_ATTRIBUTE_MEM_ALIGN
-#endif
-
-#ifndef LV_ATTRIBUTE_IMG_BATTERY_FULL
-#define LV_ATTRIBUTE_IMG_BATTERY_FULL
-#endif
-
-const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BATTERY_FULL uint8_t battery_full_map[] = {
- 0x00, 0x00, 0x00, 0xff, /*Color of index 0*/
- 0xfd, 0xfe, 0xfd, 0xff, /*Color of index 1*/
- 0x26, 0xc1, 0x38, 0xff, /*Color of index 2*/
- 0x01, 0xbe, 0x37, 0xff, /*Color of index 3*/
-
- 0x55, 0x00, 0x55,
- 0x54, 0x00, 0x15,
- 0x54, 0xaa, 0x15,
- 0x54, 0xaa, 0x15,
- 0x54, 0xaa, 0x15,
- 0x54, 0xaa, 0x15,
- 0x54, 0xaa, 0x15,
- 0x54, 0xaa, 0x15,
- 0x54, 0xaa, 0x15,
- 0x54, 0xff, 0x15,
- 0x54, 0xff, 0x15,
- 0x54, 0x00, 0x15,
-};
-
-const lv_img_dsc_t kIconBatteryFull = {
- .header.cf = LV_IMG_CF_INDEXED_2BIT,
- .header.always_zero = 0,
- .header.reserved = 0,
- .header.w = 12,
- .header.h = 12,
- .data_size = 52,
- .data = battery_full_map,
-};
diff --git a/src/ui/icons/bluetooth.c b/src/ui/icons/bluetooth.c
deleted file mode 100644
index 66322f8f..00000000
--- a/src/ui/icons/bluetooth.c
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifdef __has_include
- #if __has_include("lvgl.h")
- #ifndef LV_LVGL_H_INCLUDE_SIMPLE
- #define LV_LVGL_H_INCLUDE_SIMPLE
- #endif
- #endif
-#endif
-
-#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
- #include "lvgl.h"
-#else
- #include "lvgl/lvgl.h"
-#endif
-
-
-#ifndef LV_ATTRIBUTE_MEM_ALIGN
-#define LV_ATTRIBUTE_MEM_ALIGN
-#endif
-
-#ifndef LV_ATTRIBUTE_IMG_BLUETOOTH
-#define LV_ATTRIBUTE_IMG_BLUETOOTH
-#endif
-
-static const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BLUETOOTH uint8_t bluetooth_map[] = {
- 0xfe, 0xfe, 0xfe, 0xff, /*Color of index 0*/
- 0xff, 0x75, 0x00, 0xff, /*Color of index 1*/
- 0x00, 0x00, 0x00, 0x00, /*Color of index 2*/
- 0x00, 0x00, 0x00, 0x00, /*Color of index 3*/
-
- 0x00, 0x55, 0x40, 0x00,
- 0x01, 0x51, 0x50, 0x00,
- 0x05, 0x50, 0x54, 0x00,
- 0x05, 0x51, 0x14, 0x00,
- 0x05, 0x11, 0x04, 0x00,
- 0x15, 0x40, 0x15, 0x00,
- 0x15, 0x50, 0x55, 0x00,
- 0x15, 0x40, 0x15, 0x00,
- 0x05, 0x11, 0x04, 0x00,
- 0x05, 0x51, 0x14, 0x00,
- 0x05, 0x50, 0x54, 0x00,
- 0x01, 0x51, 0x50, 0x00,
- 0x00, 0x55, 0x40, 0x00,
- 0x00, 0x00, 0x00, 0x00,
-};
-
-const lv_img_dsc_t kIconBluetooth = {
- .header.cf = LV_IMG_CF_INDEXED_2BIT,
- .header.always_zero = 0,
- .header.reserved = 0,
- .header.w = 14,
- .header.h = 14,
- .data_size = 72,
- .data = bluetooth_map,
-};
diff --git a/src/ui/icons/pause.c b/src/ui/icons/pause.c
deleted file mode 100644
index 8201b5bb..00000000
--- a/src/ui/icons/pause.c
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifdef __has_include
- #if __has_include("lvgl.h")
- #ifndef LV_LVGL_H_INCLUDE_SIMPLE
- #define LV_LVGL_H_INCLUDE_SIMPLE
- #endif
- #endif
-#endif
-
-#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
- #include "lvgl.h"
-#else
- #include "lvgl/lvgl.h"
-#endif
-
-
-#ifndef LV_ATTRIBUTE_MEM_ALIGN
-#define LV_ATTRIBUTE_MEM_ALIGN
-#endif
-
-#ifndef LV_ATTRIBUTE_IMG_PAUSE
-#define LV_ATTRIBUTE_IMG_PAUSE
-#endif
-
-static const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_PAUSE uint8_t pause_map[] = {
- 0xfe, 0xfe, 0xfe, 0xff, /*Color of index 0*/
- 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/
- 0x00, 0x00, 0x00, 0x00, /*Color of index 2*/
- 0x00, 0x00, 0x00, 0x00, /*Color of index 3*/
-
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x50, 0x14, 0x00,
- 0x00, 0x50, 0x14, 0x00,
- 0x00, 0x50, 0x14, 0x00,
- 0x00, 0x50, 0x14, 0x00,
- 0x00, 0x50, 0x14, 0x00,
- 0x00, 0x50, 0x14, 0x00,
- 0x00, 0x50, 0x14, 0x00,
- 0x00, 0x50, 0x14, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
-};
-
-const lv_img_dsc_t kIconPause = {
- .header.cf = LV_IMG_CF_INDEXED_2BIT,
- .header.always_zero = 0,
- .header.reserved = 0,
- .header.w = 14,
- .header.h = 14,
- .data_size = 72,
- .data = pause_map,
-};
diff --git a/src/ui/icons/play.c b/src/ui/icons/play.c
deleted file mode 100644
index 8984eae4..00000000
--- a/src/ui/icons/play.c
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifdef __has_include
- #if __has_include("lvgl.h")
- #ifndef LV_LVGL_H_INCLUDE_SIMPLE
- #define LV_LVGL_H_INCLUDE_SIMPLE
- #endif
- #endif
-#endif
-
-#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
- #include "lvgl.h"
-#else
- #include "lvgl/lvgl.h"
-#endif
-
-
-#ifndef LV_ATTRIBUTE_MEM_ALIGN
-#define LV_ATTRIBUTE_MEM_ALIGN
-#endif
-
-#ifndef LV_ATTRIBUTE_IMG_PLAY
-#define LV_ATTRIBUTE_IMG_PLAY
-#endif
-
-static const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_PLAY uint8_t play_map[] = {
- 0xfe, 0xfe, 0xfe, 0xff, /*Color of index 0*/
- 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/
- 0x00, 0x00, 0x00, 0x00, /*Color of index 2*/
- 0x00, 0x00, 0x00, 0x00, /*Color of index 3*/
-
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x50, 0x00, 0x00,
- 0x00, 0x55, 0x00, 0x00,
- 0x00, 0x55, 0x50, 0x00,
- 0x00, 0x55, 0x54, 0x00,
- 0x00, 0x55, 0x54, 0x00,
- 0x00, 0x55, 0x50, 0x00,
- 0x00, 0x55, 0x00, 0x00,
- 0x00, 0x50, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
-};
-
-const lv_img_dsc_t kIconPlay = {
- .header.cf = LV_IMG_CF_INDEXED_2BIT,
- .header.always_zero = 0,
- .header.reserved = 0,
- .header.w = 14,
- .header.h = 14,
- .data_size = 72,
- .data = play_map,
-};
diff --git a/src/ui/include/ui_tick.hpp b/src/ui/include/ui_tick.hpp
deleted file mode 100644
index 37f8a8bd..00000000
--- a/src/ui/include/ui_tick.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#pragma once
-
-#include "esp_timer.h"
-
-#define LV_TICK_CUSTOM_SYS_TIME_EXPR (esp_timer_get_time() / 1000)
diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt
index bb4ce320..49554be8 100644
--- a/src/util/CMakeLists.txt
+++ b/src/util/CMakeLists.txt
@@ -2,4 +2,4 @@
#
# SPDX-License-Identifier: GPL-3.0-only
-idf_component_register(SRCS INCLUDE_DIRS "include" REQUIRES "database" "span")
+idf_component_register(SRCS INCLUDE_DIRS "include" REQUIRES "memory")
diff --git a/src/util/include/debug.hpp b/src/util/include/debug.hpp
index 620b0974..27fb2999 100644
--- a/src/util/include/debug.hpp
+++ b/src/util/include/debug.hpp
@@ -8,13 +8,12 @@
#include <iomanip>
#include <ostream>
-
+#include <span>
#include <string>
-#include "span.hpp"
namespace util {
-inline std::string format_hex_string(cpp::span<const std::byte> data) {
+inline std::string format_hex_string(std::span<const std::byte> data) {
std::ostringstream oss;
std::ostringstream ascii_values;
int count = 0;
diff --git a/src/util/random.cpp b/src/util/random.cpp
index ae543765..2b2af9c7 100644
--- a/src/util/random.cpp
+++ b/src/util/random.cpp
@@ -29,8 +29,8 @@ auto Random::Next() -> std::uint64_t {
return komirand(&seed1_, &seed2_);
}
-auto Random::RangeInclusive(std::uint64_t lower, std::uint64_t upper)
- -> std::uint64_t {
+auto Random::RangeInclusive(std::uint64_t lower,
+ std::uint64_t upper) -> std::uint64_t {
return (Next() % (upper - lower + 1)) + lower;
}