@@ -18,7 +18,12 @@
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto main_menu_count = std::size_t{2};
|
||||
constexpr auto main_menu_performance = std::size_t{0};
|
||||
constexpr auto main_menu_practice = std::size_t{1};
|
||||
constexpr auto main_menu_track = 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};
|
||||
|
||||
auto wrap_next(std::size_t value, std::size_t count) -> std::size_t {
|
||||
if (count == 0U) {
|
||||
@@ -38,7 +43,28 @@ auto menu_row_count(std::size_t item_count) -> std::size_t {
|
||||
return item_count + 1U;
|
||||
}
|
||||
|
||||
auto clamp_audio_state(vamp_zero::audio_state audio) -> vamp_zero::audio_state {
|
||||
audio.metronome_min_bpm = std::clamp<std::uint32_t>(audio.metronome_min_bpm, 1U, 400U);
|
||||
audio.metronome_max_bpm = std::clamp<std::uint32_t>(audio.metronome_max_bpm, audio.metronome_min_bpm, 400U);
|
||||
audio.metronome_step = std::clamp<std::uint32_t>(audio.metronome_step, 1U, 100U);
|
||||
audio.metronome_bpm = std::clamp(audio.metronome_bpm, audio.metronome_min_bpm, audio.metronome_max_bpm);
|
||||
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 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;
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -96,6 +122,10 @@ auto clamp_state(vamp_zero::app_state state) -> vamp_zero::app_state {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -114,6 +144,18 @@ 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 = boost::json::object{};
|
||||
audio["track_enabled"] = state.audio.track_enabled;
|
||||
audio["metronome_enabled"] = state.audio.metronome_enabled;
|
||||
audio["metronome_bpm"] = static_cast<std::uint64_t>(state.audio.metronome_bpm);
|
||||
audio["metronome_min_bpm"] = static_cast<std::uint64_t>(state.audio.metronome_min_bpm);
|
||||
audio["metronome_max_bpm"] = static_cast<std::uint64_t>(state.audio.metronome_max_bpm);
|
||||
audio["metronome_step"] = static_cast<std::uint64_t>(state.audio.metronome_step);
|
||||
audio["tempo_editing"] = state.audio.tempo_editing;
|
||||
return audio;
|
||||
}
|
||||
|
||||
auto part_json(vamp_zero::library_part const& part) -> boost::json::object {
|
||||
auto result = boost::json::object{};
|
||||
result["id"] = part.id;
|
||||
@@ -291,17 +333,25 @@ auto vamp_zero::apply_app_command(vamp_zero::app_state state, vamp_protocol::pag
|
||||
|
||||
switch (state.mode) {
|
||||
case vamp_zero::app_mode::MAIN_MENU:
|
||||
if (command == vamp_protocol::page_command::NEXT) {
|
||||
if (state.audio.tempo_editing) {
|
||||
if (command == vamp_protocol::page_command::NEXT) {
|
||||
increase_bpm(state.audio);
|
||||
} else if (command == vamp_protocol::page_command::PREVIOUS) {
|
||||
decrease_bpm(state.audio);
|
||||
} else {
|
||||
state.audio.tempo_editing = false;
|
||||
}
|
||||
} else if (command == vamp_protocol::page_command::NEXT) {
|
||||
state.selected_main_menu_index = wrap_next(state.selected_main_menu_index, main_menu_count);
|
||||
} else if (command == vamp_protocol::page_command::PREVIOUS) {
|
||||
state.selected_main_menu_index = wrap_previous(state.selected_main_menu_index, main_menu_count);
|
||||
} else if (state.selected_main_menu_index == 0U) {
|
||||
} else if (state.selected_main_menu_index == main_menu_performance) {
|
||||
state.mode = vamp_zero::app_mode::PERFORMANCE_SONG_MENU;
|
||||
state.selected_song_index = 0;
|
||||
state.selected_part_index = 0;
|
||||
state.active_part_id.clear();
|
||||
state.page_index = 0;
|
||||
} else {
|
||||
} else if (state.selected_main_menu_index == main_menu_practice) {
|
||||
state.mode = vamp_zero::app_mode::PRACTICE_TYPE_MENU;
|
||||
state.selected_practice_type_index = 0;
|
||||
state.selected_practice_lesson_index = 0;
|
||||
@@ -309,6 +359,12 @@ auto vamp_zero::apply_app_command(vamp_zero::app_state state, vamp_protocol::pag
|
||||
state.selected_practice_key_index = 0;
|
||||
state.active_practice_id.clear();
|
||||
state.page_index = 0;
|
||||
} else if (state.selected_main_menu_index == main_menu_track) {
|
||||
state.audio.track_enabled = !state.audio.track_enabled;
|
||||
} else if (state.selected_main_menu_index == main_menu_metronome) {
|
||||
state.audio.metronome_enabled = !state.audio.metronome_enabled;
|
||||
} else if (state.selected_main_menu_index == main_menu_tempo) {
|
||||
state.audio.tempo_editing = true;
|
||||
}
|
||||
return clamp_state(std::move(state));
|
||||
|
||||
@@ -523,6 +579,7 @@ auto vamp_zero::app_status_json(vamp_zero::app_state const& state) -> boost::jso
|
||||
root["practice"] = practice_json(state);
|
||||
root["selection"] = std::move(selection);
|
||||
root["active"] = std::move(active);
|
||||
root["audio"] = audio_json(state);
|
||||
root["commands"] = command_json(state);
|
||||
root["remote"] = std::move(remote);
|
||||
return root;
|
||||
@@ -567,6 +624,21 @@ auto vamp_zero::validate_app_state(vamp_zero::app_state const& state) -> std::ex
|
||||
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"));
|
||||
}
|
||||
if (state.audio.metronome_min_bpm == 0U || state.audio.metronome_min_bpm > 400U) {
|
||||
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "metronome minimum BPM is out of range"));
|
||||
}
|
||||
if (state.audio.metronome_max_bpm < state.audio.metronome_min_bpm || state.audio.metronome_max_bpm > 400U) {
|
||||
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "metronome maximum BPM is out of range"));
|
||||
}
|
||||
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) {
|
||||
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) {
|
||||
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.performance.groups.size())) {
|
||||
return std::unexpected(vamp_zero::make_error(vamp_zero::error_code::INVALID_ARGUMENT, "selected performance song row is out of range"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user