Playback
publish / publish (push) Successful in 13m11s

This commit is contained in:
khodowany
2026-10-06 00:04:22 -05:00
parent 8bedccaa02
commit 3aa3a67e5d
9 changed files with 419 additions and 0 deletions
+1
View File
@@ -7,6 +7,7 @@ add_executable(sonus_test
test.cpp
render_snapshot.cpp
control.cpp
transport.cpp
sequence.cpp
)
find_package(Threads REQUIRED)
+111
View File
@@ -0,0 +1,111 @@
#include <boost/test/unit_test.hpp>
#include <sonus.hpp>
#include <array>
#include <filesystem>
#include <fstream>
#include <unistd.h>
namespace {
struct recordings {
std::filesystem::path root;
recordings() {
auto path = (std::filesystem::temp_directory_path() / "sonus-recordings-XXXXXX").string();
BOOST_REQUIRE(::mkdtemp(path.data()));
root = path;
write("backing.wav", 16, 1024, -1024);
write("beat1.wav", 2, 8192, -8192);
write("beat2.wav", 2, 4096, -4096);
}
~recordings() {
std::error_code error;
std::filesystem::remove_all(root, error);
}
auto write(char const *name, unsigned frames, int left, int right) -> void {
auto output = std::ofstream{root / name, std::ios::binary};
auto word = [&](unsigned value, unsigned bytes) { for (unsigned i = 0; i < bytes; ++i){ output.put(static_cast<char>(value >> (8 * i)));
} };
output.write("RIFF", 4);
word(36 + frames * 4, 4);
output.write("WAVEfmt ", 8);
word(16, 4);
word(1, 2);
word(2, 2);
word(48000, 4);
word(192000, 4);
word(4, 2);
word(16, 2);
output.write("data", 4);
word(frames * 4, 4);
for (unsigned i = 0; i < frames; ++i) {
word(static_cast<unsigned>(left), 2);
word(static_cast<unsigned>(right), 2);
}
}
auto config() const -> sonus::transport_config {
return {.backing = root / "backing.wav", .beat1 = root / "beat1.wav", .beat2 = root / "beat2.wav", .clicks = {{4, true}, {8, false}}, .duration_frames = 10, .click_volume = 1.F};
}
};
}
BOOST_AUTO_TEST_CASE(recording_transport_preserves_stereo_and_has_one_sample_clock) {
auto files = recordings{};
auto prepared = sonus::prepared_transport::create(files.config(), 48000, 2);
BOOST_REQUIRE(prepared);
auto engine = sonus::engine::create({.sample_rate = 48000, .channels = 2, .master_volume = 1.F, .no_device = true});
BOOST_REQUIRE(engine);
auto session = engine->play_transport(std::move(*prepared));
BOOST_REQUIRE(session);
std::array<float, 40> output{};
BOOST_REQUIRE(engine->render(output.data(), 20));
BOOST_TEST(session->frames() == 16U);
BOOST_CHECK(session->status() == sonus::playback_status::COMPLETED);
for (unsigned frame = 0; frame < 20; ++frame) {
auto value = frame < 16 ? .03125F : 0.F;
if (frame == 4 || frame == 5) {
value += .25F;
}
if (frame == 8 || frame == 9) {
value += .125F;
}
BOOST_TEST(output[frame * 2] == value);
BOOST_TEST(output[frame * 2 + 1] == -value);
}
engine->collect_finished();
BOOST_CHECK(session->status() == sonus::playback_status::COMPLETED);
}
BOOST_AUTO_TEST_CASE(recording_transport_replacement_cancels_old_session_and_validates_inputs) {
auto files = recordings{};
auto engine = sonus::engine::create({.sample_rate = 48000, .channels = 2, .no_device = true});
BOOST_REQUIRE(engine);
auto config = files.config();
auto first = sonus::prepared_transport::create(config, 48000, 2);
BOOST_REQUIRE(first);
auto session = engine->play_transport(std::move(*first));
BOOST_REQUIRE(session);
auto second = sonus::prepared_transport::create(config, 48000, 2);
BOOST_REQUIRE(second);
auto replacement = engine->play_transport(std::move(*second));
BOOST_REQUIRE(replacement);
BOOST_CHECK(session->status() == sonus::playback_status::STOPPED);
replacement->stop();
std::array<float, 16> output{};
BOOST_REQUIRE(engine->render(output.data(), 8));
BOOST_TEST(replacement->frames() == 0U);
BOOST_TEST(output.front() == 0.F);
config.duration_frames = 17;
BOOST_CHECK(!sonus::prepared_transport::create(config, 48000, 2));
config = files.config();
config.beat1 = files.root / "missing.wav";
BOOST_CHECK(!sonus::prepared_transport::create(config, 48000, 2));
config = files.config();
config.clicks = {{9, true}, {4, false}};
BOOST_CHECK(!sonus::prepared_transport::create(config, 48000, 2));
config = files.config();
config.backing.clear();
config.clicks.push_back({10, true});
BOOST_CHECK(!sonus::prepared_transport::create(config, 48000, 2));
}