Files
product-vamp/tests/test_playback.cpp
T
khodowany 1be98e14aa
ci / checks (push) Successful in 1h15m13s
Update
2026-09-24 06:20:37 -05:00

134 lines
5.8 KiB
C++

#include <boost/test/unit_test.hpp>
#include <boost/json.hpp>
#include <array>
#include <fstream>
#include <iterator>
#include <thread>
#include <vamp_zero/audio.hpp>
#include <vamp_zero/config.hpp>
#include <vamp_zero/playback.hpp>
#include <vamp_zero/portal.hpp>
namespace {
auto playback_score() -> vamp_zero::score_document {
auto input = std::ifstream{std::string{VAMP_TEST_PLAYBACK_ASSETS_DIR} + "/score.json"};
auto score = vamp_zero::score_document{};
score.source_json = std::string{std::istreambuf_iterator<std::char_t>{input}, std::istreambuf_iterator<std::char_t>{}};
return score;
}
auto performance_state() -> vamp_zero::app_state {
auto catalog = std::make_shared<vamp_zero::catalog>();
catalog->performance.parts.push_back({.id = "part", .title = "Song", .part = "Vocal", .total_pages = 2U});
catalog->performance.groups.push_back({.title = "Song", .parts = {0U}});
auto state = vamp_zero::app_state{};
state.catalog = std::move(catalog);
state.mode = vamp_zero::app_mode::PERFORMANCE_SCORE;
state.selected_song_index = 1U;
state.selected_part_index = 1U;
state.active_part_id = "part";
state.score_session = 42U;
state.audio.playback_enabled = true;
return state;
}
}
BOOST_AUTO_TEST_CASE(generated_audio_plays_at_menu_bpm) {
auto score = playback_score();
auto normal = vamp_zero::prepare_playback(score, VAMP_TEST_PLAYBACK_ASSETS_DIR, 120U);
auto slow = vamp_zero::prepare_playback(score, VAMP_TEST_PLAYBACK_ASSETS_DIR, 60U);
BOOST_REQUIRE_MESSAGE(normal, (normal ? "" : normal.error().describe()));
BOOST_REQUIRE(slow);
BOOST_TEST(normal->duration_frames == 96000U);
BOOST_TEST(slow->duration_frames == normal->duration_frames * 2U);
BOOST_TEST(normal->samples[0].frames == slow->samples[0].frames, boost::test_tools::per_element());
auto engine = sonus::engine::create({.sample_rate = 48000U, .channels = 1U, .no_device = true});
BOOST_REQUIRE(engine);
auto session = engine->play_sequence(std::move(*normal));
BOOST_REQUIRE(session);
auto output = std::array<std::float32_t, 480>{};
auto energy = std::float32_t{};
for (auto block = std::uint32_t{}; block < 200U; ++block) {
BOOST_REQUIRE(engine->render(output.data(), output.size()));
for (auto value : output) {
energy += value * value;
}
if (block == 150U) {
BOOST_CHECK(session->status() == sonus::playback_status::PLAYING);
}
}
BOOST_TEST(energy > 0.1F);
BOOST_CHECK(session->status() == sonus::playback_status::COMPLETED);
BOOST_REQUIRE(vamp_zero::validate_playback_assets(score, VAMP_TEST_PLAYBACK_ASSETS_DIR));
}
BOOST_AUTO_TEST_CASE(playback_completion_is_session_guarded) {
auto state = vamp_zero::control_state{{}, performance_state()};
state.finish_playback(41U);
BOOST_CHECK(state.snapshot().mode == vamp_zero::app_mode::PERFORMANCE_SCORE);
state.finish_playback(42U, {}, std::make_shared<vamp_zero::catalog const>());
BOOST_CHECK(state.snapshot().mode == vamp_zero::app_mode::PERFORMANCE_SCORE);
state.finish_playback(42U, "sample missing");
BOOST_CHECK(state.snapshot().mode == vamp_zero::app_mode::PERFORMANCE_SCORE);
BOOST_TEST(state.snapshot().audio.playback_error == "sample missing");
state.finish_playback(42U);
BOOST_CHECK(state.snapshot().mode == vamp_zero::app_mode::PERFORMANCE_PART_MENU);
BOOST_TEST(state.snapshot().selected_part_index == 1U);
BOOST_TEST(state.snapshot().select_count == 0U);
state.apply(vamp_protocol::page_command::SELECT);
BOOST_TEST(state.snapshot().score_session == 43U);
state.finish_playback(42U);
BOOST_CHECK(state.snapshot().mode == vamp_zero::app_mode::PERFORMANCE_SCORE);
auto disabled = performance_state();
disabled.audio.playback_enabled = false;
auto quiet = vamp_zero::control_state{{}, disabled};
quiet.finish_playback(42U);
BOOST_CHECK(quiet.snapshot().mode == vamp_zero::app_mode::PERFORMANCE_SCORE);
}
BOOST_AUTO_TEST_CASE(playback_worker_discards_canceled_preparation_and_reports_errors_once) {
auto engine = sonus::engine::create({.no_device = true});
BOOST_REQUIRE(engine);
auto audio = vamp_zero::audio_controller{{}, std::move(*engine)};
auto state = performance_state();
auto invalid = std::make_shared<vamp_zero::score_document>();
invalid->id = "part";
invalid->source_json = "{}";
auto loaded = vamp_zero::score_load_state{.document = invalid};
BOOST_CHECK(!audio.tick(state, loaded, VAMP_TEST_PLAYBACK_ASSETS_DIR));
state.mode = vamp_zero::app_mode::PERFORMANCE_PART_MENU;
for (auto attempt = std::uint32_t{}; attempt < 100U; ++attempt) {
BOOST_CHECK(!audio.tick(state, {}, VAMP_TEST_PLAYBACK_ASSETS_DIR));
std::this_thread::sleep_for(std::chrono::milliseconds{1});
}
state.mode = vamp_zero::app_mode::PERFORMANCE_SCORE;
++state.score_session;
auto reported = std::bool_t{};
for (auto attempt = std::uint32_t{}; attempt < 1000U && !reported; ++attempt) {
if (auto result = audio.tick(state, loaded, VAMP_TEST_PLAYBACK_ASSETS_DIR)) {
BOOST_CHECK(!*result);
reported = true;
}
std::this_thread::sleep_for(std::chrono::milliseconds{1});
}
BOOST_TEST(reported);
BOOST_CHECK(!audio.tick(state, loaded, VAMP_TEST_PLAYBACK_ASSETS_DIR));
}
BOOST_AUTO_TEST_CASE(playback_legacy_config_and_invalid_assets) {
auto legacy = vamp_zero::parse_config(boost::json::object{{"audio", {{"track_enabled", true}}}});
BOOST_REQUIRE(legacy);
BOOST_TEST(legacy->audio.playback_enabled);
BOOST_CHECK(!vamp_zero::parse_config(boost::json::object{{"audio", {{"track_enabled", true}, {"playback_enabled", false}}}}));
auto score = playback_score();
BOOST_CHECK(!vamp_zero::prepare_playback(score, "/missing-vamp-assets", 120U));
BOOST_CHECK(!vamp_zero::prepare_playback(score, VAMP_TEST_PLAYBACK_ASSETS_DIR, 0U));
score.source_json = "{}";
BOOST_CHECK(!vamp_zero::prepare_playback(score, VAMP_TEST_PLAYBACK_ASSETS_DIR, 120U));
}