voice_audio_multi/tests/soulx_flashhead/session_test.cpp
Fabio c36510c21e
Some checks failed
Linux Build / Linux cpu (push) Has been cancelled
Linux Build / Linux cpu-clang (push) Has been cancelled
Linux Build / Linux vulkan (push) Has been cancelled
mac-build / macOS CPU (push) Has been cancelled
Nix Build / Nix (cpu) (push) Has been cancelled
Nix Build / Nix (vulkan) (push) Has been cancelled
windows-build / Windows CPU (push) Has been cancelled
Initial import: audio.cpp with Orange Pi documentation
2026-11-04 19:05:22 +01:00

116 lines
6.1 KiB
C++

#include "engine/framework/runtime/registry.h"
#include "engine/framework/debug/trace.h"
#include "engine/framework/debug/profiler.h"
#include "engine/framework/audio/wav_reader.h"
#include <iostream>
using namespace engine;
int main(int argc, char ** argv) {
try {
if (argc != 6)
throw std::runtime_error("Usage: flashhead_session_test MODEL PORTRAIT AUDIO BACKEND LOG");
debug::configure_logging({true, argv[5]});
auto registry = runtime::make_default_registry();
runtime::ModelLoadRequest load;
load.model_path = argv[1];
load.family_hint = "soulx_flashhead";
auto model = registry.load(load);
runtime::SessionOptions options;
const std::string backend = argv[4];
if (backend == "cuda")
options.backend.type = core::BackendType::Cuda;
else if (backend == "vulkan")
options.backend.type = core::BackendType::Vulkan;
else if (backend == "cpu")
options.backend.type = core::BackendType::Cpu;
else
throw std::runtime_error("Unknown backend");
options.backend.threads = 8;
auto base =
model->create_task_session({runtime::VoiceTaskKind::AudioGeneration, runtime::RunMode::Streaming}, options);
auto * session = dynamic_cast<runtime::IStreamingVoiceTaskSession *>(base.get());
if (!session)
throw std::runtime_error("Missing streaming session");
base->prepare({});
runtime::TaskRequest request;
request.options = {{"reference_image_file", argv[2]}, {"height", "256"}, {"width", "256"}, {"seed", "42"}};
const auto wav = audio::read_wav_f32(std::filesystem::path(argv[3]));
if (wav.sample_rate != 16000 || wav.channels != 1 || wav.samples.size() < 16000)
throw std::runtime_error("Test requires at least one second of mono 16 kHz audio");
const auto run = [&](size_t count, size_t block, bool callback) {
std::vector<runtime::VoiceArtifact> output;
session->set_stream_event_sink(callback ? runtime::StreamEventCallback([&](const auto & event) {
output.insert(output.end(), event.output_artifacts.begin(), event.output_artifacts.end());
})
: runtime::StreamEventCallback{});
session->start_stream(request);
for (size_t begin = 0; begin < count; begin += block) {
const auto end = std::min(count, begin + block);
auto event = session->process_audio_chunk(
{16000, 1, static_cast<int64_t>(begin), {wav.samples.begin() + begin, wav.samples.begin() + end}});
output.insert(output.end(), event.output_artifacts.begin(), event.output_artifacts.end());
}
auto result = session->finalize();
output.insert(output.end(), result.output_artifacts.begin(), result.output_artifacts.end());
if (output.size() != (count + 15359) / 15360)
throw std::runtime_error("Missing or duplicate video chunks");
const auto height = std::stoll(request.options.at("height"));
const auto width = std::stoll(request.options.at("width"));
for (const auto & item : output)
if (item.payload.size() != static_cast<size_t>(24 * height * width * 3))
throw std::runtime_error("Invalid RGB chunk size");
return output;
};
const auto equal = [](const auto & a, const auto & b) {
if (a.size() != b.size())
throw std::runtime_error("Output chunk count changed");
for (size_t i = 0; i < a.size(); ++i)
if (a[i].payload != b[i].payload)
throw std::runtime_error("Repeated/fragmented request changed output");
};
const auto first = run(16000, 15360, true);
equal(first, run(16000, 997, false));
(void)run(4000, 4000, true);
equal(first, run(16000, 16000, true));
request.options["height"] = request.options["width"] = "512";
(void)run(16000, 16000, true);
request.options["height"] = request.options["width"] = "256";
equal(first, run(16000, 16000, true));
session->set_stream_event_sink({});
auto * synchronous = dynamic_cast<runtime::IOfflineVoiceTaskSession *>(base.get());
if (!synchronous)
throw std::runtime_error("Missing synchronous run interface");
request.audio_input = runtime::AudioBuffer{16000, 1, {wav.samples.begin(), wav.samples.begin() + 16000}};
equal(first, synchronous->run(request).output_artifacts);
equal(first, run(16000, 997, false));
session->start_stream(request);
bool empty_rejected = false;
try {
(void)session->finalize();
} catch (const std::exception &) {
empty_rejected = true;
}
if (!empty_rejected)
throw std::runtime_error("Empty stream accepted");
base.reset();
auto offline_base =
model->create_task_session({runtime::VoiceTaskKind::AudioGeneration, runtime::RunMode::Offline}, options);
auto * offline = dynamic_cast<runtime::IOfflineVoiceTaskSession *>(offline_base.get());
if (!offline)
throw std::runtime_error("Missing offline session");
offline_base->prepare({});
request.audio_input = runtime::AudioBuffer{16000, 1, {wav.samples.begin(), wav.samples.begin() + 16000}};
equal(first, offline->run(request).output_artifacts);
request.options["seed"] = "43";
const auto changed = offline->run(request).output_artifacts;
if (changed.front().payload == first.front().payload)
throw std::runtime_error("Seed did not affect generation");
std::cout << "PASS: repeated requests, long-short-long, shape switching, fragmented streaming, padded final chunk, callback and "
"pull delivery, offline equivalence, seed control, empty-stream rejection\n";
return 0;
} catch (const std::exception & error) {
std::cerr << error.what() << '\n';
return 1;
}
}