Skip to content

Commit

Permalink
Presets (#94)
Browse files Browse the repository at this point in the history
* Refactor parameter state management

* Refactor JSON out of parameter logic

* Update linter

* Update Flutter for CI

* Implement preset serialisation using JSON

* Implement protobuf serialisation of presets

* Integrate new messages with firmware

* Disable WiFi power save

* Implement NVS iteration

* Implement preset storage

* Use freezed for parameter messages

* Improve firmware memory usage

* Tweak stack sizes
  • Loading branch information
Barabas5532 authored Dec 15, 2023
1 parent b180b27 commit b98fe17
Show file tree
Hide file tree
Showing 124 changed files with 8,401 additions and 633 deletions.
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,9 @@
[submodule "test/support/freertos/FreeRTOS-Kernel"]
path = test/support/freertos/FreeRTOS-Kernel
url = https://github.com/Barabas5532/FreeRTOS-Kernel.git
[submodule "test/support/protobuf-c/protobuf-c"]
path = test/support/protobuf-c/protobuf-c
url = https://github.com/protobuf-c/protobuf-c.git
[submodule "thirdparty/esp-idf-components/cppcodec"]
path = thirdparty/esp-idf-components/cppcodec
url = https://github.com/ShrapnelDSP/cppcodec.git
6 changes: 6 additions & 0 deletions .licenserc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ header:
- '**/thirdparty/**'
- '**/*.g.dart'
- '**/*.mocks.dart'
- '**/*.pb.dart'
- '**/*.pbenum.dart'
- '**/*.pbjson.dart'
- '**/*.pbserver.dart'
- '**/*.pb-c.c'
- '**/*.pb-c.h'
- 'frontend/.dart_tool/**'
- 'frontend/linux/flutter/**'
# Test support files are usually derivatives of third-party library code,
Expand Down
4 changes: 4 additions & 0 deletions firmware/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ if (ESP_PLATFORM)
add_subdirectory(../effects/wah wah)
add_subdirectory(../thirdparty/esp-idf-components/etl etl)
add_subdirectory(../thirdparty/esp-idf-components/rapidjson rapidjson)
add_subdirectory(../thirdparty/esp-idf-components/cppcodec cppcodec)
add_subdirectory(components/audio)
add_subdirectory(components/audio_events)
add_subdirectory(components/audio_param)
Expand All @@ -40,7 +41,10 @@ if (ESP_PLATFORM)
add_subdirectory(components/os)
add_subdirectory(components/pcm3060)
add_subdirectory(components/persistence)
add_subdirectory(components/presets)
add_subdirectory(components/presets_storage)
add_subdirectory(components/server)
add_subdirectory(components/uuid)
add_subdirectory(components/wifi)

return()
Expand Down
5 changes: 4 additions & 1 deletion firmware/components/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ add_subdirectory(messages)
add_subdirectory(midi_mapping)
add_subdirectory(midi_protocol)
add_subdirectory(os)
add_subdirectory(persistence)
add_subdirectory(persistence)
add_subdirectory(presets)
add_subdirectory(presets_storage)
add_subdirectory(uuid)
2 changes: 1 addition & 1 deletion firmware/components/audio_param/include/audio_param.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class AudioParameters final : public etl::observable<ParameterObserver, MAX_OBSE
*
* \return 0 on success
*/
int update(const id_t &param, float value)
[[nodiscard]] int update(const id_t &param, float value)
{
auto element = parameters.find(param);

Expand Down
3 changes: 2 additions & 1 deletion firmware/components/messages/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ target_link_libraries(messages
INTERFACE
shrapnel::audio_events
shrapnel::cmd_handling
shrapnel::midi_mapping)
shrapnel::midi_mapping
shrapnel::presets)
11 changes: 8 additions & 3 deletions firmware/components/messages/include/messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,15 @@
#include <cmd_handling_api.h>
#include <midi_mapping_api.h>
#include <optional>
#include <presets_api.h>
#include <selected_preset_api.h>
#include <variant>

using ApiMessage = std::variant<shrapnel::parameters::ApiMessage,
shrapnel::midi::MappingApiMessage,
shrapnel::events::ApiMessage>;
using ApiMessage =
std::variant<shrapnel::parameters::ApiMessage,
shrapnel::midi::MappingApiMessage,
shrapnel::events::ApiMessage,
shrapnel::selected_preset::SelectedPresetApiMessage,
shrapnel::presets::PresetsApiMessage>;
using FileDescriptor = std::optional<int>;
using AppMessage = std::pair<ApiMessage, FileDescriptor>;
1 change: 1 addition & 0 deletions firmware/components/midi_mapping/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ target_link_libraries(midi_mapping
shrapnel::audio_param
shrapnel::etl
shrapnel::midi_protocol
shrapnel::uuid
shrapnel::rapidjson
PRIVATE
shrapnel::compiler_warning_flags)
Expand Down
3 changes: 2 additions & 1 deletion firmware/components/midi_mapping/include/midi_mapping.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,13 @@

#include "audio_param.h"
#include "midi_protocol.h"
#include "uuid.h"

namespace shrapnel {
namespace midi {

struct Mapping {
using id_t = std::array<uint8_t, 16>;
using id_t = uuid::uuid_t;

enum class Mode
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,6 @@ rapidjson::Value to_json(rapidjson::Document &document, const Message &object);
template<>
rapidjson::Value to_json(rapidjson::Document &document, const Mapping &object);

template<>
rapidjson::Value to_json(rapidjson::Document &document, const Mapping::id_t &object);

template<>
rapidjson::Value to_json(rapidjson::Document &document, const std::pair<Mapping::id_t, Mapping> &object);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ std::optional<MapType> from_json(const rapidjson::Value &json) {
return std::nullopt;
}

auto id = from_json<Mapping::id_t>(entry.name);
auto id = uuid::from_json<Mapping::id_t>(entry.name);
if(!id.has_value())
{
ESP_LOGE(TAG, "failed to get uuid");
Expand Down
21 changes: 2 additions & 19 deletions firmware/components/midi_mapping/src/midi_mapping_json_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,31 +114,14 @@ rapidjson::Value to_json(rapidjson::Document &document, const Mapping &object)
return json;
}

template <>
rapidjson::Value to_json(rapidjson::Document &document,
const Mapping::id_t &object) {
char uuid[37];
sprintf(uuid,
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
"%02x%02x%02x%02x%02x%02x",
object[0], object[1], object[2], object[3], object[4], object[5],
object[6], object[7], object[8], object[9], object[10], object[11],
object[12], object[13], object[14], object[15]);

rapidjson::Value out;
out.SetString(uuid, 36, document.GetAllocator());

return out;
}

template<>
rapidjson::Value to_json(rapidjson::Document &document, const std::pair<Mapping::id_t, Mapping> &object)
{
rapidjson::Value json;
json.SetObject();

rapidjson::Value mapping = to_json(document, object.second);
auto id = to_json(document, object.first);
auto id = uuid::to_json(document, object.first);
json.AddMember(id, mapping, document.GetAllocator());
return json;
}
Expand Down Expand Up @@ -209,7 +192,7 @@ rapidjson::Value to_json(rapidjson::Document &document, const etl::imap<Mapping:
for(const auto &entry : object)
{
rapidjson::Value mapping = to_json(document, entry.second);
auto id = to_json(document, entry.first);
auto id = uuid::to_json(document, entry.first);
json.AddMember(id, mapping, document.GetAllocator());
}

Expand Down
76 changes: 2 additions & 74 deletions firmware/components/midi_mapping/src/midi_mapping_json_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
namespace shrapnel {
namespace midi {

static int parse_uuid(Mapping::id_t &uuid, const char *string);

template<>
std::optional<GetRequest> from_json(const rapidjson::Value &)
{
Expand Down Expand Up @@ -117,7 +115,7 @@ std::optional<std::pair<Mapping::id_t, Mapping>> from_json(const rapidjson::Valu
return std::nullopt;
}

auto id = from_json<Mapping::id_t>(mapping_id);
auto id = uuid::from_json<Mapping::id_t>(mapping_id);
if(!id.has_value()) {
ESP_LOGE(TAG, "failed to get id");
return std::nullopt;
Expand Down Expand Up @@ -166,7 +164,7 @@ std::optional<Remove> from_json(const rapidjson::Value &json) {
return std::nullopt;
}

auto uuid = from_json<Mapping::id_t>(id_member->value);
auto uuid = uuid::from_json<Mapping::id_t>(id_member->value);
if(!uuid.has_value())
{
ESP_LOGE(TAG, "Failed to parse UUID");
Expand Down Expand Up @@ -210,75 +208,5 @@ std::optional<MappingApiMessage> from_json(const rapidjson::Value &json) {
return std::nullopt;
}

template<>
std::optional<Mapping::id_t> from_json(const rapidjson::Value &json) {
constexpr const char *TAG = "Mapping::id_t from_json";

if (!json.IsString()) {
ESP_LOGE(TAG, "id is not string");
return std::nullopt;
}

Mapping::id_t out;
int rc = parse_uuid(out, json.GetString());
if(rc != 0) {
ESP_LOGE(TAG, "failed to get uuid");
return std::nullopt;
}

return out;
}

static int parse_uuid(Mapping::id_t &uuid, const char *string)
{
constexpr std::size_t UUID_LENGTH = 36;
constexpr char TAG[] = "parse_uuid";

if(UUID_LENGTH != std::strlen(string))
{
ESP_LOGE(TAG, "Incorrect UUID string length");
return -1;
}

size_t i = 0;
size_t j = 0;
ESP_LOGD(TAG, "i = %zu, j = %zu", i , j);
while(i < UUID_LENGTH)
{
char digit[2];
std::memcpy(digit, &string[i], 2);

ESP_LOGD(TAG, "digit = %c%c", digit[0] , digit[1]);

// TODO error on invalid characters: z, symbols etc
// The only valid characters are 0 to 9 and a to f.
//
// Handle uppercase as well. It is required when a string UUID
// is an input as per RFC 4122 Section 3
// https://tools.ietf.org/html/rfc4122#section-3
auto get_value = [&] (char hex) -> uint8_t {
if(hex >= 'a')
{
return hex - 'a' + 10;
}
else
{
return hex - '0';
}
};

uuid[j] = get_value(digit[0]) << 4 | get_value(digit[1]);

j++;

i += 2;
bool is_separator = (i == 8) || (i == 13) || (i == 18) || (i == 23);
if(is_separator) i++;
ESP_LOGD(TAG, "i = %zu, j = %zu", i , j);
}

return 0;
}

}
}
2 changes: 1 addition & 1 deletion firmware/components/os/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ target_link_libraries(os
idf::freertos)

if(ESP_PLATFORM)
target_sources(os PRIVATE esp32/timer_impl.cpp universal/timer.cpp)
target_sources(os PRIVATE esp32/timer_impl.cpp universal/timer.cpp esp32/debug.cpp)
target_include_directories(os PRIVATE esp32)

return()
Expand Down
38 changes: 38 additions & 0 deletions firmware/components/os/esp32/debug.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2022 Barabas Raffai
*
* This file is part of ShrapnelDSP.
*
* ShrapnelDSP is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* ShrapnelDSP is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* ShrapnelDSP. If not, see <https://www.gnu.org/licenses/>.
*/

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

#define TAG "os_debug"

void debug_dump_task_list()
{
#if configUSE_TRACE_FACILITY && configUSE_STATS_FORMATTING_FUNCTIONS
constexpr size_t characters_per_task = 40;
constexpr size_t approximate_task_count = 20;
char buffer[characters_per_task * approximate_task_count + 1] = {0};
vTaskList(buffer);
// crash if the buffer was overflowed
assert(buffer[sizeof(buffer) - 1] == '\0');
ESP_LOGI(TAG, "Task list:\n%s", buffer);
#endif
}

22 changes: 22 additions & 0 deletions firmware/components/os/include/os/debug.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2022 Barabas Raffai
*
* This file is part of ShrapnelDSP.
*
* ShrapnelDSP is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* ShrapnelDSP is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* ShrapnelDSP. If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once

void debug_dump_task_list();
6 changes: 4 additions & 2 deletions firmware/components/persistence/include/esp_persistence.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ namespace shrapnel::persistence {
*/
class EspStorage final : public Storage {
public:
int save(const char *key, etl::string_view data) override;
int load(const char *key, etl::istring &data) override;
[[nodiscard]] int save(const char *key, etl::string_view data) override;
[[nodiscard]] int save(const char *key, uint32_t data) override;
[[nodiscard]] int load(const char *key, etl::istring &data) override;
[[nodiscard]] int load(const char *key, uint32_t &data) override;
};

}
11 changes: 7 additions & 4 deletions firmware/components/persistence/include/persistence.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@ namespace shrapnel::persistence {
* The data sent to \ref save should be reloaded by \ref load even after power
* down.
*/
class Storage {
class Storage
{
public:
/// \return non-zero on error
virtual int save(const char *key, etl::string_view data) = 0;
[[nodiscard]] virtual int save(const char *key, etl::string_view data) = 0;
[[nodiscard]] virtual int save(const char *key, uint32_t data) = 0;
/// \return non-zero on error
virtual int load(const char *key, etl::istring &data) = 0;
[[nodiscard]] virtual int load(const char *key, etl::istring &data) = 0;
[[nodiscard]] virtual int load(const char *key, uint32_t &data) = 0;
};

}
} // namespace shrapnel::persistence
Loading

0 comments on commit b98fe17

Please sign in to comment.