+101
-99
@@ -16,6 +16,7 @@
|
||||
#include <vamp_zero/errors.hpp>
|
||||
#include <vamp_zero/navigation.hpp>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto main_menu_performance = std::size_t{0};
|
||||
@@ -51,41 +52,43 @@ auto clamp_audio_state(vamp_zero::audio_state audio) -> vamp_zero::audio_state {
|
||||
return audio;
|
||||
}
|
||||
|
||||
auto increase_bpm(vamp_zero::audio_state& audio) -> void {
|
||||
audio.metronome_bpm = audio.metronome_bpm > audio.metronome_max_bpm - std::min(audio.metronome_step, audio.metronome_max_bpm)
|
||||
? audio.metronome_max_bpm
|
||||
: audio.metronome_bpm + audio.metronome_step;
|
||||
auto increase_bpm(vamp_zero::audio_state &audio) -> void {
|
||||
audio.metronome_bpm =
|
||||
audio.metronome_bpm > audio.metronome_max_bpm - std::min(audio.metronome_step, audio.metronome_max_bpm)
|
||||
? audio.metronome_max_bpm
|
||||
: audio.metronome_bpm + audio.metronome_step;
|
||||
}
|
||||
|
||||
auto decrease_bpm(vamp_zero::audio_state& audio) -> void {
|
||||
auto decrease_bpm(vamp_zero::audio_state &audio) -> void {
|
||||
audio.metronome_bpm = audio.metronome_bpm < audio.metronome_min_bpm + audio.metronome_step
|
||||
? audio.metronome_min_bpm
|
||||
: audio.metronome_bpm - audio.metronome_step;
|
||||
? audio.metronome_min_bpm
|
||||
: audio.metronome_bpm - audio.metronome_step;
|
||||
}
|
||||
|
||||
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.performance.groups.size()) - 1U);
|
||||
if (auto const* group = selected_group(state); group != nullptr) {
|
||||
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_type_index = std::min(state.selected_practice_type_index, menu_row_count(state.practice.types.size()) - 1U);
|
||||
if (auto const* type = selected_practice_type(state); type != nullptr) {
|
||||
state.selected_practice_lesson_index = std::min(state.selected_practice_lesson_index, menu_row_count(type->lesson_groups.size()) - 1U);
|
||||
state.selected_practice_type_index =
|
||||
std::min(state.selected_practice_type_index, menu_row_count(state.practice.types.size()) - 1U);
|
||||
if (auto const *type = selected_practice_type(state); type != nullptr) {
|
||||
state.selected_practice_lesson_index =
|
||||
std::min(state.selected_practice_lesson_index, menu_row_count(type->lesson_groups.size()) - 1U);
|
||||
} else {
|
||||
state.selected_practice_lesson_index = 0U;
|
||||
}
|
||||
if (auto const* lesson = selected_practice_lesson(state); lesson != nullptr) {
|
||||
state.selected_practice_exercise_index = std::min(state.selected_practice_exercise_index, menu_row_count(lesson->exercises.size()) - 1U);
|
||||
if (auto const *lesson = selected_practice_lesson(state); lesson != nullptr) {
|
||||
state.selected_practice_exercise_index =
|
||||
std::min(state.selected_practice_exercise_index, menu_row_count(lesson->exercises.size()) - 1U);
|
||||
} else {
|
||||
state.selected_practice_exercise_index = 0U;
|
||||
}
|
||||
state.selected_practice_key_index = std::min(state.selected_practice_key_index, practice_key_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;
|
||||
}
|
||||
@@ -93,10 +96,11 @@ auto clamp_state(vamp_zero::app_state state) -> vamp_zero::app_state {
|
||||
state.mode = vamp_zero::app_mode::PRACTICE_TYPE_MENU;
|
||||
}
|
||||
if (state.mode == vamp_zero::app_mode::PRACTICE_EXERCISE_MENU && selected_practice_lesson(state) == nullptr) {
|
||||
state.mode = selected_practice_type(state) == nullptr ? vamp_zero::app_mode::PRACTICE_TYPE_MENU : vamp_zero::app_mode::PRACTICE_LESSON_MENU;
|
||||
state.mode = selected_practice_type(state) == nullptr ? vamp_zero::app_mode::PRACTICE_TYPE_MENU
|
||||
: vamp_zero::app_mode::PRACTICE_LESSON_MENU;
|
||||
}
|
||||
if (state.mode == vamp_zero::app_mode::PRACTICE_KEY_MENU) {
|
||||
auto const* exercise = selected_practice_exercise(state);
|
||||
auto const *exercise = selected_practice_exercise(state);
|
||||
if (exercise == nullptr) {
|
||||
state.mode = practice_fallback_menu(state);
|
||||
} else if (!supports_key_selection(*exercise) || exercise->variants.empty()) {
|
||||
@@ -104,13 +108,13 @@ auto clamp_state(vamp_zero::app_state state) -> vamp_zero::app_state {
|
||||
state.selected_practice_key_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.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);
|
||||
auto const *exercise = active_practice(state);
|
||||
if (exercise == nullptr) {
|
||||
state.mode = practice_fallback_menu(state);
|
||||
state.active_practice_id.clear();
|
||||
@@ -121,20 +125,19 @@ auto clamp_state(vamp_zero::app_state state) -> vamp_zero::app_state {
|
||||
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.mode != vamp_zero::app_mode::PRACTICE_SCORE) {
|
||||
state.page_index = 0;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
auto command_json(vamp_zero::app_state const& state) -> boost::json::object {
|
||||
auto command_json(vamp_zero::app_state const &state) -> boost::json::object {
|
||||
auto commands = boost::json::object{};
|
||||
commands["last"] = state.last_command;
|
||||
commands["next_count"] = state.next_count;
|
||||
@@ -144,7 +147,7 @@ auto command_json(vamp_zero::app_state const& state) -> boost::json::object {
|
||||
return commands;
|
||||
}
|
||||
|
||||
auto audio_json(vamp_zero::app_state const& state) -> boost::json::object {
|
||||
auto audio_json(vamp_zero::app_state const &state) -> boost::json::object {
|
||||
auto audio = boost::json::object{};
|
||||
audio["track_enabled"] = state.audio.track_enabled;
|
||||
audio["metronome_enabled"] = state.audio.metronome_enabled;
|
||||
@@ -156,7 +159,7 @@ auto audio_json(vamp_zero::app_state const& state) -> boost::json::object {
|
||||
return audio;
|
||||
}
|
||||
|
||||
auto part_json(vamp_zero::library_part const& part) -> boost::json::object {
|
||||
auto part_json(vamp_zero::library_part const &part) -> boost::json::object {
|
||||
auto result = boost::json::object{};
|
||||
result["id"] = part.id;
|
||||
result["title"] = part.title;
|
||||
@@ -169,9 +172,9 @@ 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 exercise_json(vamp_zero::practice_exercise const &exercise) -> boost::json::object {
|
||||
auto variants = boost::json::array{};
|
||||
for (auto const& variant : exercise.variants) {
|
||||
for (auto const &variant : exercise.variants) {
|
||||
auto item = boost::json::object{};
|
||||
item["id"] = variant.id;
|
||||
item["key_signature"] = variant.key_signature;
|
||||
@@ -198,9 +201,9 @@ auto exercise_json(vamp_zero::practice_exercise const& exercise) -> boost::json:
|
||||
return result;
|
||||
}
|
||||
|
||||
auto lesson_json(vamp_zero::practice_lesson_group const& lesson) -> boost::json::object {
|
||||
auto lesson_json(vamp_zero::practice_lesson_group const &lesson) -> boost::json::object {
|
||||
auto exercises = boost::json::array{};
|
||||
for (auto const& exercise : lesson.exercises) {
|
||||
for (auto const &exercise : lesson.exercises) {
|
||||
exercises.push_back(exercise_json(exercise));
|
||||
}
|
||||
auto result = boost::json::object{};
|
||||
@@ -211,17 +214,17 @@ auto lesson_json(vamp_zero::practice_lesson_group const& lesson) -> boost::json:
|
||||
return result;
|
||||
}
|
||||
|
||||
auto practice_type_exercise_count(vamp_zero::practice_type_group const& type) -> std::size_t {
|
||||
auto practice_type_exercise_count(vamp_zero::practice_type_group const &type) -> std::size_t {
|
||||
auto count = std::size_t{};
|
||||
for (auto const& lesson : type.lesson_groups) {
|
||||
for (auto const &lesson : type.lesson_groups) {
|
||||
count += lesson.exercises.size();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
auto type_json(vamp_zero::practice_type_group const& type) -> boost::json::object {
|
||||
auto type_json(vamp_zero::practice_type_group const &type) -> boost::json::object {
|
||||
auto lessons = boost::json::array{};
|
||||
for (auto const& lesson : type.lesson_groups) {
|
||||
for (auto const &lesson : type.lesson_groups) {
|
||||
lessons.push_back(lesson_json(lesson));
|
||||
}
|
||||
auto result = boost::json::object{};
|
||||
@@ -233,11 +236,11 @@ auto type_json(vamp_zero::practice_type_group const& type) -> boost::json::objec
|
||||
return result;
|
||||
}
|
||||
|
||||
auto performance_json(vamp_zero::app_state const& state) -> boost::json::object {
|
||||
auto performance_json(vamp_zero::app_state const &state) -> boost::json::object {
|
||||
auto groups = boost::json::array{};
|
||||
for (auto const& group : state.performance.groups) {
|
||||
for (auto const &group : state.performance.groups) {
|
||||
auto parts = boost::json::array{};
|
||||
for (auto const& part : group.parts) {
|
||||
for (auto const &part : group.parts) {
|
||||
parts.push_back(part_json(part));
|
||||
}
|
||||
auto item = boost::json::object{};
|
||||
@@ -246,7 +249,6 @@ auto performance_json(vamp_zero::app_state const& state) -> boost::json::object
|
||||
item["parts"] = std::move(parts);
|
||||
groups.push_back(std::move(item));
|
||||
}
|
||||
|
||||
auto library = boost::json::object{};
|
||||
library["song_count"] = static_cast<std::uint64_t>(state.performance.groups.size());
|
||||
library["part_count"] = static_cast<std::uint64_t>(state.performance.parts.size());
|
||||
@@ -254,32 +256,31 @@ auto performance_json(vamp_zero::app_state const& state) -> boost::json::object
|
||||
return library;
|
||||
}
|
||||
|
||||
auto practice_json(vamp_zero::app_state const& state) -> boost::json::object {
|
||||
auto practice_json(vamp_zero::app_state const &state) -> boost::json::object {
|
||||
auto types = boost::json::array{};
|
||||
for (auto const& type : state.practice.types) {
|
||||
for (auto const &type : state.practice.types) {
|
||||
types.push_back(type_json(type));
|
||||
}
|
||||
auto exercises = boost::json::array{};
|
||||
for (auto const& exercise : state.practice.exercises) {
|
||||
for (auto const &exercise : state.practice.exercises) {
|
||||
exercises.push_back(exercise_json(exercise));
|
||||
}
|
||||
auto keys = boost::json::array{};
|
||||
for (auto key : vamp_zero::practice_key_names()) {
|
||||
keys.push_back(boost::json::value{std::string{key}});
|
||||
}
|
||||
|
||||
auto selection = boost::json::object{};
|
||||
selection["type_row_index"] = static_cast<std::uint64_t>(state.selected_practice_type_index);
|
||||
selection["lesson_row_index"] = static_cast<std::uint64_t>(state.selected_practice_lesson_index);
|
||||
selection["exercise_row_index"] = static_cast<std::uint64_t>(state.selected_practice_exercise_index);
|
||||
selection["key_row_index"] = static_cast<std::uint64_t>(state.selected_practice_key_index);
|
||||
if (auto const* type = selected_practice_type(state); type != nullptr) {
|
||||
if (auto const *type = selected_practice_type(state); type != nullptr) {
|
||||
selection["type"] = type_json(*type);
|
||||
}
|
||||
if (auto const* lesson = selected_practice_lesson(state); lesson != nullptr) {
|
||||
if (auto const *lesson = selected_practice_lesson(state); lesson != nullptr) {
|
||||
selection["lesson_group"] = lesson_json(*lesson);
|
||||
}
|
||||
if (auto const* exercise = selected_practice_exercise(state); exercise != nullptr) {
|
||||
if (auto const *exercise = selected_practice_exercise(state); exercise != nullptr) {
|
||||
selection["exercise"] = exercise_json(*exercise);
|
||||
auto variant = selected_practice_variant(state, *exercise);
|
||||
if (variant != nullptr) {
|
||||
@@ -292,12 +293,11 @@ auto practice_json(vamp_zero::app_state const& state) -> boost::json::object {
|
||||
selection["variant"] = std::move(item);
|
||||
}
|
||||
}
|
||||
if (auto const* exercise = selected_practice_exercise(state); exercise != nullptr) {
|
||||
if (auto const *exercise = selected_practice_exercise(state); exercise != nullptr) {
|
||||
if (auto key = selected_practice_key(state, *exercise)) {
|
||||
selection["key"] = std::string{*key};
|
||||
}
|
||||
}
|
||||
|
||||
auto practice = boost::json::object{};
|
||||
practice["type_count"] = static_cast<std::uint64_t>(state.practice.types.size());
|
||||
practice["exercise_count"] = static_cast<std::uint64_t>(state.practice.exercises.size());
|
||||
@@ -310,7 +310,7 @@ auto practice_json(vamp_zero::app_state const& state) -> boost::json::object {
|
||||
}
|
||||
|
||||
auto sanitize_line_value(std::string value) -> std::string {
|
||||
for (auto& ch : value) {
|
||||
for (auto &ch : value) {
|
||||
if (ch == '\r' || ch == '\n') {
|
||||
ch = ' ';
|
||||
}
|
||||
@@ -330,7 +330,6 @@ auto vamp_zero::apply_app_command(vamp_zero::app_state state, vamp_protocol::pag
|
||||
++state.select_count;
|
||||
}
|
||||
state.last_command = vamp_protocol::page_command_name(command);
|
||||
|
||||
switch (state.mode) {
|
||||
case vamp_zero::app_mode::MAIN_MENU:
|
||||
if (state.audio.tempo_editing) {
|
||||
@@ -367,13 +366,13 @@ auto vamp_zero::apply_app_command(vamp_zero::app_state state, vamp_protocol::pag
|
||||
state.audio.tempo_editing = true;
|
||||
}
|
||||
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.performance.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.performance.groups.size()));
|
||||
state.selected_song_index =
|
||||
wrap_previous(state.selected_song_index, menu_row_count(state.performance.groups.size()));
|
||||
state.selected_part_index = 0;
|
||||
} else if (state.selected_song_index == 0U) {
|
||||
state.mode = vamp_zero::app_mode::MAIN_MENU;
|
||||
@@ -382,9 +381,8 @@ auto vamp_zero::apply_app_command(vamp_zero::app_state state, vamp_protocol::pag
|
||||
state.selected_part_index = 0;
|
||||
}
|
||||
return clamp_state(std::move(state));
|
||||
|
||||
case vamp_zero::app_mode::PERFORMANCE_PART_MENU:
|
||||
if (auto const* group = selected_group(state); group != nullptr) {
|
||||
if (auto const *group = selected_group(state); group != nullptr) {
|
||||
if (command == vamp_protocol::page_command::NEXT) {
|
||||
state.selected_part_index = wrap_next(state.selected_part_index, menu_row_count(group->parts.size()));
|
||||
} else if (command == vamp_protocol::page_command::PREVIOUS) {
|
||||
@@ -393,7 +391,7 @@ auto vamp_zero::apply_app_command(vamp_zero::app_state state, vamp_protocol::pag
|
||||
state.mode = vamp_zero::app_mode::PERFORMANCE_SONG_MENU;
|
||||
state.active_part_id.clear();
|
||||
state.page_index = 0;
|
||||
} else if (auto const* part = selected_part(state); part != nullptr) {
|
||||
} 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;
|
||||
@@ -401,9 +399,8 @@ 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_SCORE:
|
||||
if (auto const* part = active_part(state); part != nullptr) {
|
||||
if (auto const *part = active_part(state); part != nullptr) {
|
||||
if (command == vamp_protocol::page_command::NEXT) {
|
||||
state.page_index = std::min(state.page_index + 1U, part->total_pages == 0U ? 0U : part->total_pages - 1U);
|
||||
} else if (command == vamp_protocol::page_command::PREVIOUS) {
|
||||
@@ -415,15 +412,16 @@ 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_TYPE_MENU:
|
||||
if (command == vamp_protocol::page_command::NEXT) {
|
||||
state.selected_practice_type_index = wrap_next(state.selected_practice_type_index, menu_row_count(state.practice.types.size()));
|
||||
state.selected_practice_type_index =
|
||||
wrap_next(state.selected_practice_type_index, menu_row_count(state.practice.types.size()));
|
||||
state.selected_practice_lesson_index = 0;
|
||||
state.selected_practice_exercise_index = 0;
|
||||
state.selected_practice_key_index = 0;
|
||||
} else if (command == vamp_protocol::page_command::PREVIOUS) {
|
||||
state.selected_practice_type_index = wrap_previous(state.selected_practice_type_index, menu_row_count(state.practice.types.size()));
|
||||
state.selected_practice_type_index =
|
||||
wrap_previous(state.selected_practice_type_index, menu_row_count(state.practice.types.size()));
|
||||
state.selected_practice_lesson_index = 0;
|
||||
state.selected_practice_exercise_index = 0;
|
||||
state.selected_practice_key_index = 0;
|
||||
@@ -436,15 +434,16 @@ auto vamp_zero::apply_app_command(vamp_zero::app_state state, vamp_protocol::pag
|
||||
state.selected_practice_key_index = 0;
|
||||
}
|
||||
return clamp_state(std::move(state));
|
||||
|
||||
case vamp_zero::app_mode::PRACTICE_LESSON_MENU:
|
||||
if (auto const* type = selected_practice_type(state); type != nullptr) {
|
||||
if (auto const *type = selected_practice_type(state); type != nullptr) {
|
||||
if (command == vamp_protocol::page_command::NEXT) {
|
||||
state.selected_practice_lesson_index = wrap_next(state.selected_practice_lesson_index, menu_row_count(type->lesson_groups.size()));
|
||||
state.selected_practice_lesson_index =
|
||||
wrap_next(state.selected_practice_lesson_index, menu_row_count(type->lesson_groups.size()));
|
||||
state.selected_practice_exercise_index = 0;
|
||||
state.selected_practice_key_index = 0;
|
||||
} else if (command == vamp_protocol::page_command::PREVIOUS) {
|
||||
state.selected_practice_lesson_index = wrap_previous(state.selected_practice_lesson_index, menu_row_count(type->lesson_groups.size()));
|
||||
state.selected_practice_lesson_index =
|
||||
wrap_previous(state.selected_practice_lesson_index, menu_row_count(type->lesson_groups.size()));
|
||||
state.selected_practice_exercise_index = 0;
|
||||
state.selected_practice_key_index = 0;
|
||||
} else if (state.selected_practice_lesson_index == 0U) {
|
||||
@@ -458,20 +457,21 @@ 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 (auto const* lesson = selected_practice_lesson(state); lesson != nullptr) {
|
||||
if (auto const *lesson = selected_practice_lesson(state); lesson != nullptr) {
|
||||
if (command == vamp_protocol::page_command::NEXT) {
|
||||
state.selected_practice_exercise_index = wrap_next(state.selected_practice_exercise_index, menu_row_count(lesson->exercises.size()));
|
||||
state.selected_practice_exercise_index =
|
||||
wrap_next(state.selected_practice_exercise_index, menu_row_count(lesson->exercises.size()));
|
||||
state.selected_practice_key_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(lesson->exercises.size()));
|
||||
state.selected_practice_exercise_index =
|
||||
wrap_previous(state.selected_practice_exercise_index, menu_row_count(lesson->exercises.size()));
|
||||
state.selected_practice_key_index = 0;
|
||||
} else if (state.selected_practice_exercise_index == 0U) {
|
||||
state.mode = vamp_zero::app_mode::PRACTICE_LESSON_MENU;
|
||||
state.active_practice_id.clear();
|
||||
state.page_index = 0;
|
||||
} else if (auto const* exercise = selected_practice_exercise(state); exercise != nullptr) {
|
||||
} else if (auto const *exercise = selected_practice_exercise(state); exercise != nullptr) {
|
||||
state.active_part_id.clear();
|
||||
state.active_practice_id.clear();
|
||||
state.page_index = 0;
|
||||
@@ -486,13 +486,15 @@ 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_KEY_MENU:
|
||||
if (auto const* exercise = selected_practice_exercise(state); exercise != nullptr && supports_key_selection(*exercise)) {
|
||||
if (auto const *exercise = selected_practice_exercise(state);
|
||||
exercise != nullptr && supports_key_selection(*exercise)) {
|
||||
if (command == vamp_protocol::page_command::NEXT) {
|
||||
state.selected_practice_key_index = wrap_next(state.selected_practice_key_index, menu_row_count(exercise->variants.size()));
|
||||
state.selected_practice_key_index =
|
||||
wrap_next(state.selected_practice_key_index, menu_row_count(exercise->variants.size()));
|
||||
} else if (command == vamp_protocol::page_command::PREVIOUS) {
|
||||
state.selected_practice_key_index = wrap_previous(state.selected_practice_key_index, menu_row_count(exercise->variants.size()));
|
||||
state.selected_practice_key_index =
|
||||
wrap_previous(state.selected_practice_key_index, menu_row_count(exercise->variants.size()));
|
||||
} else if (state.selected_practice_key_index == 0U) {
|
||||
state.mode = vamp_zero::app_mode::PRACTICE_EXERCISE_MENU;
|
||||
state.active_practice_id.clear();
|
||||
@@ -505,17 +507,17 @@ 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_SCORE:
|
||||
if (auto const* exercise = active_practice(state); exercise != nullptr) {
|
||||
auto const* variant = selected_practice_variant(state, *exercise);
|
||||
if (auto const *exercise = active_practice(state); exercise != nullptr) {
|
||||
auto const *variant = selected_practice_variant(state, *exercise);
|
||||
if (command == vamp_protocol::page_command::NEXT) {
|
||||
auto pages = variant == nullptr ? 0U : variant->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 = supports_key_selection(*exercise) ? vamp_zero::app_mode::PRACTICE_KEY_MENU : vamp_zero::app_mode::PRACTICE_EXERCISE_MENU;
|
||||
state.mode = supports_key_selection(*exercise) ? vamp_zero::app_mode::PRACTICE_KEY_MENU
|
||||
: vamp_zero::app_mode::PRACTICE_EXERCISE_MENU;
|
||||
state.active_practice_id.clear();
|
||||
state.page_index = 0;
|
||||
}
|
||||
@@ -525,22 +527,21 @@ auto vamp_zero::apply_app_command(vamp_zero::app_state state, vamp_protocol::pag
|
||||
return clamp_state(std::move(state));
|
||||
}
|
||||
|
||||
auto vamp_zero::app_status_json(vamp_zero::app_state const& state) -> boost::json::value {
|
||||
auto vamp_zero::app_status_json(vamp_zero::app_state const &state) -> boost::json::value {
|
||||
auto selection = boost::json::object{};
|
||||
selection["main_menu_index"] = static_cast<std::uint64_t>(state.selected_main_menu_index);
|
||||
selection["song_row_index"] = static_cast<std::uint64_t>(state.selected_song_index);
|
||||
selection["part_row_index"] = static_cast<std::uint64_t>(state.selected_part_index);
|
||||
if (auto const* group = selected_group(state); group != nullptr) {
|
||||
if (auto const *group = selected_group(state); group != nullptr) {
|
||||
selection["title"] = group->title;
|
||||
selection["composer"] = group->composer;
|
||||
}
|
||||
if (auto const* part = selected_part(state); part != nullptr) {
|
||||
if (auto const *part = selected_part(state); part != nullptr) {
|
||||
selection["part"] = part_json(*part);
|
||||
}
|
||||
|
||||
auto active = boost::json::object{};
|
||||
if (state.mode == vamp_zero::app_mode::PERFORMANCE_SCORE) {
|
||||
if (auto const* part = active_part(state); part != nullptr) {
|
||||
if (auto const *part = active_part(state); part != nullptr) {
|
||||
active["selected"] = true;
|
||||
active["kind"] = "performance";
|
||||
active["part"] = part_json(*part);
|
||||
@@ -549,8 +550,8 @@ auto vamp_zero::app_status_json(vamp_zero::app_state const& state) -> boost::jso
|
||||
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* variant = selected_practice_variant(state, *exercise);
|
||||
if (auto const *exercise = active_practice(state); exercise != nullptr) {
|
||||
auto const *variant = selected_practice_variant(state, *exercise);
|
||||
active["selected"] = true;
|
||||
active["kind"] = "practice";
|
||||
active["exercise"] = exercise_json(*exercise);
|
||||
@@ -566,11 +567,9 @@ auto vamp_zero::app_status_json(vamp_zero::app_state const& state) -> boost::jso
|
||||
active["page_number"] = 0;
|
||||
active["page_total"] = 0;
|
||||
}
|
||||
|
||||
auto remote = boost::json::object{};
|
||||
remote["pico_connected"] = state.pico_connected;
|
||||
remote["heartbeat_count"] = state.heartbeat_count;
|
||||
|
||||
auto performance = performance_json(state);
|
||||
auto root = boost::json::object{};
|
||||
root["mode"] = vamp_zero::to_string(state.mode);
|
||||
@@ -585,14 +584,14 @@ auto vamp_zero::app_status_json(vamp_zero::app_state const& state) -> boost::jso
|
||||
return root;
|
||||
}
|
||||
|
||||
auto vamp_zero::remote_display_text(vamp_zero::app_state const& state) -> std::string {
|
||||
auto vamp_zero::remote_display_text(vamp_zero::app_state const &state) -> std::string {
|
||||
auto title = std::string{};
|
||||
auto part = std::string{};
|
||||
auto page = std::size_t{};
|
||||
auto pages = std::size_t{};
|
||||
auto active = std::bool_t{false};
|
||||
if (state.mode == vamp_zero::app_mode::PERFORMANCE_SCORE) {
|
||||
if (auto const* selected = active_part(state); selected != nullptr) {
|
||||
if (auto const *selected = active_part(state); selected != nullptr) {
|
||||
active = true;
|
||||
title = selected->title;
|
||||
part = selected->part;
|
||||
@@ -600,8 +599,8 @@ auto vamp_zero::remote_display_text(vamp_zero::app_state const& state) -> std::s
|
||||
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* variant = selected_practice_variant(state, *selected);
|
||||
if (auto const *selected = active_practice(state); selected != nullptr) {
|
||||
auto const *variant = selected_practice_variant(state, *selected);
|
||||
active = true;
|
||||
title = selected->title;
|
||||
part = "Key: " + effective_practice_key(state, *selected);
|
||||
@@ -609,7 +608,6 @@ auto vamp_zero::remote_display_text(vamp_zero::app_state const& state) -> std::s
|
||||
pages = variant == nullptr ? 0U : variant->total_pages;
|
||||
}
|
||||
}
|
||||
|
||||
auto output = std::ostringstream{};
|
||||
output << "mode=" << vamp_zero::to_string(state.mode) << "\n";
|
||||
output << "active=" << (active ? "1" : "0") << "\n";
|
||||
@@ -620,7 +618,7 @@ auto vamp_zero::remote_display_text(vamp_zero::app_state const& state) -> std::s
|
||||
return output.str();
|
||||
}
|
||||
|
||||
auto vamp_zero::validate_app_state(vamp_zero::app_state const& state) -> std::expected<void, modulus::error> {
|
||||
auto vamp_zero::validate_app_state(vamp_zero::app_state const &state) -> std::expected<void, modulus::error> {
|
||||
if (state.selected_main_menu_index >= main_menu_count) {
|
||||
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "selected main menu index is out of range"));
|
||||
}
|
||||
@@ -633,7 +631,8 @@ auto vamp_zero::validate_app_state(vamp_zero::app_state const& state) -> std::ex
|
||||
if (state.audio.metronome_step == 0U || state.audio.metronome_step > 100U) {
|
||||
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "metronome BPM step is out of range"));
|
||||
}
|
||||
if (state.audio.metronome_bpm < state.audio.metronome_min_bpm || state.audio.metronome_bpm > state.audio.metronome_max_bpm) {
|
||||
if (state.audio.metronome_bpm < state.audio.metronome_min_bpm ||
|
||||
state.audio.metronome_bpm > state.audio.metronome_max_bpm) {
|
||||
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "metronome BPM is out of range"));
|
||||
}
|
||||
if (state.audio.tempo_editing && state.mode != vamp_zero::app_mode::MAIN_MENU) {
|
||||
@@ -642,16 +641,19 @@ auto vamp_zero::validate_app_state(vamp_zero::app_state const& state) -> std::ex
|
||||
if (state.selected_song_index >= menu_row_count(state.performance.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())) {
|
||||
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_type_index >= menu_row_count(state.practice.types.size())) {
|
||||
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "selected practice type row is out of range"));
|
||||
}
|
||||
if (auto const* type = selected_practice_type(state); type != nullptr && state.selected_practice_lesson_index >= menu_row_count(type->lesson_groups.size())) {
|
||||
if (auto const *type = selected_practice_type(state);
|
||||
type != nullptr && state.selected_practice_lesson_index >= menu_row_count(type->lesson_groups.size())) {
|
||||
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "selected practice lesson row is out of range"));
|
||||
}
|
||||
if (auto const* lesson = selected_practice_lesson(state); lesson != nullptr && state.selected_practice_exercise_index >= menu_row_count(lesson->exercises.size())) {
|
||||
if (auto const *lesson = selected_practice_lesson(state);
|
||||
lesson != nullptr && state.selected_practice_exercise_index >= menu_row_count(lesson->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_key_index >= practice_key_row_count(state)) {
|
||||
@@ -664,13 +666,13 @@ 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, "practice exercise mode requires a selected lesson group"));
|
||||
}
|
||||
if (state.mode == vamp_zero::app_mode::PRACTICE_KEY_MENU) {
|
||||
auto const* exercise = selected_practice_exercise(state);
|
||||
auto const *exercise = selected_practice_exercise(state);
|
||||
if (exercise == nullptr || !supports_key_selection(*exercise)) {
|
||||
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "practice key mode requires a melody exercise"));
|
||||
}
|
||||
}
|
||||
if (state.mode == vamp_zero::app_mode::PERFORMANCE_SCORE) {
|
||||
auto const* part = active_part(state);
|
||||
auto const *part = active_part(state);
|
||||
if (part == nullptr) {
|
||||
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "performance score mode requires an active part"));
|
||||
}
|
||||
@@ -679,11 +681,11 @@ auto vamp_zero::validate_app_state(vamp_zero::app_state const& state) -> std::ex
|
||||
}
|
||||
}
|
||||
if (state.mode == vamp_zero::app_mode::PRACTICE_SCORE) {
|
||||
auto const* exercise = active_practice(state);
|
||||
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* variant = selected_practice_variant(state, *exercise);
|
||||
auto const *variant = selected_practice_variant(state, *exercise);
|
||||
if (variant == nullptr) {
|
||||
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "practice score mode requires a selected generated variant"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user