Init
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
add_executable(sonus-test
|
||||
test.cpp
|
||||
)
|
||||
find_package(Threads REQUIRED)
|
||||
target_include_directories(sonus-test PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
target_link_libraries(sonus-test PRIVATE
|
||||
sonus::sonus
|
||||
Threads::Threads
|
||||
)
|
||||
target_compile_features(sonus-test PRIVATE cxx_std_23)
|
||||
add_test(NAME sonus COMMAND sonus-test)
|
||||
set_tests_properties(sonus PROPERTIES TIMEOUT 20)
|
||||
add_custom_target(check
|
||||
COMMAND ${CMAKE_CTEST_COMMAND}
|
||||
--test-dir ${CMAKE_BINARY_DIR}
|
||||
-R ^sonus$
|
||||
--output-on-failure
|
||||
--timeout 20
|
||||
USES_TERMINAL
|
||||
)
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
#include <test.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <sonus.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
auto no_device_config() -> sonus::audio_config {
|
||||
auto config = sonus::audio_config{};
|
||||
config.no_device = true;
|
||||
return config;
|
||||
}
|
||||
|
||||
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 const begin = begin_frame * channels;
|
||||
auto const end = std::min(samples.size(), begin + frame_count * channels);
|
||||
for (auto index = begin; index < end; ++index) {
|
||||
result = std::max(result, std::abs(samples[index]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sonus_validates_configs) {
|
||||
auto audio = sonus::audio_config{};
|
||||
BOOST_TEST(static_cast<bool>(sonus::validate(audio)));
|
||||
|
||||
audio.sample_rate = 0;
|
||||
BOOST_TEST(!sonus::validate(audio));
|
||||
audio = {};
|
||||
audio.channels = 0;
|
||||
BOOST_TEST(!sonus::validate(audio));
|
||||
audio = {};
|
||||
audio.master_volume = 1.5F;
|
||||
BOOST_TEST(!sonus::validate(audio));
|
||||
|
||||
auto metronome = sonus::metronome_config{};
|
||||
BOOST_TEST(static_cast<bool>(sonus::validate(metronome)));
|
||||
metronome.bpm = 0;
|
||||
BOOST_TEST(!sonus::validate(metronome));
|
||||
metronome = {};
|
||||
metronome.beats_per_bar = 0;
|
||||
BOOST_TEST(!sonus::validate(metronome));
|
||||
metronome = {};
|
||||
metronome.volume = -0.1F;
|
||||
BOOST_TEST(!sonus::validate(metronome));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sonus_no_device_engine_renders_silence_when_idle) {
|
||||
auto created = sonus::engine::create(no_device_config());
|
||||
BOOST_REQUIRE(created);
|
||||
|
||||
auto samples = std::vector<std::float32_t>(2048U * created->channels(), 1.0F);
|
||||
auto rendered = created->render(samples.data(), 2048U);
|
||||
BOOST_REQUIRE(rendered);
|
||||
|
||||
BOOST_TEST(std::all_of(samples.begin(), samples.end(), [](auto sample) {
|
||||
return sample == 0.0F;
|
||||
}));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sonus_metronome_renders_accented_clicks) {
|
||||
auto config = no_device_config();
|
||||
config.sample_rate = 48000;
|
||||
config.channels = 2;
|
||||
auto created = sonus::engine::create(config);
|
||||
BOOST_REQUIRE(created);
|
||||
|
||||
auto metro = sonus::metronome_config{};
|
||||
metro.bpm = 120;
|
||||
metro.beats_per_bar = 4;
|
||||
metro.volume = 0.70F;
|
||||
metro.click_duration = std::chrono::milliseconds{25};
|
||||
BOOST_REQUIRE(created->start_metronome(metro));
|
||||
|
||||
auto samples = std::vector<std::float32_t>(config.sample_rate * config.channels);
|
||||
BOOST_REQUIRE(created->render(samples.data(), config.sample_rate));
|
||||
|
||||
auto downbeat_peak = peak(samples, 0, 2400, config.channels);
|
||||
auto beat_peak = peak(samples, config.sample_rate / 2, 2400, config.channels);
|
||||
auto between_peak = peak(samples, config.sample_rate / 4, 2400, config.channels);
|
||||
|
||||
BOOST_TEST(downbeat_peak > 0.2F);
|
||||
BOOST_TEST(beat_peak > 0.1F);
|
||||
BOOST_TEST(downbeat_peak > beat_peak * 1.2F);
|
||||
BOOST_TEST(between_peak < 0.05F);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sonus_metronome_can_be_updated_concurrently) {
|
||||
auto created = sonus::engine::create(no_device_config());
|
||||
BOOST_REQUIRE(created);
|
||||
|
||||
auto worker = [&created](std::uint32_t offset) {
|
||||
for (auto index = std::uint32_t{}; index < 100U; ++index) {
|
||||
auto metro = sonus::metronome_config{};
|
||||
metro.bpm = 80U + ((index + offset) % 80U);
|
||||
static_cast<void>(created->start_metronome(metro));
|
||||
if (index % 7U == 0U) {
|
||||
created->stop_metronome();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto first = std::thread{worker, 0U};
|
||||
auto second = std::thread{worker, 11U};
|
||||
auto third = std::thread{worker, 23U};
|
||||
first.join();
|
||||
second.join();
|
||||
third.join();
|
||||
|
||||
auto samples = std::vector<std::float32_t>(4096U * created->channels());
|
||||
BOOST_TEST(static_cast<bool>(created->render(samples.data(), 4096U)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sonus_file_playback_reports_missing_files) {
|
||||
auto created = sonus::engine::create(no_device_config());
|
||||
BOOST_REQUIRE(created);
|
||||
|
||||
auto missing = std::filesystem::path{"/tmp/sonus-missing-file.wav"};
|
||||
auto played = created->play_file(missing);
|
||||
BOOST_TEST(!played);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#define BOOST_TEST_MODULE sonus
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
Reference in New Issue
Block a user