Improve Catalog
ci / checks (push) Successful in 46m42s

This commit is contained in:
khodowany
2026-09-27 11:03:43 -05:00
parent a416a80dd9
commit 142d843fac
418 changed files with 440 additions and 324488 deletions
+13 -184
View File
@@ -20,11 +20,10 @@
namespace {
constexpr auto main_menu_performance = std::size_t{0};
constexpr auto main_menu_practice = std::size_t{1};
constexpr auto main_menu_playback = std::size_t{2};
constexpr auto main_menu_metronome = std::size_t{3};
constexpr auto main_menu_tempo = std::size_t{4};
constexpr auto main_menu_count = std::size_t{5};
constexpr auto main_menu_playback = std::size_t{1};
constexpr auto main_menu_metronome = std::size_t{2};
constexpr auto main_menu_tempo = std::size_t{3};
constexpr auto main_menu_count = std::size_t{4};
auto wrap_next(std::size_t value, std::size_t count) -> std::size_t {
if (count == 0U) {
@@ -71,48 +70,27 @@ auto clamp_state(vamp_zero::app_state state) -> vamp_zero::app_state {
}
state.audio = clamp_audio_state(state.audio);
state.selected_main_menu_index = std::min(state.selected_main_menu_index, main_menu_count - 1U);
state.selected_song_index = std::min(state.selected_song_index, menu_row_count(state.catalog->performance.groups.size()) - 1U);
state.selected_song_index = std::min(state.selected_song_index, menu_row_count(state.catalog->groups.size()) - 1U);
if (auto const *group = selected_group(state); group != nullptr) {
state.selected_part_index = std::min(state.selected_part_index, menu_row_count(group->parts.size()) - 1U);
} else {
state.selected_part_index = 0U;
}
state.selected_practice_exercise_index = std::min(state.selected_practice_exercise_index, state.catalog->practice.exercises.size());
state.selected_practice_part_index = std::min(state.selected_practice_part_index, practice_part_row_count(state) - 1U);
if (state.mode == vamp_zero::app_mode::PERFORMANCE_PART_MENU && selected_group(state) == nullptr) {
state.mode = vamp_zero::app_mode::PERFORMANCE_SONG_MENU;
}
if (state.mode == vamp_zero::app_mode::PRACTICE_PART_MENU) {
auto const *exercise = selected_practice_exercise(state);
if (exercise == nullptr || exercise->parts.empty()) {
state.mode = vamp_zero::app_mode::PRACTICE_EXERCISE_MENU;
state.selected_practice_part_index = 0U;
}
}
if (state.mode == vamp_zero::app_mode::PERFORMANCE_SCORE && active_part(state) == nullptr) {
state.mode = selected_group(state) == nullptr ? vamp_zero::app_mode::PERFORMANCE_SONG_MENU
: vamp_zero::app_mode::PERFORMANCE_PART_MENU;
state.active_part_id.clear();
state.page_index = 0;
} else if (state.mode == vamp_zero::app_mode::PRACTICE_SCORE) {
auto const *exercise = active_practice(state);
if (exercise == nullptr) {
state.mode = vamp_zero::app_mode::PRACTICE_EXERCISE_MENU;
state.active_practice_id.clear();
state.page_index = 0;
} else if (selected_practice_part(state, *exercise) == nullptr) {
state.mode = vamp_zero::app_mode::PRACTICE_PART_MENU;
state.active_practice_id.clear();
state.page_index = 0;
}
}
if (state.mode != vamp_zero::app_mode::MAIN_MENU) {
state.audio.tempo_editing = false;
}
if (auto pages = active_pages(state); pages != 0U) {
state.page_index = std::min(state.page_index, pages - 1U);
} else if (state.mode != vamp_zero::app_mode::PERFORMANCE_SCORE &&
state.mode != vamp_zero::app_mode::PRACTICE_SCORE) {
} else if (state.mode != vamp_zero::app_mode::PERFORMANCE_SCORE) {
state.page_index = 0;
}
return state;
@@ -154,26 +132,12 @@ auto part_json(vamp_zero::library_part const &part) -> boost::json::object {
return result;
}
auto exercise_json(vamp_zero::practice_exercise const &exercise) -> boost::json::object {
auto parts = boost::json::array{};
for (auto const &part : exercise.parts) {
parts.push_back(part_json(part));
}
auto result = boost::json::object{};
result["id"] = exercise.id;
result["title"] = exercise.title;
result["composer"] = exercise.composer;
result["part_count"] = static_cast<std::uint64_t>(exercise.parts.size());
result["parts"] = std::move(parts);
return result;
}
auto performance_json(vamp_zero::app_state const &state) -> boost::json::object {
auto groups = boost::json::array{};
for (auto const &group : state.catalog->performance.groups) {
for (auto const &group : state.catalog->groups) {
auto parts = boost::json::array{};
for (auto const &part : group.parts) {
parts.push_back(part_json(state.catalog->performance.parts[part]));
parts.push_back(part_json(state.catalog->parts[part]));
}
auto item = boost::json::object{};
item["title"] = group.title;
@@ -182,34 +146,12 @@ auto performance_json(vamp_zero::app_state const &state) -> boost::json::object
groups.push_back(std::move(item));
}
auto library = boost::json::object{};
library["song_count"] = static_cast<std::uint64_t>(state.catalog->performance.groups.size());
library["part_count"] = static_cast<std::uint64_t>(state.catalog->performance.parts.size());
library["song_count"] = static_cast<std::uint64_t>(state.catalog->groups.size());
library["part_count"] = static_cast<std::uint64_t>(state.catalog->parts.size());
library["groups"] = std::move(groups);
return library;
}
auto practice_json(vamp_zero::app_state const &state) -> boost::json::object {
auto exercises = boost::json::array{};
for (auto const &exercise : state.catalog->practice.exercises) {
exercises.push_back(exercise_json(exercise));
}
auto selection = boost::json::object{};
selection["exercise_row_index"] = static_cast<std::uint64_t>(state.selected_practice_exercise_index);
selection["part_row_index"] = static_cast<std::uint64_t>(state.selected_practice_part_index);
if (auto const *exercise = selected_practice_exercise(state); exercise != nullptr) {
selection["exercise"] = exercise_json(*exercise);
auto practice_part = selected_practice_part(state, *exercise);
if (practice_part != nullptr) {
selection["part"] = part_json(*practice_part);
}
}
auto practice = boost::json::object{};
practice["exercise_count"] = static_cast<std::uint64_t>(state.catalog->practice.exercises.size());
practice["exercises"] = std::move(exercises);
practice["selection"] = std::move(selection);
return practice;
}
auto sanitize_line_value(std::string value) -> std::string {
for (auto &ch : value) {
if (ch == '\r' || ch == '\n') {
@@ -251,12 +193,6 @@ auto vamp_zero::apply_app_command(vamp_zero::app_state state, vamp_protocol::pag
state.selected_part_index = 0;
state.active_part_id.clear();
state.page_index = 0;
} else if (state.selected_main_menu_index == main_menu_practice) {
state.mode = vamp_zero::app_mode::PRACTICE_EXERCISE_MENU;
state.selected_practice_exercise_index = 0;
state.selected_practice_part_index = 0;
state.active_practice_id.clear();
state.page_index = 0;
} else if (state.selected_main_menu_index == main_menu_playback) {
state.audio.playback_enabled = !state.audio.playback_enabled;
} else if (state.selected_main_menu_index == main_menu_metronome) {
@@ -267,11 +203,11 @@ auto vamp_zero::apply_app_command(vamp_zero::app_state state, vamp_protocol::pag
return clamp_state(std::move(state));
case vamp_zero::app_mode::PERFORMANCE_SONG_MENU:
if (command == vamp_protocol::page_command::NEXT) {
state.selected_song_index = wrap_next(state.selected_song_index, menu_row_count(state.catalog->performance.groups.size()));
state.selected_song_index = wrap_next(state.selected_song_index, menu_row_count(state.catalog->groups.size()));
state.selected_part_index = 0;
} else if (command == vamp_protocol::page_command::PREVIOUS) {
state.selected_song_index =
wrap_previous(state.selected_song_index, menu_row_count(state.catalog->performance.groups.size()));
wrap_previous(state.selected_song_index, menu_row_count(state.catalog->groups.size()));
state.selected_part_index = 0;
} else if (state.selected_song_index == 0U) {
state.mode = vamp_zero::app_mode::MAIN_MENU;
@@ -292,7 +228,6 @@ auto vamp_zero::apply_app_command(vamp_zero::app_state state, vamp_protocol::pag
state.page_index = 0;
} else if (auto const *part = selected_part(state); part != nullptr) {
state.active_part_id = part->id;
state.active_practice_id.clear();
state.page_index = 0;
state.mode = vamp_zero::app_mode::PERFORMANCE_SCORE;
++state.score_session;
@@ -313,63 +248,6 @@ auto vamp_zero::apply_app_command(vamp_zero::app_state state, vamp_protocol::pag
}
}
return clamp_state(std::move(state));
case vamp_zero::app_mode::PRACTICE_EXERCISE_MENU:
if (command == vamp_protocol::page_command::NEXT) {
state.selected_practice_exercise_index = wrap_next(state.selected_practice_exercise_index, menu_row_count(state.catalog->practice.exercises.size()));
state.selected_practice_part_index = 0;
} else if (command == vamp_protocol::page_command::PREVIOUS) {
state.selected_practice_exercise_index = wrap_previous(state.selected_practice_exercise_index, menu_row_count(state.catalog->practice.exercises.size()));
state.selected_practice_part_index = 0;
} else if (state.selected_practice_exercise_index == 0U) {
state.mode = vamp_zero::app_mode::MAIN_MENU;
state.active_practice_id.clear();
state.page_index = 0;
} else if (selected_practice_exercise(state) != nullptr) {
state.active_part_id.clear();
state.active_practice_id.clear();
state.page_index = 0;
state.mode = vamp_zero::app_mode::PRACTICE_PART_MENU;
state.selected_practice_part_index = 0;
}
return clamp_state(std::move(state));
case vamp_zero::app_mode::PRACTICE_PART_MENU:
if (auto const *exercise = selected_practice_exercise(state);
exercise != nullptr) {
if (command == vamp_protocol::page_command::NEXT) {
state.selected_practice_part_index =
wrap_next(state.selected_practice_part_index, menu_row_count(exercise->parts.size()));
} else if (command == vamp_protocol::page_command::PREVIOUS) {
state.selected_practice_part_index =
wrap_previous(state.selected_practice_part_index, menu_row_count(exercise->parts.size()));
} else if (state.selected_practice_part_index == 0U) {
state.mode = vamp_zero::app_mode::PRACTICE_EXERCISE_MENU;
state.active_practice_id.clear();
state.page_index = 0;
} else if (selected_practice_part(state, *exercise) != nullptr) {
state.active_practice_id = exercise->id;
state.active_part_id.clear();
state.page_index = 0;
state.mode = vamp_zero::app_mode::PRACTICE_SCORE;
++state.score_session;
state.audio.playback_error.clear();
}
}
return clamp_state(std::move(state));
case vamp_zero::app_mode::PRACTICE_SCORE:
if (auto const *exercise = active_practice(state); exercise != nullptr) {
auto const *practice_part = selected_practice_part(state, *exercise);
if (command == vamp_protocol::page_command::NEXT) {
auto pages = practice_part == nullptr ? 0U : practice_part->total_pages;
state.page_index = std::min(state.page_index + 1U, pages == 0U ? 0U : pages - 1U);
} else if (command == vamp_protocol::page_command::PREVIOUS) {
state.page_index = state.page_index == 0U ? 0U : state.page_index - 1U;
} else {
state.mode = vamp_zero::app_mode::PRACTICE_PART_MENU;
state.active_practice_id.clear();
state.page_index = 0;
}
}
return clamp_state(std::move(state));
}
return clamp_state(std::move(state));
}
@@ -396,19 +274,6 @@ auto vamp_zero::app_status_json(vamp_zero::app_state const &state) -> boost::jso
active["page_number"] = static_cast<std::uint64_t>(state.page_index + 1U);
active["page_total"] = static_cast<std::uint64_t>(part->total_pages);
}
} else if (state.mode == vamp_zero::app_mode::PRACTICE_SCORE) {
if (auto const *exercise = active_practice(state); exercise != nullptr) {
auto const *practice_part = selected_practice_part(state, *exercise);
active["selected"] = true;
active["kind"] = "practice";
active["exercise"] = exercise_json(*exercise);
if (practice_part != nullptr) {
active["part"] = part_json(*practice_part);
}
active["page_index"] = static_cast<std::uint64_t>(state.page_index);
active["page_number"] = static_cast<std::uint64_t>(state.page_index + 1U);
active["page_total"] = static_cast<std::uint64_t>(practice_part == nullptr ? 0U : practice_part->total_pages);
}
}
if (active.empty()) {
active["selected"] = false;
@@ -424,7 +289,6 @@ auto vamp_zero::app_status_json(vamp_zero::app_state const &state) -> boost::jso
root["mode"] = vamp_zero::to_string(state.mode);
root["library"] = performance;
root["performance"] = std::move(performance);
root["practice"] = practice_json(state);
root["selection"] = std::move(selection);
root["active"] = std::move(active);
root["audio"] = audio_json(state);
@@ -449,16 +313,6 @@ auto vamp_zero::remote_display_text(vamp_zero::app_state const &state) -> std::s
page = state.page_index + 1U;
pages = selected->total_pages;
}
} else if (state.mode == vamp_zero::app_mode::PRACTICE_SCORE) {
if (auto const *selected = active_practice(state); selected != nullptr) {
auto const *practice_part = selected_practice_part(state, *selected);
active = true;
title = selected->title;
part = practice_part == nullptr ? std::string{} : practice_part->part;
title_image = practice_part == nullptr ? std::string{} : practice_part->title_image;
page = state.page_index + 1U;
pages = practice_part == nullptr ? 0U : practice_part->total_pages;
}
}
auto output = std::ostringstream{};
output << "mode=" << vamp_zero::to_string(state.mode) << "\n";
@@ -494,25 +348,13 @@ auto vamp_zero::validate_app_state(vamp_zero::app_state const &state) -> std::ex
if (state.audio.tempo_editing && state.mode != vamp_zero::app_mode::MAIN_MENU) {
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "tempo edit mode requires the main menu"));
}
if (state.selected_song_index >= menu_row_count(state.catalog->performance.groups.size())) {
if (state.selected_song_index >= menu_row_count(state.catalog->groups.size())) {
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "selected performance song row is out of range"));
}
if (auto const *group = selected_group(state);
group != nullptr && state.selected_part_index >= menu_row_count(group->parts.size())) {
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "selected performance part row is out of range"));
}
if (state.selected_practice_exercise_index >= menu_row_count(state.catalog->practice.exercises.size())) {
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "selected practice exercise row is out of range"));
}
if (state.selected_practice_part_index >= practice_part_row_count(state)) {
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "selected practice part row is out of range"));
}
if (state.mode == vamp_zero::app_mode::PRACTICE_PART_MENU) {
auto const *exercise = selected_practice_exercise(state);
if (exercise == nullptr || exercise->parts.empty()) {
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "practice part mode requires an exercise with parts"));
}
}
if (state.mode == vamp_zero::app_mode::PERFORMANCE_SCORE) {
auto const *part = active_part(state);
if (part == nullptr) {
@@ -522,18 +364,5 @@ auto vamp_zero::validate_app_state(vamp_zero::app_state const &state) -> std::ex
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "performance score page index is out of range"));
}
}
if (state.mode == vamp_zero::app_mode::PRACTICE_SCORE) {
auto const *exercise = active_practice(state);
if (exercise == nullptr) {
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "practice score mode requires an active exercise"));
}
auto const *practice_part = selected_practice_part(state, *exercise);
if (practice_part == nullptr) {
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "practice score mode requires a selected generated part"));
}
if (practice_part->total_pages == 0U || state.page_index >= practice_part->total_pages) {
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "practice score page index is out of range"));
}
}
return {};
}