#include #include #include #include namespace { auto fixture() -> sonus::sample_sequence { return {.samples = {{.sample_rate = 48000U, .frames = {0.1F, 0.2F, 0.3F, 0.4F}, .loop_start = 2U, .loop_end = 4U}}, .events = {{.sample = 0U, .start_frame = 2U, .duration_frames = 6U}}, .release_envelope = {1.0F, 0.5F, 0.0F}, .duration_frames = 12U, .max_voices = 2U, .gain = 1.0F}; } } BOOST_AUTO_TEST_CASE(sequence_sample_clock_and_completion) { auto engine = sonus::engine::create({.sample_rate = 48000U, .channels = 1U, .master_volume = 1.0F, .no_device = true}); BOOST_REQUIRE(engine); auto session = engine->play_sequence(fixture()); BOOST_REQUIRE(session); auto output = std::array{}; BOOST_REQUIRE(engine->render(output.data(), output.size())); auto expected = std::array{0.0F, 0.0F, 0.1F, 0.2F, 0.3F, 0.4F, 0.3F, 0.4F, 0.3F, 0.2F, 0.0F, 0.0F}; BOOST_TEST(output == expected, boost::test_tools::per_element()); BOOST_CHECK(session->status() == sonus::playback_status::COMPLETED); engine->collect_finished(); BOOST_CHECK(session->status() == sonus::playback_status::COMPLETED); } BOOST_AUTO_TEST_CASE(sequence_cancel_replace_and_independent_unisons) { auto engine = sonus::engine::create({.channels = 1U, .master_volume = 1.0F, .no_device = true}); BOOST_REQUIRE(engine); auto value = fixture(); value.events.push_back({.sample = 0U, .start_frame = 2U, .duration_frames = 2U}); auto first = engine->play_sequence(value); BOOST_REQUIRE(first); auto output = std::array{}; BOOST_REQUIRE(engine->render(output.data(), output.size())); BOOST_TEST(output[2] == 0.2F); auto second = engine->play_sequence(value); BOOST_REQUIRE(second); BOOST_CHECK(first->status() == sonus::playback_status::STOPPED); second->stop(); BOOST_REQUIRE(engine->render(output.data(), output.size())); BOOST_TEST(output[2] == 0.0F); BOOST_CHECK(second->status() == sonus::playback_status::STOPPED); value.max_voices = 1U; BOOST_CHECK(!engine->play_sequence(value)); } BOOST_AUTO_TEST_CASE(sequence_concurrent_control) { auto engine = sonus::engine::create({.channels = 1U, .no_device = true}); BOOST_REQUIRE(engine); auto render = std::thread{[&] { auto output = std::array{}; for (auto i = std::uint32_t{}; i < 1000U; ++i) { static_cast(engine->render(output.data(), output.size())); } }}; for (auto i = std::uint32_t{}; i < 1000U; ++i) { auto session = engine->play_sequence(fixture()); if (session) { session->stop(); } engine->collect_finished(); } render.join(); } BOOST_AUTO_TEST_CASE(sequence_preparation_is_single_use_and_rate_checked) { auto prepared = sonus::prepared_sequence::create(fixture(), 48000U); BOOST_REQUIRE(prepared); auto engine = sonus::engine::create({.channels = 1U, .master_volume = 1.0F, .no_device = true}); BOOST_REQUIRE(engine); auto session = engine->play_sequence(std::move(*prepared)); BOOST_REQUIRE(session); BOOST_CHECK(!engine->play_sequence(std::move(*prepared))); auto output = std::array{}; for (auto i = std::size_t{}; i < output.size(); ++i) { BOOST_REQUIRE(engine->render(output.data() + i, 1U)); } BOOST_TEST(output[8] == 0.3F); BOOST_CHECK(session->status() == sonus::playback_status::COMPLETED); auto invalid = fixture(); invalid.samples[0].loop_end = 0U; BOOST_CHECK(!sonus::prepared_sequence::create(invalid, 48000U)); BOOST_CHECK(!sonus::prepared_sequence::create(fixture(), 44100U)); auto silent = fixture(); silent.samples.clear(); silent.events.clear(); auto quiet = engine->play_sequence(std::move(silent)); BOOST_REQUIRE(quiet); BOOST_REQUIRE(engine->render(output.data(), output.size())); BOOST_CHECK(quiet->status() == sonus::playback_status::COMPLETED); for (auto value : output) { BOOST_TEST(value == 0.0F); } }