Deepmark

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);
    }
}