C API
Full example showing the complete workflow — embedding audio and then detecting the watermark.
#include <vigil/c_api.h>
// --- Embedder ---
vigil_embedder_config_t cfg = {
.model_path = "models/embedder.enc",
.sample_rate = 48000,
.hop_length = 512,
.step_samples = 512,
.intra_op_threads = 4,
.inter_op_threads = 4,
.enable_memory_arena = 1,
};
vigil_embedder_t emb;
vigil_embedder_create(&cfg, &emb);
vigil_embedder_load(emb);
float output[512];
size_t out_count;
vigil_embedder_step(emb, input_samples, 512, output, &out_count);
vigil_embedder_reset(emb); // Call before a new stream
vigil_embedder_destroy(emb);
// --- Detector ---
vigil_detector_config_t dcfg = {
.model_path = "models/detector.enc",
.sample_rate = 48000,
.hop_length = 512,
.confidence_a = 150.0f,
.confidence_threshold = 0.75f,
.intra_op_threads = 4,
.inter_op_threads = 4,
.enable_memory_arena = 1,
};
vigil_detector_t det;
vigil_detector_create(&dcfg, &det);
vigil_detector_load(det);
vigil_chunk_result_t chunk_result;
vigil_detector_detect_chunk(det, samples, chunk_size, &chunk_result);
vigil_detect_result_t full_result;
vigil_detector_detect(det, audio_buffer, num_samples, &full_result, 1);
vigil_detector_destroy(det);Streaming Embedding
Real-time pipeline for embedding a watermark into a streaming audio signal.
#include <vigil/vigil.h>
#include <vector>
void embed_stream(const std::vector<float>& signal, std::vector<float>& output) {
vigil::EmbedderConfig cfg;
cfg.model_path = "models/embedder.enc";
vigil::StreamingEmbedder embedder(cfg);
embedder.Load();
const size_t step = static_cast<size_t>(embedder.step_samples());
std::vector<float> step_out(step);
output.reserve(signal.size());
size_t pos = 0;
while (pos + step <= signal.size()) {
size_t out_count = 0;
embedder.EmbedStep(signal.data() + pos, step,
step_out.data(), &out_count);
output.insert(output.end(),
step_out.begin(), step_out.begin() + out_count);
pos += step;
}
// Handle remaining samples — zero-pad to step size
size_t remaining = signal.size() - pos;
if (remaining > 0) {
std::vector<float> padded(step, 0.0f);
std::copy(signal.data() + pos,
signal.data() + pos + remaining, padded.data());
size_t out_count = 0;
embedder.EmbedStep(padded.data(), step, step_out.data(), &out_count);
output.insert(output.end(),
step_out.begin(),
step_out.begin() + std::min(out_count, remaining));
}
}Offline Detection
Detect a watermark across an entire audio file in one pass.
#include <vigil/vigil.h>
#include <iostream>
void detect_file(const float* audio, size_t num_samples) {
vigil::DetectorConfig cfg;
cfg.model_path = "models/detector.enc";
vigil::Detector detector(cfg);
detector.Load();
vigil::DetectResult result;
detector.Detect(audio, num_samples, result, true);
std::cout << "Decision: "
<< (result.decision ? "WATERMARKED" : "CLEAN") << "\n";
std::cout << "Mean logit: " << result.mean_logit << "\n";
std::cout << "Confident frames: " << result.num_confident_frames
<< "/" << result.total_frames << "\n";
for (int i = 0; i < result.total_frames; ++i) {
std::cout << " [" << i << "] logit=" << result.per_frame[i].logit
<< " conf=" << result.per_frame[i].confidence << "\n";
}
}Multi-Stream Embedding
Reuse a single embedder across multiple audio streams by calling Reset between streams.
vigil::EmbedderConfig cfg;
cfg.model_path = "models/embedder.enc";
vigil::StreamingEmbedder embedder(cfg);
embedder.Load();
// Process stream 1
for (/* audio blocks from stream 1 */) {
embedder.EmbedStep(input, 512, output, &out_count);
// ... use output ...
}
// IMPORTANT: reset before starting a new stream
embedder.Reset();
// Process stream 2
for (/* audio blocks from stream 2 */) {
embedder.EmbedStep(input, 512, output, &out_count);
// ... use output ...
}Real-Time Detection
Sliding-window detection on a live audio stream as chunks arrive.
vigil::DetectorConfig cfg;
cfg.model_path = "models/detector.enc";
vigil::Detector detector(cfg);
detector.Load();
const size_t chunk = static_cast<size_t>(detector.chunk_size());
std::vector<float> buffer;
// Accumulate incoming audio
while (has_more_audio) {
// Append new samples to buffer...
if (buffer.size() >= chunk) {
vigil::DetectChunkResult result;
detector.DetectChunk(buffer.data(), chunk, result);
if (result.confidence >= 0.75f) {
bool is_watermarked = result.logit > 0.0f;
// Act on detection result...
}
// Slide the window forward
buffer.erase(buffer.begin(), buffer.begin() + chunk);
}
}