+94
-1
@@ -30,7 +30,7 @@ auto test_file(std::string_view name) -> std::filesystem::path {
|
||||
}
|
||||
|
||||
auto peak(std::vector<std::float32_t> const& samples, std::size_t begin_frame, std::size_t frame_count, std::size_t channels) -> std::float32_t {
|
||||
auto result = 0.0F;
|
||||
auto result = std::float32_t{};
|
||||
auto const begin = begin_frame * channels;
|
||||
auto const end = std::min(samples.size(), begin + frame_count * channels);
|
||||
for (auto index = begin; index < end; ++index) {
|
||||
@@ -126,6 +126,14 @@ BOOST_AUTO_TEST_CASE(sonus_validates_configs) {
|
||||
audio.playback_device_name = "default";
|
||||
BOOST_TEST(!sonus::validate(audio));
|
||||
audio = {};
|
||||
audio.no_device = true;
|
||||
audio.playback_device_id = "0:1:default";
|
||||
BOOST_TEST(!sonus::validate(audio));
|
||||
BOOST_TEST(!sonus::engine::create(audio));
|
||||
audio = {};
|
||||
audio.playback_device_id.assign("a\0b", 3);
|
||||
BOOST_TEST(!sonus::validate(audio));
|
||||
audio = {};
|
||||
audio.playback_device_name.assign("a\0b", 3);
|
||||
BOOST_TEST(!sonus::validate(audio));
|
||||
|
||||
@@ -141,6 +149,26 @@ BOOST_AUTO_TEST_CASE(sonus_validates_configs) {
|
||||
BOOST_TEST(!sonus::validate(metronome));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sonus_playback_devices_can_be_enumerated) {
|
||||
auto enumerated = sonus::enumerate_playback_devices();
|
||||
if (!enumerated) {
|
||||
BOOST_TEST(!enumerated.error().message().empty());
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto const& device : *enumerated) {
|
||||
BOOST_TEST(!device.id.empty());
|
||||
BOOST_TEST(device.id.find('\0') == std::string::npos);
|
||||
BOOST_TEST(device.name.find('\0') == std::string::npos);
|
||||
}
|
||||
|
||||
if (!enumerated->empty()) {
|
||||
auto config = sonus::audio_config{};
|
||||
config.playback_device_id = enumerated->front().id;
|
||||
BOOST_TEST(static_cast<bool>(sonus::validate(config)));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sonus_no_device_engine_renders_silence_when_idle) {
|
||||
auto created = sonus::engine::create(no_device_config());
|
||||
BOOST_REQUIRE(created);
|
||||
@@ -154,6 +182,40 @@ BOOST_AUTO_TEST_CASE(sonus_no_device_engine_renders_silence_when_idle) {
|
||||
}));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sonus_master_volume_can_be_changed_concurrently) {
|
||||
auto created = sonus::engine::create(no_device_config());
|
||||
BOOST_REQUIRE(created);
|
||||
|
||||
auto failed = std::atomic_bool{false};
|
||||
auto volume_worker = [&created, &failed](std::uint32_t offset) {
|
||||
for (auto index = std::uint32_t{}; index < 250U; ++index) {
|
||||
auto const volume = static_cast<std::float32_t>(((index + offset) % 101U)) / 100.0F;
|
||||
if (!created->set_master_volume(volume)) {
|
||||
failed.store(true, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
};
|
||||
auto render_worker = [&created, &failed] {
|
||||
auto samples = std::vector<std::float32_t>(256U * created->channels());
|
||||
for (auto index = std::uint32_t{}; index < 250U; ++index) {
|
||||
if (!created->render(samples.data(), 256U) || !created->update_metronome({})) {
|
||||
failed.store(true, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto first = std::thread{volume_worker, 0U};
|
||||
auto second = std::thread{volume_worker, 37U};
|
||||
auto renderer = std::thread{render_worker};
|
||||
first.join();
|
||||
second.join();
|
||||
renderer.join();
|
||||
|
||||
BOOST_TEST(!failed.load(std::memory_order_acquire));
|
||||
BOOST_TEST(!created->set_master_volume(-0.01F));
|
||||
BOOST_TEST(!created->set_master_volume(1.01F));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sonus_metronome_renders_accented_clicks) {
|
||||
auto config = no_device_config();
|
||||
config.sample_rate = 48000;
|
||||
@@ -197,6 +259,36 @@ BOOST_AUTO_TEST_CASE(sonus_metronome_update_keeps_inactive_metronome_stopped) {
|
||||
BOOST_TEST(created->metronome_active());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sonus_stopped_metronome_updates_stay_silent_and_restart) {
|
||||
auto config = no_device_config();
|
||||
config.sample_rate = 48000;
|
||||
config.channels = 2;
|
||||
auto created = sonus::engine::create(config);
|
||||
BOOST_REQUIRE(created);
|
||||
|
||||
for (auto index = std::uint32_t{}; index < 500U; ++index) {
|
||||
auto metro = sonus::metronome_config{};
|
||||
metro.bpm = 60U + (index % 180U);
|
||||
metro.beats_per_bar = 1U + (index % 8U);
|
||||
metro.click_duration = std::chrono::milliseconds{1 + static_cast<int>(index % 40U)};
|
||||
BOOST_REQUIRE(created->update_metronome(metro));
|
||||
BOOST_TEST(!created->metronome_active());
|
||||
}
|
||||
|
||||
auto samples = std::vector<std::float32_t>(4096U * config.channels, 1.0F);
|
||||
BOOST_REQUIRE(created->render(samples.data(), 4096U));
|
||||
BOOST_TEST(std::all_of(samples.begin(), samples.end(), [](auto sample) {
|
||||
return sample == 0.0F;
|
||||
}));
|
||||
|
||||
auto metro = sonus::metronome_config{};
|
||||
metro.bpm = 120;
|
||||
BOOST_REQUIRE(created->start_metronome(metro));
|
||||
BOOST_TEST(created->metronome_active());
|
||||
BOOST_REQUIRE(created->render(samples.data(), 4096U));
|
||||
BOOST_TEST(peak(samples, 0, 4096U, config.channels) > 0.1F);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sonus_metronome_can_be_updated_concurrently) {
|
||||
auto created = sonus::engine::create(no_device_config());
|
||||
BOOST_REQUIRE(created);
|
||||
@@ -206,6 +298,7 @@ BOOST_AUTO_TEST_CASE(sonus_metronome_can_be_updated_concurrently) {
|
||||
auto metro = sonus::metronome_config{};
|
||||
metro.bpm = 80U + ((index + offset) % 80U);
|
||||
static_cast<void>(created->start_metronome(metro));
|
||||
static_cast<void>(created->update_metronome(metro));
|
||||
if (index % 7U == 0U) {
|
||||
created->stop_metronome();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user