diff --git a/.reuse/dep5 b/.reuse/dep5 index 71ca581e..615c30c4 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -66,10 +66,6 @@ Files: lib/result/include/* Copyright: 2017-2021 Matthew Rodusek License: MIT -Files: lib/span/include/* -Copyright: 2018 Tristan Brindle -License: BSL-1.0 - Files: lib/speexdsp/* Copyright: 2002-2008 Xiph.org Foundation 2002-2008 Jean-Marc Valin diff --git a/CMakeLists.txt b/CMakeLists.txt index 89ebf8aa..f9d1f72c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,10 +16,15 @@ list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/src") project(tangara) +# 'collate' partition on internal flash. Contains collation data for sorting +# strings across languages (defaults to a generic dataset based on ISO14651). +get_filename_component(collate_full_path "tools/collate/Generic.LC_COLLATE" ABSOLUTE) +esptool_py_flash_to_partition(flash "collate" ${collate_full_path}) + # /lua partition on internal flash, for storing the lua application spiffs_create_partition_image(lua lua FLASH_IN_PROJECT) -# /repl partition on internal flash, for storing the developer repl and its deps. +# /repl partition on internal flash, for storing the developer repl and its deps file(COPY lib/lua-repl/repl DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/repl) file(COPY lib/lua-term/term DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/repl) spiffs_create_partition_image(repl ${CMAKE_CURRENT_BINARY_DIR}/repl FLASH_IN_PROJECT) diff --git a/lib/lvgl/env_support/cmake/esp.cmake b/lib/lvgl/env_support/cmake/esp.cmake index 443fb514..4b4b91da 100644 --- a/lib/lvgl/env_support/cmake/esp.cmake +++ b/lib/lvgl/env_support/cmake/esp.cmake @@ -27,7 +27,6 @@ else() ${LVGL_ROOT_DIR} ${LVGL_ROOT_DIR}/src ${LVGL_ROOT_DIR}/../ - $ENV{PROJ_PATH}/src/ui/include/ REQUIRES esp_timer) target_compile_definitions(${COMPONENT_LIB} PUBLIC "-DLV_CONF_INCLUDE_SIMPLE") diff --git a/lib/lvgl/lv_conf.h b/lib/lvgl/lv_conf.h index fdaeb390..9617d253 100644 --- a/lib/lvgl/lv_conf.h +++ b/lib/lvgl/lv_conf.h @@ -87,7 +87,8 @@ *It removes the need to manually update the tick with `lv_tick_inc()`)*/ #define LV_TICK_CUSTOM 1 #if LV_TICK_CUSTOM - #define LV_TICK_CUSTOM_INCLUDE "ui_tick.hpp" /*Header for the system time function*/ + #define LV_TICK_CUSTOM_INCLUDE "esp_timer.h" + #define LV_TICK_CUSTOM_SYS_TIME_EXPR (esp_timer_get_time() / 1000) #endif /*LV_TICK_CUSTOM*/ /*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings. diff --git a/lib/span/CMakeLists.txt b/lib/span/CMakeLists.txt deleted file mode 100644 index 57871f57..00000000 --- a/lib/span/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright 2023 jacqueline -# -# SPDX-License-Identifier: GPL-3.0-only - -idf_component_register(INCLUDE_DIRS "include") diff --git a/lib/span/LICENSE.txt b/lib/span/LICENSE.txt deleted file mode 100644 index 36b7cd93..00000000 --- a/lib/span/LICENSE.txt +++ /dev/null @@ -1,23 +0,0 @@ -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/lib/span/include/span.hpp b/lib/span/include/span.hpp deleted file mode 100644 index fdc3a988..00000000 --- a/lib/span/include/span.hpp +++ /dev/null @@ -1,618 +0,0 @@ - -/* -This is an implementation of C++20's std::span -http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/n4820.pdf -*/ - -// Copyright Tristan Brindle 2018. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file ../../LICENSE_1_0.txt or copy at -// https://www.boost.org/LICENSE_1_0.txt) - -#ifndef TCB_SPAN_HPP_INCLUDED -#define TCB_SPAN_HPP_INCLUDED - -#include -#include -#include -#include - -#ifndef TCB_SPAN_NO_EXCEPTIONS -// Attempt to discover whether we're being compiled with exception support -#if !(defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) -#define TCB_SPAN_NO_EXCEPTIONS -#endif -#endif - -#ifndef TCB_SPAN_NO_EXCEPTIONS -#include -#include -#endif - -// Various feature test macros - -#ifndef TCB_SPAN_NAMESPACE_NAME -#define TCB_SPAN_NAMESPACE_NAME tcb -#endif - -#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -#define TCB_SPAN_HAVE_CPP17 -#endif - -#if __cplusplus >= 201402L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) -#define TCB_SPAN_HAVE_CPP14 -#endif - -namespace TCB_SPAN_NAMESPACE_NAME { - -// Establish default contract checking behavior -#if !defined(TCB_SPAN_THROW_ON_CONTRACT_VIOLATION) && \ - !defined(TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION) && \ - !defined(TCB_SPAN_NO_CONTRACT_CHECKING) -#if defined(NDEBUG) || !defined(TCB_SPAN_HAVE_CPP14) -#define TCB_SPAN_NO_CONTRACT_CHECKING -#else -#define TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION -#endif -#endif - -#if defined(TCB_SPAN_THROW_ON_CONTRACT_VIOLATION) -struct contract_violation_error : std::logic_error { - explicit contract_violation_error(const char* msg) : std::logic_error(msg) - {} -}; - -inline void contract_violation(const char* msg) -{ - throw contract_violation_error(msg); -} - -#elif defined(TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION) -[[noreturn]] inline void contract_violation(const char* /*unused*/) -{ - std::terminate(); -} -#endif - -#if !defined(TCB_SPAN_NO_CONTRACT_CHECKING) -#define TCB_SPAN_STRINGIFY(cond) #cond -#define TCB_SPAN_EXPECT(cond) \ - cond ? (void) 0 : contract_violation("Expected " TCB_SPAN_STRINGIFY(cond)) -#else -#define TCB_SPAN_EXPECT(cond) -#endif - -#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_inline_variables) -#define TCB_SPAN_INLINE_VAR inline -#else -#define TCB_SPAN_INLINE_VAR -#endif - -#if defined(TCB_SPAN_HAVE_CPP14) || \ - (defined(__cpp_constexpr) && __cpp_constexpr >= 201304) -#define TCB_SPAN_HAVE_CPP14_CONSTEXPR -#endif - -#if defined(TCB_SPAN_HAVE_CPP14_CONSTEXPR) -#define TCB_SPAN_CONSTEXPR14 constexpr -#else -#define TCB_SPAN_CONSTEXPR14 -#endif - -#if defined(TCB_SPAN_HAVE_CPP14_CONSTEXPR) && \ - (!defined(_MSC_VER) || _MSC_VER > 1900) -#define TCB_SPAN_CONSTEXPR_ASSIGN constexpr -#else -#define TCB_SPAN_CONSTEXPR_ASSIGN -#endif - -#if defined(TCB_SPAN_NO_CONTRACT_CHECKING) -#define TCB_SPAN_CONSTEXPR11 constexpr -#else -#define TCB_SPAN_CONSTEXPR11 TCB_SPAN_CONSTEXPR14 -#endif - -#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_deduction_guides) -#define TCB_SPAN_HAVE_DEDUCTION_GUIDES -#endif - -#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_byte) -#define TCB_SPAN_HAVE_STD_BYTE -#endif - -#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_array_constexpr) -#define TCB_SPAN_HAVE_CONSTEXPR_STD_ARRAY_ETC -#endif - -#if defined(TCB_SPAN_HAVE_CONSTEXPR_STD_ARRAY_ETC) -#define TCB_SPAN_ARRAY_CONSTEXPR constexpr -#else -#define TCB_SPAN_ARRAY_CONSTEXPR -#endif - -#ifdef TCB_SPAN_HAVE_STD_BYTE -using byte = std::byte; -#else -using byte = unsigned char; -#endif - -#if defined(TCB_SPAN_HAVE_CPP17) -#define TCB_SPAN_NODISCARD [[nodiscard]] -#else -#define TCB_SPAN_NODISCARD -#endif - -TCB_SPAN_INLINE_VAR constexpr std::size_t dynamic_extent = SIZE_MAX; - -template -class span; - -namespace detail { - -template -struct span_storage { - constexpr span_storage() noexcept = default; - - constexpr span_storage(E* p_ptr, std::size_t /*unused*/) noexcept - : ptr(p_ptr) - {} - - E* ptr = nullptr; - static constexpr std::size_t size = S; -}; - -template -struct span_storage { - constexpr span_storage() noexcept = default; - - constexpr span_storage(E* p_ptr, std::size_t p_size) noexcept - : ptr(p_ptr), size(p_size) - {} - - E* ptr = nullptr; - std::size_t size = 0; -}; - -// Reimplementation of C++17 std::size() and std::data() -#if defined(TCB_SPAN_HAVE_CPP17) || \ - defined(__cpp_lib_nonmember_container_access) -using std::data; -using std::size; -#else -template -constexpr auto size(const C& c) -> decltype(c.size()) -{ - return c.size(); -} - -template -constexpr std::size_t size(const T (&)[N]) noexcept -{ - return N; -} - -template -constexpr auto data(C& c) -> decltype(c.data()) -{ - return c.data(); -} - -template -constexpr auto data(const C& c) -> decltype(c.data()) -{ - return c.data(); -} - -template -constexpr T* data(T (&array)[N]) noexcept -{ - return array; -} - -template -constexpr const E* data(std::initializer_list il) noexcept -{ - return il.begin(); -} -#endif // TCB_SPAN_HAVE_CPP17 - -#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_void_t) -using std::void_t; -#else -template -using void_t = void; -#endif - -template -using uncvref_t = - typename std::remove_cv::type>::type; - -template -struct is_span : std::false_type {}; - -template -struct is_span> : std::true_type {}; - -template -struct is_std_array : std::false_type {}; - -template -struct is_std_array> : std::true_type {}; - -template -struct has_size_and_data : std::false_type {}; - -template -struct has_size_and_data())), - decltype(detail::data(std::declval()))>> - : std::true_type {}; - -template > -struct is_container { - static constexpr bool value = - !is_span::value && !is_std_array::value && - !std::is_array::value && has_size_and_data::value; -}; - -template -using remove_pointer_t = typename std::remove_pointer::type; - -template -struct is_container_element_type_compatible : std::false_type {}; - -template -struct is_container_element_type_compatible< - T, E, - typename std::enable_if< - !std::is_same< - typename std::remove_cv()))>::type, - void>::value && - std::is_convertible< - remove_pointer_t()))> (*)[], - E (*)[]>::value - >::type> - : std::true_type {}; - -template -struct is_complete : std::false_type {}; - -template -struct is_complete : std::true_type {}; - -} // namespace detail - -template -class span { - static_assert(std::is_object::value, - "A span's ElementType must be an object type (not a " - "reference type or void)"); - static_assert(detail::is_complete::value, - "A span's ElementType must be a complete type (not a forward " - "declaration)"); - static_assert(!std::is_abstract::value, - "A span's ElementType cannot be an abstract class type"); - - using storage_type = detail::span_storage; - -public: - // constants and types - using element_type = ElementType; - using value_type = typename std::remove_cv::type; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using pointer = element_type*; - using const_pointer = const element_type*; - using reference = element_type&; - using const_reference = const element_type&; - using iterator = pointer; - using reverse_iterator = std::reverse_iterator; - - static constexpr size_type extent = Extent; - - // [span.cons], span constructors, copy, assignment, and destructor - template < - std::size_t E = Extent, - typename std::enable_if<(E == dynamic_extent || E <= 0), int>::type = 0> - constexpr span() noexcept - {} - - TCB_SPAN_CONSTEXPR11 span(pointer ptr, size_type count) - : storage_(ptr, count) - { - TCB_SPAN_EXPECT(extent == dynamic_extent || count == extent); - } - - TCB_SPAN_CONSTEXPR11 span(pointer first_elem, pointer last_elem) - : storage_(first_elem, last_elem - first_elem) - { - TCB_SPAN_EXPECT(extent == dynamic_extent || - last_elem - first_elem == - static_cast(extent)); - } - - template ::value, - int>::type = 0> - constexpr span(element_type (&arr)[N]) noexcept : storage_(arr, N) - {} - - template &, ElementType>::value, - int>::type = 0> - TCB_SPAN_ARRAY_CONSTEXPR span(std::array& arr) noexcept - : storage_(arr.data(), N) - {} - - template &, ElementType>::value, - int>::type = 0> - TCB_SPAN_ARRAY_CONSTEXPR span(const std::array& arr) noexcept - : storage_(arr.data(), N) - {} - - template < - typename Container, std::size_t E = Extent, - typename std::enable_if< - E == dynamic_extent && detail::is_container::value && - detail::is_container_element_type_compatible< - Container&, ElementType>::value, - int>::type = 0> - constexpr span(Container& cont) - : storage_(detail::data(cont), detail::size(cont)) - {} - - template < - typename Container, std::size_t E = Extent, - typename std::enable_if< - E == dynamic_extent && detail::is_container::value && - detail::is_container_element_type_compatible< - const Container&, ElementType>::value, - int>::type = 0> - constexpr span(const Container& cont) - : storage_(detail::data(cont), detail::size(cont)) - {} - - constexpr span(const span& other) noexcept = default; - - template ::value, - int>::type = 0> - constexpr span(const span& other) noexcept - : storage_(other.data(), other.size()) - {} - - ~span() noexcept = default; - - TCB_SPAN_CONSTEXPR_ASSIGN span& - operator=(const span& other) noexcept = default; - - // [span.sub], span subviews - template - TCB_SPAN_CONSTEXPR11 span first() const - { - TCB_SPAN_EXPECT(Count <= size()); - return {data(), Count}; - } - - template - TCB_SPAN_CONSTEXPR11 span last() const - { - TCB_SPAN_EXPECT(Count <= size()); - return {data() + (size() - Count), Count}; - } - - template - using subspan_return_t = - span; - - template - TCB_SPAN_CONSTEXPR11 subspan_return_t subspan() const - { - TCB_SPAN_EXPECT(Offset <= size() && - (Count == dynamic_extent || Offset + Count <= size())); - return {data() + Offset, - Count != dynamic_extent ? Count : size() - Offset}; - } - - TCB_SPAN_CONSTEXPR11 span - first(size_type count) const - { - TCB_SPAN_EXPECT(count <= size()); - return {data(), count}; - } - - TCB_SPAN_CONSTEXPR11 span - last(size_type count) const - { - TCB_SPAN_EXPECT(count <= size()); - return {data() + (size() - count), count}; - } - - TCB_SPAN_CONSTEXPR11 span - subspan(size_type offset, size_type count = dynamic_extent) const - { - TCB_SPAN_EXPECT(offset <= size() && - (count == dynamic_extent || offset + count <= size())); - return {data() + offset, - count == dynamic_extent ? size() - offset : count}; - } - - // [span.obs], span observers - constexpr size_type size() const noexcept { return storage_.size; } - - constexpr size_type size_bytes() const noexcept - { - return size() * sizeof(element_type); - } - - TCB_SPAN_NODISCARD constexpr bool empty() const noexcept - { - return size() == 0; - } - - // [span.elem], span element access - TCB_SPAN_CONSTEXPR11 reference operator[](size_type idx) const - { - TCB_SPAN_EXPECT(idx < size()); - return *(data() + idx); - } - - TCB_SPAN_CONSTEXPR11 reference front() const - { - TCB_SPAN_EXPECT(!empty()); - return *data(); - } - - TCB_SPAN_CONSTEXPR11 reference back() const - { - TCB_SPAN_EXPECT(!empty()); - return *(data() + (size() - 1)); - } - - constexpr pointer data() const noexcept { return storage_.ptr; } - - // [span.iterators], span iterator support - constexpr iterator begin() const noexcept { return data(); } - - constexpr iterator end() const noexcept { return data() + size(); } - - TCB_SPAN_ARRAY_CONSTEXPR reverse_iterator rbegin() const noexcept - { - return reverse_iterator(end()); - } - - TCB_SPAN_ARRAY_CONSTEXPR reverse_iterator rend() const noexcept - { - return reverse_iterator(begin()); - } - -private: - storage_type storage_{}; -}; - -#ifdef TCB_SPAN_HAVE_DEDUCTION_GUIDES - -/* Deduction Guides */ -template -span(T (&)[N])->span; - -template -span(std::array&)->span; - -template -span(const std::array&)->span; - -template -span(Container&)->span()))>::type>; - -template -span(const Container&)->span; - -#endif // TCB_HAVE_DEDUCTION_GUIDES - -template -constexpr span -make_span(span s) noexcept -{ - return s; -} - -template -constexpr span make_span(T (&arr)[N]) noexcept -{ - return {arr}; -} - -template -TCB_SPAN_ARRAY_CONSTEXPR span make_span(std::array& arr) noexcept -{ - return {arr}; -} - -template -TCB_SPAN_ARRAY_CONSTEXPR span -make_span(const std::array& arr) noexcept -{ - return {arr}; -} - -template -constexpr span()))>::type> -make_span(Container& cont) -{ - return {cont}; -} - -template -constexpr span -make_span(const Container& cont) -{ - return {cont}; -} - -template -span -as_bytes(span s) noexcept -{ - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -template < - class ElementType, size_t Extent, - typename std::enable_if::value, int>::type = 0> -span -as_writable_bytes(span s) noexcept -{ - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -template -constexpr auto get(span s) -> decltype(s[N]) -{ - return s[N]; -} - -} // namespace TCB_SPAN_NAMESPACE_NAME - -namespace std { - -template -class tuple_size> - : public integral_constant {}; - -template -class tuple_size>; // not defined - -template -class tuple_element> { -public: - static_assert(Extent != TCB_SPAN_NAMESPACE_NAME::dynamic_extent && - I < Extent, - ""); - using type = ElementType; -}; - -} // end namespace std - -#endif // TCB_SPAN_HPP_INCLUDED diff --git a/lua/licenses.lua b/lua/licenses.lua index 8d5813c4..b8c71f36 100644 --- a/lua/licenses.lua +++ b/lua/licenses.lua @@ -63,16 +63,6 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.]]) end -local function boost(copyright) - show_license(copyright .. [[ - -Boost Software License - Version 1.0 - August 17th, 2003 -Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: -The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.]]) -end - - return function(self) local container = self.root:Object { flex = { @@ -159,9 +149,6 @@ return function(self) library("result", "MIT", function() mit("Copyright (c) 2017-2021 Matthew Rodusek") end) - library("span", "Boost", function() - boost("Copyright Tristan Brindle 2018") - end) library("speexdsp", "bsd", function() xiphbsd( "Copyright 2002-2008 Xiph.org Foundation, Copyright 2002-2008 Jean-Marc Valin, Copyright 2005-2007 Analog Devices Inc., Copyright 2005-2008 Commonwealth Scientific and Industrial Research, Organisation (CSIRO), Copyright 1993, 2002, 2006 David Rowe, Copyright 2003 EpicGames, Copyright 1992-1994 Jutta Degener, Carsten Bormann") diff --git a/lua/main.lua b/lua/main.lua index dc73c964..e5adcaae 100644 --- a/lua/main.lua +++ b/lua/main.lua @@ -3,12 +3,15 @@ local vol = require("volume") local theme = require("theme") local controls = require("controls") local time = require("time") - -local lock_time = time.ticks() +local sd_card = require("sd_card") +local backstack = require("backstack") +local main_menu = require("main_menu") local theme_dark = require("theme_dark") theme.set(theme_dark) +local lock_time = time.ticks() + -- Set up property bindings that are used across every screen. GLOBAL_BINDINGS = { -- Show an alert with the current volume whenever the volume changes @@ -52,9 +55,8 @@ GLOBAL_BINDINGS = { end end end), + sd_card.mounted:bind(function(mounted) + print("reset ui stack") + backstack.reset(main_menu:new()) + end), } - -local backstack = require("backstack") -local main_menu = require("main_menu") - -backstack.push(main_menu) 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 -# -# 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 -# -# 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 - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "audio_decoder.hpp" -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#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 source, - std::shared_ptr sink) -> Decoder* { - Decoder* task = new Decoder(source, sink); - tasks::StartPersistent([=]() { task->Main(); }); - return task; -} - -Decoder::Decoder(std::shared_ptr source, - std::shared_ptr mixer) - : source_(source), converter_(mixer), codec_(), current_format_() { - ESP_LOGI(kTag, "allocating codec buffer, %u KiB", kCodecBufferLength / 1024); - codec_buffer_ = { - reinterpret_cast(heap_caps_calloc( - kCodecBufferLength, sizeof(sample::Sample), MALLOC_CAP_DMA)), - kCodecBufferLength}; -} - -void Decoder::Main() { - for (;;) { - if (source_->HasNewStream() || !stream_) { - std::shared_ptr 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 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 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{ - .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 - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "fatfs_audio_input.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 path) -> void { - if (path) { - SetPath(*path); - } else { - SetPath(); - } -} - -auto FatfsAudioInput::SetPath(const std::string& path,uint32_t offset) -> void { - std::lock_guard guard{new_stream_mutex_}; - if (OpenFile(path, offset)) { - has_new_stream_ = true; - has_new_stream_.notify_one(); - } -} - -auto FatfsAudioInput::SetPath() -> void { - std::lock_guard 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 { - while (true) { - has_new_stream_.wait(false); - - { - std::lock_guard 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 file = std::make_unique(); - 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(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 { - 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 - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include -#include - -#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 source, - std::shared_ptr converter) -> Decoder*; - - auto Main() -> void; - - Decoder(const Decoder&) = delete; - Decoder& operator=(const Decoder&) = delete; - - private: - Decoder(std::shared_ptr source, - std::shared_ptr converter); - - auto BeginDecoding(std::shared_ptr) -> bool; - auto ContinueDecoding() -> bool; - - std::shared_ptr source_; - std::shared_ptr converter_; - - std::shared_ptr stream_; - std::unique_ptr codec_; - - std::optional current_format_; - std::optional current_sink_format_; - - cpp::span 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 - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include -#include -#include -#include -#include - -#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) -> void; - auto SetPath(const std::string&,uint32_t offset = 0) -> void; - auto SetPath() -> void; - - auto HasNewStream() -> bool override; - auto NextStream() -> std::shared_ptr 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; - - database::ITagParser& tag_parser_; - tasks::WorkerPool& bg_worker_; - - std::mutex new_stream_mutex_; - std::shared_ptr new_stream_; - - std::atomic 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 -# -# 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 #include -#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 input, uint32_t offset) return format; } -auto DrFlacDecoder::DecodeTo(cpp::span output) +auto DrFlacDecoder::DecodeTo(std::span output) -> cpp::result { 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 -#include - #include #include #include #include +#include #include #include #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 dest) -> ssize_t = 0; + virtual auto Read(std::span 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 input,uint32_t offset) + virtual auto OpenStream(std::shared_ptr input, uint32_t offset) -> cpp::result = 0; struct OutputInfo { @@ -128,7 +125,7 @@ class ICodec { /* * Writes PCM samples to the given output buffer. */ - virtual auto DecodeTo(cpp::span destination) + virtual auto DecodeTo(std::span destination) -> cpp::result = 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 #include #include +#include #include #include #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 input,uint32_t offset) + auto OpenStream(std::shared_ptr input, uint32_t offset) -> cpp::result override; - auto DecodeTo(cpp::span destination) + auto DecodeTo(std::span destination) -> cpp::result override; DrFlacDecoder(const DrFlacDecoder&) = delete; @@ -38,7 +38,7 @@ class DrFlacDecoder : public ICodec { private: std::shared_ptr 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 #include #include +#include #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 input,uint32_t offset) -> cpp::result override; - auto DecodeTo(cpp::span destination) + auto DecodeTo(std::span destination) -> cpp::result 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 #include #include +#include #include #include #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 input,uint32_t offset) + auto OpenStream(std::shared_ptr input, uint32_t offset) -> cpp::result override; - auto DecodeTo(cpp::span destination) + auto DecodeTo(std::span destination) -> cpp::result 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 #include #include - -#include "span.hpp" +#include #include "codec.hpp" @@ -22,15 +21,15 @@ class SourceBuffer { ~SourceBuffer(); auto Refill(IStream* src) -> bool; - auto AddBytes(std::function)> writer) -> void; - auto ConsumeBytes(std::function)> reader) -> void; + auto AddBytes(std::function)> writer) -> void; + auto ConsumeBytes(std::function)> reader) -> void; auto Empty() -> void; SourceBuffer(const SourceBuffer&) = delete; SourceBuffer& operator=(const SourceBuffer&) = delete; private: - const cpp::span buffer_; + const std::span 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 #include #include +#include #include #include #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 input,uint32_t offset) + auto OpenStream(std::shared_ptr input, uint32_t offset) -> cpp::result override; - auto DecodeTo(cpp::span destination) + auto DecodeTo(std::span destination) -> cpp::result 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 input,uint32_t offset) -> cpp::result override; - auto DecodeTo(cpp::span destination) + auto DecodeTo(std::span destination) -> cpp::result 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 input, uint32_t offset) while (!eof && !got_header) { eof = buffer_.Refill(input_.get()); - buffer_.ConsumeBytes([&](cpp::span buf) -> size_t { + buffer_.ConsumeBytes([&](std::span buf) -> size_t { mad_stream_buffer(stream_.get(), reinterpret_cast(buf.data()), buf.size_bytes()); @@ -130,7 +130,7 @@ auto MadMp3Decoder::OpenStream(std::shared_ptr input, uint32_t offset) } need_refill = false; - buffer_.ConsumeBytes([&](cpp::span buf) -> size_t { + buffer_.ConsumeBytes([&](std::span buf) -> size_t { mad_stream_buffer(stream_.get(), reinterpret_cast(buf.data()), buf.size()); @@ -156,13 +156,13 @@ auto MadMp3Decoder::OpenStream(std::shared_ptr input, uint32_t offset) return output; } -auto MadMp3Decoder::DecodeTo(cpp::span output) +auto MadMp3Decoder::DecodeTo(std::span output) -> cpp::result { if (current_sample_ < 0 && !is_eos_) { if (!is_eof_) { is_eof_ = buffer_.Refill(input_.get()); if (is_eof_) { - buffer_.AddBytes([&](cpp::span buf) -> size_t { + buffer_.AddBytes([&](std::span buf) -> size_t { if (buf.size() < MAD_BUFFER_GUARD) { is_eof_ = false; return 0; @@ -174,7 +174,7 @@ auto MadMp3Decoder::DecodeTo(cpp::span output) } } - buffer_.ConsumeBytes([&](cpp::span buf) -> size_t { + buffer_.ConsumeBytes([&](std::span buf) -> size_t { mad_stream_buffer(stream_.get(), reinterpret_cast(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 input, }; } -auto XiphOpusDecoder::DecodeTo(cpp::span output) +auto XiphOpusDecoder::DecodeTo(std::span output) -> cpp::result { 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 buf) -> size_t { + AddBytes([&](std::span 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)> writer) +auto SourceBuffer::AddBytes(std::function)> 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)> writer) } auto SourceBuffer::ConsumeBytes( - std::function)> reader) -> void { - size_t bytes_consumed = std::invoke( - reader, buffer_.subspan(offset_of_bytes_, bytes_in_buffer_)); + std::function)> 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 #include +#include #include "catch2/catch.hpp" -#include "span.hpp" #include "test.mp3.hpp" -void load_mp3(cpp::span dest) { - cpp::span src(reinterpret_cast(test_mp3), +void load_mp3(std::span dest) { + std::span src(reinterpret_cast(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 input, }; } -auto TremorVorbisDecoder::DecodeTo(cpp::span output) +auto TremorVorbisDecoder::DecodeTo(std::span output) -> cpp::result { 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 bytes) +static inline auto bytes_to_u16(std::span bytes) -> uint16_t { return (uint16_t)bytes[0] | (uint16_t)bytes[1] << 8; } -static inline auto bytes_to_u32(cpp::span bytes) +static inline auto bytes_to_u32(std::span 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 bytes) +static inline auto bytes_to_str(std::span bytes) -> std::string { return std::string(reinterpret_cast(bytes.data()), - bytes.size_bytes()); + bytes.size_bytes()); } -static int16_t convert_f32_to_16_bit(cpp::span bytes) { +static int16_t convert_f32_to_16_bit(std::span 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 bytes) { return sample::FromDouble(*fval); } -static int16_t convert_f64_to_16_bit(cpp::span bytes) { +static int16_t convert_f64_to_16_bit(std::span 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 bytes) { return sample::FromDouble(*fval); } -static int16_t convert_to_16_bit(cpp::span bytes) { +static int16_t convert_to_16_bit(std::span 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 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 input,uint32_t offset) +auto WavDecoder::OpenStream(std::shared_ptr input, uint32_t offset) -> cpp::result { input_ = input; @@ -123,7 +126,7 @@ auto WavDecoder::OpenStream(std::shared_ptr 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 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 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 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 data_tag = {std::byte{0x64}, std::byte{0x61}, @@ -180,7 +183,7 @@ auto WavDecoder::OpenStream(std::shared_ptr 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 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 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 input,uint32_t offset) return output_format_; } -auto WavDecoder::DecodeTo(cpp::span output) +auto WavDecoder::DecodeTo(std::span output) -> cpp::result { bool is_eof = buffer_.Refill(input_.get()); size_t samples_written = 0; - buffer_.ConsumeBytes([&](cpp::span buf) -> size_t { + buffer_.ConsumeBytes([&](std::span 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 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 -# -# 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 -# -# 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 #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 @@ -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 sVolumeFactor = 1.f; +DRAM_ATTR static std::atomic 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(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 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 #include @@ -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 @@ -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 #include @@ -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& motor) { // Set library // TODO(robin): try the other libraries and test response. C is marginal, D // too much? - WriteRegister(Register::kWaveformLibrary, static_cast(kDefaultErmLibrary)); + WriteRegister(Register::kWaveformLibrary, + static_cast(kDefaultErmLibrary)); } else if (std::holds_alternative(motor)) { ESP_LOGI(kTag, "Setting up LRA motor..."); @@ -75,7 +76,8 @@ Haptics::Haptics(const std::variant& motor) { ControlMask::kLraOpenLoop); // Set library; only option is the LRA one for, well, LRA motors. - WriteRegister(Register::kWaveformLibrary, static_cast(Library::LRA)); + WriteRegister(Register::kWaveformLibrary, + static_cast(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(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 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 #include #include @@ -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& data) -> void { +auto I2SDac::WriteData(const std::span& 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& 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 similarity index 100% rename from src/drivers/include/a2dp_audio_output.hpp rename to src/drivers/include/drivers/a2dp_audio_output.hpp diff --git a/src/drivers/include/adc.hpp b/src/drivers/include/drivers/adc.hpp similarity index 100% rename from src/drivers/include/adc.hpp rename to src/drivers/include/drivers/adc.hpp diff --git a/src/drivers/include/bluetooth.hpp b/src/drivers/include/drivers/bluetooth.hpp similarity index 98% rename from src/drivers/include/bluetooth.hpp rename to 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 #include #include -#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 cb) -> void; }; diff --git a/src/drivers/include/bluetooth_types.hpp b/src/drivers/include/drivers/bluetooth_types.hpp similarity index 100% rename from src/drivers/include/bluetooth_types.hpp rename to src/drivers/include/drivers/bluetooth_types.hpp diff --git a/src/drivers/include/display.hpp b/src/drivers/include/drivers/display.hpp similarity index 96% rename from src/drivers/include/display.hpp rename to 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 similarity index 100% rename from src/drivers/include/display_init.hpp rename to 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 similarity index 100% rename from src/drivers/include/fatfs_audio_input.hpp rename to src/drivers/include/drivers/fatfs_audio_input.hpp diff --git a/src/drivers/include/gpios.hpp b/src/drivers/include/drivers/gpios.hpp similarity index 100% rename from src/drivers/include/gpios.hpp rename to src/drivers/include/drivers/gpios.hpp diff --git a/src/drivers/include/haptics.hpp b/src/drivers/include/drivers/haptics.hpp similarity index 100% rename from src/drivers/include/haptics.hpp rename to src/drivers/include/drivers/haptics.hpp diff --git a/src/drivers/include/i2c.hpp b/src/drivers/include/drivers/i2c.hpp similarity index 100% rename from src/drivers/include/i2c.hpp rename to src/drivers/include/drivers/i2c.hpp diff --git a/src/drivers/include/i2s_dac.hpp b/src/drivers/include/drivers/i2s_dac.hpp similarity index 93% rename from src/drivers/include/i2s_dac.hpp rename to 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 #include #include +#include #include #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& data) -> void; + auto WriteData(const std::span& 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 similarity index 98% rename from src/drivers/include/nvs.hpp rename to 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 similarity index 100% rename from src/drivers/include/samd.hpp rename to src/drivers/include/drivers/samd.hpp diff --git a/src/drivers/include/spi.hpp b/src/drivers/include/drivers/spi.hpp similarity index 100% rename from src/drivers/include/spi.hpp rename to src/drivers/include/drivers/spi.hpp diff --git a/src/drivers/include/spiffs.hpp b/src/drivers/include/drivers/spiffs.hpp similarity index 100% rename from src/drivers/include/spiffs.hpp rename to src/drivers/include/drivers/spiffs.hpp diff --git a/src/drivers/include/storage.hpp b/src/drivers/include/drivers/storage.hpp similarity index 98% rename from src/drivers/include/storage.hpp rename to 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 similarity index 97% rename from src/drivers/include/touchwheel.hpp rename to 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 similarity index 100% rename from src/drivers/include/wm8523.hpp rename to 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 -#include +#include "drivers/nvs.hpp" #include #include -#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 { +static auto nvs_get_string(nvs_handle_t nvs, + const char* key) -> std::optional { 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 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 #include @@ -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 #include @@ -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 @@ -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 #include @@ -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 #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 -# -# 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 +# +# 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 similarity index 100% rename from src/ui/font_fusion_10.c rename to src/graphics/font_fusion_10.c diff --git a/src/ui/font_fusion_12.c b/src/graphics/font_fusion_12.c similarity index 100% rename from src/ui/font_fusion_12.c rename to src/graphics/font_fusion_12.c diff --git a/src/ui/splash.c b/src/graphics/splash.c similarity index 100% rename from src/ui/splash.c rename to 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 -# -# 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 - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include -#include -#include -#include - -#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> inputs_; - std::vector> 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 - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "lvgl_input_driver.hpp" -#include - -#include -#include -#include - -#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(drv->user_data); - instance->read(data); -} - -static void feedback_cb(lv_indev_drv_t* drv, uint8_t event) { - LvglInputDriver* instance = - reinterpret_cast(drv->user_data); - instance->feedback(event); -} - -auto intToMode(int raw) -> std::optional { - 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(nvs.PrimaryInput()), - [&](const lua::LuaValue& val) { - if (!std::holds_alternative(val)) { - return false; - } - auto mode = intToMode(std::get(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 #include #include +#include #include #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 -# -# 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 #include +#include #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 { + auto Get() -> std::span { if (bytes_ == nullptr) { return {}; } return {bytes_, size}; } - auto Map(const HimemAlloc& alloc) -> cpp::span { + auto Map(const HimemAlloc& alloc) -> std::span { assert(bytes_ == nullptr); ESP_ERROR_CHECK(esp_himem_map(alloc.handle, range_handle, 0, 0, size, 0, reinterpret_cast(&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 -# -# 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 +# +# 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 similarity index 97% rename from src/app_console/app_console.cpp rename to 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 -#include -#include #include #include @@ -21,11 +19,7 @@ #include #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 similarity index 63% rename from src/app_console/include/app_console.hpp rename to 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 -#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 similarity index 100% rename from src/audio/README.md rename to 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 + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "audio/audio_decoder.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 sink) -> Decoder* { + Decoder* task = new Decoder(sink); + tasks::StartPersistent([=]() { task->Main(); }); + return task; +} + +auto Decoder::open(std::shared_ptr 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 processor) + : processor_(processor), next_stream_(xQueueCreate(1, sizeof(void*))) { + ESP_LOGI(kTag, "allocating codec buffer, %u KiB", kCodecBufferLength / 1024); + codec_buffer_ = { + reinterpret_cast(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 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 stream) -> void { + auto stub_track = std::make_shared(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{ + .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 + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include +#include + +#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) -> Decoder*; + + auto open(std::shared_ptr) -> void; + + Decoder(const Decoder&) = delete; + Decoder& operator=(const Decoder&) = delete; + + private: + Decoder(std::shared_ptr); + + auto Main() -> void; + + auto prepareDecode(std::shared_ptr) -> void; + auto continueDecode() -> bool; + auto finishDecode(bool cancel) -> void; + + std::shared_ptr processor_; + + // Struct used with the next_stream_ queue. + struct NextStream { + std::shared_ptr stream; + }; + QueueHandle_t next_stream_; + + std::shared_ptr stream_; + std::unique_ptr codec_; + std::shared_ptr track_; + + std::span codec_buffer_; +}; + +} // namespace audio diff --git a/src/audio/include/audio_events.hpp b/src/tangara/audio/audio_events.hpp similarity index 84% rename from src/audio/include/audio_events.hpp rename to 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 #include -#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 new_track; std::optional 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 track; +}; + +struct DecodingFailedToStart : tinyfsm::Event { + std::shared_ptr track; +}; + +struct DecodingCancelled : tinyfsm::Event { + std::shared_ptr track; +}; + +struct DecodingFinished : tinyfsm::Event { + std::shared_ptr track; +}; + struct StreamStarted : tinyfsm::Event { std::shared_ptr 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 similarity index 60% rename from src/audio/audio_fsm.cpp rename to 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 +#include "audio/audio_fsm.hpp" +#include #include #include #include -#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 AudioState::sServices; -std::shared_ptr AudioState::sFileSource; +std::shared_ptr AudioState::sStreamFactory; + std::unique_ptr AudioState::sDecoder; -std::shared_ptr AudioState::sSampleConverter; +std::shared_ptr AudioState::sSampleProcessor; + +std::shared_ptr AudioState::sOutput; std::shared_ptr AudioState::sI2SOutput; std::shared_ptr AudioState::sBtOutput; -std::shared_ptr 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 AudioState::sDrainFormat; -std::shared_ptr AudioState::sCurrentTrack; -uint64_t AudioState::sCurrentSamples; -bool AudioState::sCurrentTrackIsFromQueue; +StreamCues AudioState::sStreamCues; -std::shared_ptr AudioState::sNextTrack; -uint64_t AudioState::sNextTrackCueSamples; -bool AudioState::sNextTrackIsFromQueue; - -bool AudioState::sIsResampling; bool AudioState::sIsPaused = true; -auto AudioState::currentPositionSeconds() -> std::optional { - if (!sCurrentTrack || !sDrainFormat) { - return {}; +auto AudioState::emitPlaybackUpdate(bool paused) -> void { + std::optional 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(ev.new_track)) { ESP_LOGI(kTag, "playback finished, awaiting drain"); - sFileSource->SetPath(); - awaitEmptyDrainBuffer(); - sCurrentTrack.reset(); - sDrainFormat.reset(); - sCurrentSamples = 0; - sCurrentTrackIsFromQueue = false; - transit(); + 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([=]() { - std::optional path; + std::shared_ptr stream; if (std::holds_alternative(new_track)) { - auto db = sServices->database().lock(); - if (db) { - path = db->getTrackPath(std::get(new_track)); - } + stream = sStreamFactory->create(std::get(new_track), + seek_to); } else if (std::holds_alternative(new_track)) { - path = std::get(new_track); + stream = + sStreamFactory->create(std::get(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() && sCurrentTrack) { + if (!sIsPaused && is_in_state() && + sStreamCues.current().first) { transit(); } else if (sIsPaused && is_in_state()) { transit(); } } -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::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([=]() { + 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(), - }; - events::System().Dispatch(event); - events::Ui().Dispatch(event); - } + sStreamCues.addCue(ev.track, ev.cue_at_sample); - if (sCurrentTrack && !sIsPaused && !is_in_state()) { - ESP_LOGI(kTag, "ready to play!"); + if (!sIsPaused && !is_in_state()) { transit(); + } 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()) { - 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(); } @@ -477,7 +387,8 @@ void Standby::react(const system_fsm::KeyLockChanged& ev) { if (!ev.locking) { return; } - sServices->bg_worker().Dispatch([this]() { + auto current = sStreamCues.current(); + sServices->bg_worker().Dispatch([=]() { 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([]() { 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(); + } +} - 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(); + } } } // namespace states diff --git a/src/audio/include/audio_fsm.hpp b/src/tangara/audio/audio_fsm.hpp similarity index 62% rename from src/audio/include/audio_fsm.hpp rename to 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 #include -#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 { 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 { 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 sServices; - static std::shared_ptr sFileSource; + static std::shared_ptr sStreamFactory; static std::unique_ptr sDecoder; - static std::shared_ptr sSampleConverter; + static std::shared_ptr sSampleProcessor; static std::shared_ptr sI2SOutput; static std::shared_ptr sBtOutput; static std::shared_ptr sOutput; static StreamBufferHandle_t sDrainBuffer; - static std::shared_ptr sCurrentTrack; - static uint64_t sCurrentSamples; + static StreamCues sStreamCues; static std::optional sDrainFormat; - static bool sCurrentTrackIsFromQueue; - - static std::shared_ptr sNextTrack; - static uint64_t sNextTrackCueSamples; - static bool sNextTrackIsFromQueue; - static bool sIsResampling; static bool sIsPaused; - - auto currentPositionSeconds() -> std::optional; }; 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 similarity index 97% rename from src/audio/include/audio_sink.hpp rename to 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 similarity index 82% rename from src/audio/audio_source.cpp rename to 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 t, std::unique_ptr 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 { return tags_; } -auto TaggedStream::Read(cpp::span dest) -> ssize_t { +auto TaggedStream::Read(std::span dest) -> ssize_t { return wrapped_->Read(dest); } diff --git a/src/audio/include/audio_source.hpp b/src/tangara/audio/audio_source.hpp similarity index 89% rename from src/audio/include/audio_source.hpp rename to 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 #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, std::unique_ptr wrapped, std::string path, - uint32_t offset = 0 - ); + uint32_t offset = 0); auto tags() -> std::shared_ptr; - auto Read(cpp::span dest) -> ssize_t override; + auto Read(std::span 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 similarity index 92% rename from src/audio/bt_audio_output.cpp rename to 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 #include @@ -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 similarity index 88% rename from src/audio/include/bt_audio_output.hpp rename to 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 similarity index 87% rename from src/audio/fatfs_source.cpp rename to 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 #include @@ -12,13 +12,13 @@ #include #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 dest) -> ssize_t { +auto FatfsSource::Read(std::span 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 similarity index 90% rename from src/audio/include/fatfs_source.hpp rename to 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 file); ~FatfsSource(); - auto Read(cpp::span dest) -> ssize_t override; + auto Read(std::span 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 + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "audio/fatfs_stream_factory.hpp" + +#include +#include +#include + +#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 { + 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 { + 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 file = std::make_unique(); + 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( + tags, std::make_unique(stream_type.value(), std::move(file)), + path, offset); +} + +auto FatfsStreamFactory::ContainerToStreamType(database::Container enc) + -> std::optional { + 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 + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#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; + auto create(std::string, uint32_t offset = 0) + -> std::shared_ptr; + + FatfsStreamFactory(const FatfsStreamFactory&) = delete; + FatfsStreamFactory& operator=(const FatfsStreamFactory&) = delete; + + private: + auto ContainerToStreamType(database::Container) + -> std::optional; + + system_fsm::ServiceLocator& services_; +}; + +} // namespace audio diff --git a/src/audio/i2s_audio_output.cpp b/src/tangara/audio/i2s_audio_output.cpp similarity index 92% rename from src/audio/i2s_audio_output.cpp rename to 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 #include @@ -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(drivers::wm8523::kLineLevelReferenceVolume) - 2); + SetVolume(val * 4 + + static_cast(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 similarity index 91% rename from src/audio/include/i2s_audio_output.hpp rename to 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 #include -#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 similarity index 67% rename from src/audio/audio_converter.cpp rename to 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 #include #include #include +#include -#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(heap_caps_calloc( kSampleBufferLength, sizeof(sample::Sample), MALLOC_CAP_DMA)), @@ -55,47 +57,52 @@ SampleConverter::SampleConverter() tasks::StartPersistent([&]() { Main(); }); } -SampleConverter::~SampleConverter() { +SampleProcessor::~SampleProcessor() { vQueueDelete(commands_); vStreamBufferDelete(source_); } -auto SampleConverter::SetOutput(std::shared_ptr 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 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 track) -> void { +auto SampleProcessor::beginStream(std::shared_ptr track) -> void { Args args{ .track = new std::shared_ptr(track), .samples_available = 0, .is_end_of_stream = false, + .clear_buffers = false, }; xQueueSend(commands_, &args, portMAX_DELAY); } -auto SampleConverter::continueStream(cpp::span input) -> void { +auto SampleProcessor::continueStream(std::span 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 track) +auto SampleProcessor::handleBeginStream(std::shared_ptr 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 input) -> size_t { +auto SampleProcessor::handleSamples(std::span 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 input) -> size_t { size_t samples_used = 0; while (samples_used < input.size()) { - cpp::span output_source; + std::span 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 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 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 samples) -> void { + auto data = std::as_bytes(samples); + xStreamBufferSend(sink_, data.data(), data.size(), portMAX_DELAY); - xStreamBufferSend(sink_->stream(), - reinterpret_cast(samples.data()), - samples.size_bytes(), portMAX_DELAY); + uint32_t samples_before_overflow = + std::numeric_limits::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 similarity index 60% rename from src/audio/include/audio_converter.hpp rename to 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 #include -#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) -> void; auto beginStream(std::shared_ptr) -> void; - auto continueStream(cpp::span) -> void; - auto endStream() -> void; + auto continueStream(std::span) -> void; + auto endStream(bool cancelled) -> void; private: auto Main() -> void; auto handleBeginStream(std::shared_ptr) -> void; auto handleContinueStream(size_t samples_available) -> void; - auto handleEndStream() -> void; + auto handleEndStream(bool cancel) -> void; - auto handleSamples(cpp::span) -> size_t; + auto handleSamples(std::span) -> size_t; - auto sendToSink(cpp::span) -> void; + auto sendToSink(std::span) -> void; struct Args { std::shared_ptr* track; size_t samples_available; bool is_end_of_stream; + bool clear_buffers; }; QueueHandle_t commands_; std::unique_ptr resampler_; StreamBufferHandle_t source_; - cpp::span input_buffer_; - cpp::span input_buffer_as_bytes_; + StreamBufferHandle_t sink_; - cpp::span resampled_buffer_; + std::span input_buffer_; + std::span input_buffer_as_bytes_; - std::shared_ptr sink_; + std::span resampled_buffer_; + + std::shared_ptr 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 similarity index 95% rename from src/audio/readahead_source.cpp rename to 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 #include @@ -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 dest) -> ssize_t { +auto ReadaheadSource::Read(std::span 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 similarity index 92% rename from src/audio/include/readahead_source.hpp rename to 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); ~ReadaheadSource(); - auto Read(cpp::span dest) -> ssize_t override; + auto Read(std::span dest) -> ssize_t override; auto CanSeek() -> bool override; diff --git a/src/audio/resample.cpp b/src/tangara/audio/resample.cpp similarity index 90% rename from src/audio/resample.cpp rename to 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 #include @@ -38,8 +38,8 @@ Resampler::~Resampler() { speex_resampler_destroy(resampler_); } -auto Resampler::Process(cpp::span input, - cpp::span output, +auto Resampler::Process(std::span input, + std::span output, bool end_of_data) -> std::pair { 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 similarity index 79% rename from src/audio/include/resample.hpp rename to 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 +#include #include -#include "span.hpp" #include "speex/speex_resampler.h" #include "sample.hpp" @@ -24,8 +24,8 @@ class Resampler { ~Resampler(); - auto Process(cpp::span input, - cpp::span output, + auto Process(std::span input, + std::span output, bool end_of_data) -> std::pair; 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 + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "audio/stream_cues.hpp" + +#include +#include + +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 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, 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 + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include +#include +#include +#include + +#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, uint32_t>; + + auto hasStream() -> bool; + + auto addCue(std::shared_ptr, uint32_t start_at) -> void; + + private: + uint32_t now_; + + struct Cue { + std::shared_ptr track; + uint32_t start_at; + }; + + std::optional current_; + std::deque upcoming_; +}; + +} // namespace audio diff --git a/src/audio/test/CMakeLists.txt b/src/tangara/audio/test/CMakeLists.txt similarity index 100% rename from src/audio/test/CMakeLists.txt rename to src/tangara/audio/test/CMakeLists.txt diff --git a/src/audio/track_queue.cpp b/src/tangara/audio/track_queue.cpp similarity index 98% rename from src/audio/track_queue.cpp rename to 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 #include @@ -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 similarity index 97% rename from src/audio/include/track_queue.hpp rename to 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 #include -#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 similarity index 95% rename from src/battery/battery.cpp rename to 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 -#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 similarity index 94% rename from src/battery/include/battery.hpp rename to 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 similarity index 98% rename from src/database/database.cpp rename to 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 #include @@ -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 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 similarity index 97% rename from src/database/include/database.hpp rename to 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 similarity index 100% rename from src/database/include/db_events.hpp rename to src/tangara/database/db_events.hpp diff --git a/src/database/env_esp.cpp b/src/tangara/database/env_esp.cpp similarity index 99% rename from src/database/env_esp.cpp rename to 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 #include @@ -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 similarity index 100% rename from src/database/include/env_esp.hpp rename to src/tangara/database/env_esp.hpp diff --git a/src/database/file_gatherer.cpp b/src/tangara/database/file_gatherer.cpp similarity index 96% rename from src/database/file_gatherer.cpp rename to 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 #include @@ -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 similarity index 58% rename from src/database/include/file_gatherer.hpp rename to 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 cb) - -> void = 0; + std::function cb) -> void = 0; }; class FileGathererImpl : public IFileGatherer { public: - virtual auto FindFiles( - const std::string& root, - std::function cb) - -> void override; + virtual auto FindFiles(const std::string& root, + std::function + cb) -> void override; }; } // namespace database diff --git a/src/database/include/future_fetcher.hpp b/src/tangara/database/future_fetcher.hpp similarity index 97% rename from src/database/include/future_fetcher.hpp rename to 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 #include -#include "database.hpp" +#include "database/database.hpp" namespace database { diff --git a/src/database/index.cpp b/src/tangara/database/index.cpp similarity index 90% rename from src/database/index.cpp rename to 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 #include @@ -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 components) -> void; + std::span components) -> void; auto handleItem(const IndexKey::Header& header, std::variant item, - cpp::span components) -> void; + std::span components) -> void; auto missing_value(Tag tag) -> TagValue { switch (tag) { @@ -111,7 +111,7 @@ auto Indexer::index() -> std::vector> { } auto Indexer::handleLevel(const IndexKey::Header& header, - cpp::span components) -> void { + std::span components) -> void { Tag component = components.front(); TagValue value = track_.tags().get(component); if (std::holds_alternative(value)) { @@ -129,7 +129,7 @@ auto Indexer::handleLevel(const IndexKey::Header& header, } else if constexpr (std::is_same_v) { handleItem(header, arg, components); } else if constexpr (std::is_same_v< - T, cpp::span>) { + T, std::span>) { 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 item, - cpp::span components) -> void { + std::span 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> { +auto Index(locale::ICollator& c, + const IndexInfo& i, + const Track& t) -> std::vector> { Indexer indexer{c, t, i}; return indexer.index(); } diff --git a/src/database/include/index.hpp b/src/tangara/database/index.hpp similarity index 92% rename from src/database/include/index.hpp rename to 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 track; }; -auto Index(locale::ICollator&, const IndexInfo&, const Track&) - -> std::vector>; +auto Index(locale::ICollator&, + const IndexInfo&, + const Track&) -> std::vector>; auto ExpandHeader(const IndexKey::Header&, const std::optional&) -> IndexKey::Header; diff --git a/src/database/records.cpp b/src/tangara/database/records.cpp similarity index 98% rename from src/database/records.cpp rename to 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 #include @@ -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 bytes) -> std::optional { +auto BytesToTrackId(std::span bytes) -> std::optional { auto [res, unused, err] = cppbor::parse( reinterpret_cast(bytes.data()), bytes.size()); if (!res || res->type() != cppbor::UINT) { diff --git a/src/database/include/records.hpp b/src/tangara/database/records.hpp similarity index 94% rename from src/database/include/records.hpp rename to 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 bytes) -> std::optional; +auto BytesToTrackId(std::span bytes) -> std::optional; } // namespace database diff --git a/src/database/tag_parser.cpp b/src/tangara/database/tag_parser.cpp similarity index 98% rename from src/database/tag_parser.cpp rename to 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 #include #include #include +#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 similarity index 97% rename from src/database/include/tag_parser.hpp rename to 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 +#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 similarity index 100% rename from src/database/test/CMakeLists.txt rename to src/tangara/database/test/CMakeLists.txt diff --git a/src/database/test/test_database.cpp b/src/tangara/database/test/test_database.cpp similarity index 94% rename from src/database/test/test_database.cpp rename to 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 #include @@ -13,14 +13,14 @@ #include #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 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 similarity index 99% rename from src/database/test/test_records.cpp rename to 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 #include diff --git a/src/database/track.cpp b/src/tangara/database/track.cpp similarity index 96% rename from src/database/track.cpp rename to 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 #include #include +#include #include #include @@ -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) { return komihash(&arg, sizeof(arg), 0); } else if constexpr (std::is_same_v< - T, cpp::span>) { + T, std::span>) { 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) { return std::to_string(arg); } else if constexpr (std::is_same_v< - T, cpp::span>) { + T, std::span>) { 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 { +auto TrackTags::genres() const -> std::span { return genres_; } diff --git a/src/database/include/track.hpp b/src/tangara/database/track.hpp similarity index 96% rename from src/database/include/track.hpp rename to 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 -#include +#include #include #include #include +#include #include #include #include @@ -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::span>; 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; + auto genres() const -> std::span; auto genres(const std::string_view) -> void; /* diff --git a/src/dev_console/console.cpp b/src/tangara/dev_console/console.cpp similarity index 98% rename from src/dev_console/console.cpp rename to 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 #include diff --git a/src/dev_console/include/console.hpp b/src/tangara/dev_console/console.hpp similarity index 100% rename from src/dev_console/include/console.hpp rename to src/tangara/dev_console/console.hpp diff --git a/src/events/event_queue.cpp b/src/tangara/events/event_queue.cpp similarity index 86% rename from src/events/event_queue.cpp rename to 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 similarity index 95% rename from src/events/include/event_queue.hpp rename to 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 #include -#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 similarity index 84% rename from src/input/device_factory.cpp rename to 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 -#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 similarity index 82% rename from src/input/include/device_factory.hpp rename to 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 #include -#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 similarity index 100% rename from src/input/include/feedback_device.hpp rename to src/tangara/input/feedback_device.hpp diff --git a/src/input/feedback_haptics.cpp b/src/tangara/input/feedback_haptics.cpp similarity index 90% rename from src/input/feedback_haptics.cpp rename to 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 @@ -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 similarity index 84% rename from src/input/include/feedback_haptics.hpp rename to 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 -#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 similarity index 81% rename from src/input/include/input_device.hpp rename to 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 #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 { return {}; } + virtual auto triggers() -> std::vector> { + return {}; + } }; } // namespace input diff --git a/src/input/input_hook.cpp b/src/tangara/input/input_hook.cpp similarity index 80% rename from src/input/input_hook.cpp rename to 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 #include + #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 { TriggerHooks::TriggerHooks(std::string name, std::optional click, + std::optional double_click, std::optional long_press, std::optional 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> { + 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 similarity index 73% rename from src/input/include/input_hook.hpp rename to 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; + // Not copyable or movable. + Hook(const Hook&) = delete; + Hook& operator=(const Hook&) = delete; + private: std::string name_; std::optional default_; @@ -41,9 +45,10 @@ class Hook { class TriggerHooks { public: TriggerHooks(std::string name, std::optional cb) - : TriggerHooks(name, cb, cb, cb) {} + : TriggerHooks(name, cb, cb, cb, cb) {} TriggerHooks(std::string name, std::optional click, + std::optional double_click, std::optional long_press, std::optional repeat); @@ -51,13 +56,20 @@ class TriggerHooks { auto override(Trigger::State, std::optional) -> void; auto name() const -> const std::string&; - auto pushHooks(lua_State*) -> void; + auto hooks() -> std::vector>; + + 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 similarity index 95% rename from src/input/input_hook_actions.cpp rename to 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 #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 similarity index 94% rename from src/input/include/input_hook_actions.hpp rename to 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 similarity index 58% rename from src/input/input_nav_buttons.cpp rename to 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 { +auto NavButtons::triggers() + -> std::vector> { return {up_, down_}; } diff --git a/src/input/include/input_nav_buttons.hpp b/src/tangara/input/input_nav_buttons.hpp similarity index 63% rename from src/input/include/input_nav_buttons.hpp rename to 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 -#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 override; + auto triggers() -> std::vector> override; private: drivers::IGpios& gpios_; diff --git a/src/input/input_touch_dpad.cpp b/src/tangara/input/input_touch_dpad.cpp similarity index 65% rename from src/input/input_touch_dpad.cpp rename to 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 #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 { +auto TouchDPad::triggers() + -> std::vector> { return {centre_, up_, right_, down_, left_}; } diff --git a/src/input/include/input_touch_dpad.hpp b/src/tangara/input/input_touch_dpad.hpp similarity index 68% rename from src/input/include/input_touch_dpad.hpp rename to 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 override; + auto triggers() -> std::vector> override; private: drivers::TouchWheel& wheel_; diff --git a/src/input/input_touch_wheel.cpp b/src/tangara/input/input_touch_wheel.cpp similarity index 64% rename from src/input/input_touch_wheel.cpp rename to 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 #include #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 { +auto TouchWheel::triggers() + -> std::vector> { return {centre_, up_, right_, down_, left_}; } diff --git a/src/input/include/input_touch_wheel.hpp b/src/tangara/input/input_touch_wheel.hpp similarity index 75% rename from src/input/include/input_touch_wheel.hpp rename to 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 override; + auto triggers() -> std::vector> override; auto sensitivity() -> lua::Property&; diff --git a/src/input/input_trigger.cpp b/src/tangara/input/input_trigger.cpp similarity index 53% rename from src/input/input_trigger.cpp rename to 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 +#include "input/input_trigger.hpp" #include -#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 similarity index 77% rename from src/input/include/input_trigger.hpp rename to 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 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 similarity index 71% rename from src/input/input_volume_buttons.cpp rename to 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 { +auto VolumeButtons::triggers() + -> std::vector> { return {up_, down_}; } diff --git a/src/input/include/input_volume_buttons.hpp b/src/tangara/input/input_volume_buttons.hpp similarity index 66% rename from src/input/include/input_volume_buttons.hpp rename to 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 -#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 override; + auto triggers() -> std::vector> 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 + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "input/lvgl_input_driver.hpp" + +#include +#include +#include + +#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(drv->user_data); + instance->read(data); +} + +static void feedback_cb(lv_indev_drv_t* drv, uint8_t event) { + LvglInputDriver* instance = + reinterpret_cast(drv->user_data); + instance->feedback(event); +} + +auto intToMode(int raw) -> std::optional { + 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(nvs.PrimaryInput()), + [&](const lua::LuaValue& val) { + if (!std::holds_alternative(val)) { + return false; + } + auto mode = intToMode(std::get(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( + 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( + 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 + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include +#include +#include +#include + +#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> inputs_; + std::vector> 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 hooks_; + }; + + /* A hook override implemented as a lua callback */ + struct LuaOverride { + lua_State* L; + int ref; + }; + + std::map 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 similarity index 84% rename from src/lua/bridge.cpp rename to 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 +#include "lua/bridge.hpp" +#include #include #include -#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 similarity index 85% rename from src/lua/include/bridge.hpp rename to 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 #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>>&) - -> void; + std::vector>>&) -> 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 similarity index 100% rename from src/lua/file_iterator.cpp rename to src/tangara/lua/file_iterator.cpp diff --git a/src/lua/include/file_iterator.hpp b/src/tangara/lua/file_iterator.hpp similarity index 100% rename from src/lua/include/file_iterator.hpp rename to src/tangara/lua/file_iterator.hpp diff --git a/src/lua/lua_controls.cpp b/src/tangara/lua/lua_controls.cpp similarity index 93% rename from src/lua/lua_controls.cpp rename to 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 #include @@ -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 similarity index 100% rename from src/lua/include/lua_controls.hpp rename to src/tangara/lua/lua_controls.hpp diff --git a/src/lua/lua_database.cpp b/src/tangara/lua/lua_database.cpp similarity index 90% rename from src/lua/lua_database.cpp rename to 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 #include #include #include -#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( 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( diff --git a/src/lua/include/lua_database.hpp b/src/tangara/lua/lua_database.hpp similarity index 90% rename from src/lua/include/lua_database.hpp rename to 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 similarity index 100% rename from src/lua/lua_filesystem.cpp rename to src/tangara/lua/lua_filesystem.cpp diff --git a/src/lua/include/lua_filesystem.hpp b/src/tangara/lua/lua_filesystem.hpp similarity index 100% rename from src/lua/include/lua_filesystem.hpp rename to src/tangara/lua/lua_filesystem.hpp diff --git a/src/lua/lua_queue.cpp b/src/tangara/lua/lua_queue.cpp similarity index 83% rename from src/lua/lua_queue.cpp rename to 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 #include @@ -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 similarity index 100% rename from src/lua/include/lua_queue.hpp rename to src/tangara/lua/lua_queue.hpp diff --git a/src/lua/include/lua_registry.hpp b/src/tangara/lua/lua_registry.hpp similarity index 91% rename from src/lua/include/lua_registry.hpp rename to 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 similarity index 81% rename from src/lua/lua_screen.cpp rename to 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 #include @@ -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 similarity index 100% rename from src/lua/include/lua_screen.hpp rename to src/tangara/lua/lua_screen.hpp diff --git a/src/lua/lua_theme.cpp b/src/tangara/lua/lua_theme.cpp similarity index 80% rename from src/lua/lua_theme.cpp rename to 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 -#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 similarity index 100% rename from src/lua/include/lua_theme.hpp rename to src/tangara/lua/lua_theme.hpp diff --git a/src/lua/lua_thread.cpp b/src/tangara/lua/lua_thread.cpp similarity index 94% rename from src/lua/lua_thread.cpp rename to 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 #include @@ -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(ud); return instance->alloc(ptr, osize, nsize); } diff --git a/src/lua/include/lua_thread.hpp b/src/tangara/lua/lua_thread.hpp similarity index 95% rename from src/lua/include/lua_thread.hpp rename to 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 similarity index 94% rename from src/lua/lua_version.cpp rename to 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 -#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 similarity index 100% rename from src/lua/include/lua_version.hpp rename to src/tangara/lua/lua_version.hpp diff --git a/src/lua/property.cpp b/src/tangara/lua/property.cpp similarity index 98% rename from src/lua/property.cpp rename to 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 #include @@ -14,15 +14,15 @@ #include #include -#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) { lua_pushlstring(L, arg.data(), arg.size()); } else if constexpr (std::is_same_v< - T, cpp::span>) { + T, std::span>) { 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 similarity index 96% rename from src/lua/include/property.hpp rename to 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 #include -#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 similarity index 91% rename from src/lua/registry.cpp rename to 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 #include @@ -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 similarity index 84% rename from src/system_fsm/booting.cpp rename to 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 #include @@ -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 similarity index 89% rename from src/system_fsm/idle.cpp rename to 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 similarity index 74% rename from src/system_fsm/running.cpp rename to 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_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 similarity index 65% rename from src/system_fsm/service_locator.cpp rename to 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 -#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 similarity index 90% rename from src/system_fsm/include/service_locator.hpp rename to 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 -#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 similarity index 85% rename from src/system_fsm/include/system_events.hpp rename to 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 -#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 similarity index 90% rename from src/system_fsm/system_fsm.cpp rename to 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 similarity index 83% rename from src/system_fsm/include/system_fsm.hpp rename to 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 -#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 { 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 similarity index 100% rename from src/ui/include/fonts.hpp rename to src/tangara/ui/fonts.hpp diff --git a/src/ui/lvgl_task.cpp b/src/tangara/ui/lvgl_task.cpp similarity index 89% rename from src/ui/lvgl_task.cpp rename to 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 #include @@ -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 similarity index 79% rename from src/ui/include/lvgl_task.hpp rename to 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 similarity index 86% rename from src/ui/modal.cpp rename to 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 similarity index 95% rename from src/ui/include/modal.hpp rename to 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 similarity index 98% rename from src/ui/screen.cpp rename to 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 diff --git a/src/ui/include/screen.hpp b/src/tangara/ui/screen.hpp similarity index 100% rename from src/ui/include/screen.hpp rename to src/tangara/ui/screen.hpp diff --git a/src/ui/screen_lua.cpp b/src/tangara/ui/screen_lua.cpp similarity index 94% rename from src/ui/screen_lua.cpp rename to 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 similarity index 93% rename from src/ui/include/screen_lua.hpp rename to 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 similarity index 95% rename from src/ui/screen_splash.cpp rename to 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 similarity index 94% rename from src/ui/include/screen_splash.hpp rename to 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 similarity index 83% rename from src/ui/themes.cpp rename to 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>{}); 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 similarity index 99% rename from src/ui/include/themes.hpp rename to 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 #include +#include #include #include "lvgl.h" @@ -32,7 +32,6 @@ class Theme { Theme(); std::map>> 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 similarity index 86% rename from src/ui/include/ui_events.hpp rename to 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 -#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 similarity index 91% rename from src/ui/ui_fsm.cpp rename to 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 #include #include -#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(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(); } @@ -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 similarity index 81% rename from src/ui/include/ui_fsm.hpp rename to 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 #include -#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 { 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 { 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 # # 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() -> std::pmr::string { } template -auto AllocateStack() -> cpp::span; +auto AllocateStack() -> std::span; // 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() -> cpp::span { +auto AllocateStack() -> std::span { constexpr std::size_t size = 20 * 1024; static StackType_t sStack[size]; return {sStack, size}; @@ -46,14 +46,14 @@ auto AllocateStack() -> cpp::span { // LVGL requires only a relatively small stack. Lua's stack is allocated // separately. template <> -auto AllocateStack() -> cpp::span { +auto AllocateStack() -> std::span { 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() -> cpp::span { +auto AllocateStack() -> std::span { constexpr std::size_t size = 4 * 1024; static StackType_t sStack[size]; return {sStack, size}; @@ -63,7 +63,7 @@ auto AllocateStack() -> cpp::span { // 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() -> cpp::span { +auto AllocateStack() -> std::span { std::size_t size = 64 * 1024; return {static_cast(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 #include #include +#include #include #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 auto Name() -> std::pmr::string; template -auto AllocateStack() -> cpp::span; +auto AllocateStack() -> std::span; template auto Priority() -> UBaseType_t; @@ -56,7 +56,7 @@ template auto StartPersistent(const std::function& fn) -> void { StaticTask_t* task_buffer = static_cast(heap_caps_malloc( sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)); - cpp::span stack = AllocateStack(); + std::span stack = AllocateStack(); xTaskCreateStatic(&PersistentMain, Name().c_str(), stack.size(), new std::function(fn), Priority(), stack.data(), task_buffer); @@ -67,7 +67,7 @@ auto StartPersistent(BaseType_t core, const std::function& fn) -> void { StaticTask_t* task_buffer = static_cast(heap_caps_malloc( sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)); - cpp::span stack = AllocateStack(); + std::span stack = AllocateStack(); xTaskCreateStaticPinnedToCore(&PersistentMain, Name().c_str(), stack.size(), new std::function(fn), Priority(), 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 -# -# 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 - * - * 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 #include - +#include #include -#include "span.hpp" namespace util { -inline std::string format_hex_string(cpp::span data) { +inline std::string format_hex_string(std::span 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; } diff --git a/tools/cmake/common.cmake b/tools/cmake/common.cmake index 896dabf3..7087e896 100644 --- a/tools/cmake/common.cmake +++ b/tools/cmake/common.cmake @@ -5,7 +5,7 @@ # For more information about build system see # https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html -set(PROJECT_VER "0.8.3") +set(PROJECT_VER "0.9.0") # esp-idf sets the C++ standard weird. Set cmake vars to match. set(CMAKE_CXX_STANDARD 23) @@ -33,7 +33,6 @@ list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/drflac") list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/ogg") list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/opusfile") list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/result") -list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/span") list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/speexdsp") list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/tinyfsm") list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/tremor") diff --git a/tools/collate/Generic.LC_COLLATE b/tools/collate/Generic.LC_COLLATE new file mode 100644 index 00000000..aa21717a Binary files /dev/null and b/tools/collate/Generic.LC_COLLATE differ diff --git a/tools/collate/README.md b/tools/collate/README.md new file mode 100644 index 00000000..268db5fd --- /dev/null +++ b/tools/collate/README.md @@ -0,0 +1,24 @@ +This tool, which only works on Linux, uses your system's locale data to +generate a collator partition for Tangara. This partition is uses to sort +database information (album names, etc.) in a way that makes sense to humans. + +Because this script isn't portable, `Generic.LC_COLLATE` is included as a +pregenerated collator partition that should work well for most people's +music libraries. + +## Partition format + +The collator partition has 3MiB reserved for it in the partition table, which +is enough to hold the compiled collation data for ISO14651 character sorting, +plus a small amount of free space. + +The first 8 bytes of the partition are reserved for a partition name; usually +the name of the locale that the collation data was compiled for, or 'Generic' +for ISO14651 data. + +Following the 8 byte name, is a complete LC_COLLATE file, as generate by GNU +libc's `localedef` utility. Debian's version 2.37 of this utility is known to +work. + +FIXME: We should vendor this version of the tool, so that our format remains +stable across glibc changes. diff --git a/tools/collate/mkcollator.sh b/tools/collate/mkcollator.sh new file mode 100755 index 00000000..e5962429 --- /dev/null +++ b/tools/collate/mkcollator.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -eu + +if [ -z "${2:-}" ]; then + name="Generic" + locale="iso14651_t1" +else + name="$1" + locale="$2" +fi + +working_dir=$(mktemp -d) + +echo -n "$name" | dd iflag=fullblock bs=8 count=1 conv=sync of="$working_dir/name" +localedef -f UTF-8 -i "$locale" --no-archive "$working_dir" || true +cat "$working_dir/name" "$working_dir/LC_COLLATE" > "$name.LC_COLLATE"