SignalsDocs
Signals documentation

Build private audio recognition into your product.

Create a catalog from your media, generate a media database, and recognize audio locally on a device—or automate the entire workflow with an API key.

How Signals is organizedOrganization → Project → Media → Media database

What is a project?

A project is an isolated recognition catalog. It contains the media you want to recognize, its tags and metadata, a duration capacity, and the generated media database your application downloads.

Use separate projects when catalogs have different audiences, release schedules, applications, or access requirements. For example, a retail network and a broadcast-monitoring app should usually use different projects.

Media library

Audio and video creatives, normalized and prepared for recognition.

Media database

A versioned catalog built from the project’s current media.

Scoped access

Team roles and API keys can be limited to an organization or project.

Quickstart

  1. 1
    Create an organization

    Sign up, name your organization, and invite anyone who will manage media or releases.

  2. 2
    Create a project

    Choose a clear catalog name such as “Retail campaign” or “Broadcast ads — India.”

  3. 3
    Add media

    Upload the source audio or video files your application needs to recognize.

  4. 4
    Build and download

    Generate the database, then download the catalog and metadata for your application.

Add media

Open a project and select Add media. Choose an audio or video file and add tags that your application can use after a match—for example campaign, brand, market, or creative ID.

Capacity check

If a new file would exceed the project’s available audio capacity, Signals stops the upload and can notify organization administrators.

Upload through the API

cURL
curl -X POST \
  -H "Authorization: Bearer $SIGNALS_API_KEY" \
  -F "[email protected]" \
  https://signals.example.com/api/orgs/acme/projects/:project-slug/media

Generate the media database

On the project page, choose Build media database. Signals creates a new version from the project’s current media. Rebuild after adding, removing, or changing creatives.

The generated database is loaded fully into memory by the runtime matcher. Recognition does not need SQL or a network connection.

cURL
curl -X POST \
  -H "Authorization: Bearer $SIGNALS_API_KEY" \
  https://signals.example.com/api/orgs/acme/projects/:project-slug/offline-db/rebuild

Download the media database

Download the latest .acrf database from the project page. Download the metadata JSON beside it when your application needs filenames and custom tags for returned sound IDs.

cURL
curl -L \
  -H "Authorization: Bearer $SIGNALS_API_KEY" \
  -o offline.acrf \
  https://signals.example.com/api/orgs/acme/projects/:project-slug/offline-db
Keep files together

Ship the database and matching metadata from the same build version. A runtime will reject incompatible database formats instead of silently producing incorrect matches.

Use the library on Android

Place libacr.so in the matching ABI folder under app/src/main/jniLibs/. Add the downloaded database to your app assets, copy it to internal storage, then load it once when your application starts.

Kotlin
object AcrNative {
    external fun nativeLoadDb(path: String): Boolean
    external fun nativeMatch(buffer: ByteArray): String
}

// Load libacr.so and the catalog once at app startup.
ReLinker.loadLibrary(context, "acr")
val catalog = File(context.filesDir, "offline.acrf")
val ready = AcrNative.nativeLoadDb(catalog.absolutePath)

// Match little-endian 16-bit mono PCM captured from AudioRecord.
if (ready) {
    val resultJson = AcrNative.nativeMatch(audioBuffer)
    val result = JSONObject(resultJson)
    println(result.optString("soundID"))
    println(result.optDouble("offsetTime"))
}

Audio input

Pass little-endian, signed 16-bit mono PCM from Android’s AudioRecord. The demo application uses a rolling buffer and evaluates recognition results at regular intervals.

Use the library on Linux

Install the Signals offline tester on the Linux device, place the downloaded media database and matching metadata JSON in one folder, and start local microphone recognition from Python.

Python
from pathlib import Path
import subprocess

# The folder contains one downloaded .acrf media database
# and its matching metadata JSON file.
media_database = Path("/opt/signals/catalog")

process = subprocess.Popen([
    "offline-tester",
    str(media_database),
    "--confidence", "0.05",
])

try:
    process.wait()
except KeyboardInterrupt:
    process.terminate()
    process.wait()

The process prints recognized media with its offset, confidence, filename, and tags. Stop it with Ctrl+C or terminate the child process from your application.

Offline ACR examples

The Python example runs recognition locally against a downloaded catalog. It is useful for validating creative captures before integrating the native matcher into an Android, Linux, or edge application.

Match a recorded capture

Place exactly one downloaded .acrf database and its metadata JSON in the catalog folder, then provide a normalized WAV capture.

PowerShell
# From examples/python
python -m pip install -r requirements.txt

# Match a known 16 kHz mono WAV capture against one downloaded catalog.
python acr_match.py C:\signals\catalog --file C:\captures\query.wav

Recognize from a microphone

Use the device list to select an audio input, then keep the command running to evaluate short rolling capture windows locally.

PowerShell
# List microphone devices, then run continuous local recognition.
python acr_match.py C:\signals\catalog --list-devices
python acr_match.py C:\signals\catalog --device 2

Online catalog automation

Use the authenticated Signals API to operate the catalog workflow from a build system or media pipeline: upload source media, build a new catalog version, and distribute the completed database to recognition runtimes.

  1. 1
    Upload the creative

    Send audio or video with a scoped write key. Signals normalizes and fingerprints it for the project.

  2. 2
    Build the catalog

    Trigger a project build once the release media is ready.

  3. 3
    Download the release

    Fetch the matching database and metadata with a scoped read key, then distribute them to your runtimes.

cURL
curl -X POST \
  -H "Authorization: Bearer $SIGNALS_API_KEY" \
  -F "[email protected]" \
  https://signals.example.com/api/orgs/acme/projects/:project-slug/media
cURL
curl -X POST \
  -H "Authorization: Bearer $SIGNALS_API_KEY" \
  https://signals.example.com/api/orgs/acme/projects/:project-slug/offline-db/rebuild
cURL
curl -L \
  -H "Authorization: Bearer $SIGNALS_API_KEY" \
  -o offline.acrf \
  https://signals.example.com/api/orgs/acme/projects/:project-slug/offline-db

Stream Discovery

Stream Discovery runs directly on a VM and continuously observes a media feed for recurring creative segments. It accepts HTTP(S), HLS, UDP/RTP, RTSP, and local-file sources through FFmpeg.

The first observed segment is retained as a candidate. When the same segment appears again after the configured separation interval, Signals creates a canonical creative, records both occurrences, and saves ACR-ready audio and metadata locally.

Run on Windows

Use the bundled signals-stream-discovery-windows-amd64.exe beside a config.yml file. Install FFmpeg and FFprobe on the VM and make them available on PATH.

PowerShell
.signals-stream-discovery-windows-amd64.exe --config .config.yml

Configure a stream

Configure one or more feeds in config.yml. The service watches this file and applies stream additions, removals, and changed stream definitions without changing unaffected workers.

YAML
storage_dir: ./data
database: ./data/discovery.sqlite3

discovery:
  window_seconds: 3
  hop_seconds: 1
  min_chain_windows: 2
  min_hash_matches: 8
  same_stream_min_separation_seconds: 120

streams:
  - id: channel-1
    enabled: true
    url: udp://239.0.0.1:1234?overrun_nonfatal=1&fifo_size=5000000
    scte35: auto
    retain_video: confirmed
SettingPurpose
scte35auto uses broadcast markers when available; required limits discovery to marked breaks; off observes the full feed.
same_stream_min_separation_secondsPrevents adjacent rolling windows from confirming themselves while allowing later plays on the same channel.
retain_videoChoose never, candidate, or confirmed for source-video retention policy.

Test with a recorded stream

Record at least one hour when possible, then replay the file with -re so the discovery timeline matches the recorded channel timeline. Use a file URL on Windows.

YAML
# Replay a recorded one-hour transport stream at real time.
storage_dir: ./test-data
database: ./test-data/discovery.sqlite3

discovery:
  same_stream_min_separation_seconds: 120

streams:
  - id: channel-test
    enabled: true
    url: file:///C:/captures/channel-1h.ts
    input_args: ["-re"]
    scte35: off
    retain_video: never

Monitor UDP/RTP multicast

Use the stream URL and FFmpeg input options supplied by your broadcast environment. scte35: auto uses SCTE-35 packet markers when the feed exposes them.

YAML
streams:
  - id: regional-tv
    enabled: true
    url: udp://239.0.0.1:1234?overrun_nonfatal=1&fifo_size=5000000
    input_args: []
    scte35: auto
    retain_video: confirmed

Monitor an HLS or HTTP feed

Use the playlist or media URL as the source. HLS playlists translate #EXT-X-CUE-OUT, #EXT-X-CUE-IN, and SCTE-35 #EXT-X-DATERANGE cues into the same ad-break model; DASH feeds use SCTE-style EventStream entries.

YAML
streams:
  - id: news-hls
    enabled: true
    url: https://media.example.com/live/channel.m3u8
    input_args: []
    scte35: auto
    retain_video: candidate

Run more than one stream

Each configured stream has its own worker, candidates, occurrences, and retained media. Add or remove a stream in config.yml; the service reloads the file and applies only the required worker changes.

YAML
streams:
  - id: national-news
    enabled: true
    url: https://media.example.com/news/index.m3u8
    scte35: auto
    retain_video: confirmed

  - id: regional-radio
    enabled: true
    url: http://radio.example.com/live.mp3
    scte35: off
    retain_video: never

Events and local API

Confirmed occurrences are appended to data/events/events.jsonl. Configure an optional webhook to receive metadata-only creative.started and creative.ended events with an idempotency key.

YAML
webhook:
  url: https://operations.example.com/signals/events
  bearer_token: replace-with-a-secret
  retry_seconds: 10

The local API defaults to http://127.0.0.1:8091. Use /health, /streams, /candidates, /creatives, /occurrences, and /events to integrate a local operations view. Candidate review actions are available at POST /candidates/:id/approve and POST /candidates/:id/reject.

PowerShell
# Check the running service.
Invoke-RestMethod http://127.0.0.1:8091/health

# Inspect discovered candidates and confirmed occurrences.
Invoke-RestMethod http://127.0.0.1:8091/candidates
Invoke-RestMethod http://127.0.0.1:8091/occurrences

# Approve or reject a candidate during review.
Invoke-RestMethod -Method Post   http://127.0.0.1:8091/candidates/42/approve   -ContentType "application/json"   -Body '{"note":"Verified campaign creative"}'

Webhook event

Each webhook receives JSON metadata with an Idempotency-Key request header. Persist that key in the receiving system so retries remain safe.

JSON
{
  "id": "f5b7d9f4-…",
  "type": "creative.started",
  "creativeId": 42,
  "streamId": "national-news",
  "startMs": 1737537243000,
  "endMs": 1737537273000,
  "confidence": 0.82,
  "manifest": "data/manifests/creative-national-news-42.json"
}

Evaluation SDK

Request the prebuilt libraries and integration examples for your evaluation. We’ll help you select the package for your target platform and use case.

Request the evaluation SDKWe’ll get back to you within 24 business hours.

Tell us about your integration and target platform. We’ll review the request and share the appropriate evaluation package.

Use the API with an API key

Open Settings → API Keys inside your organization. Choose Generate API key, select organization or project scope, and choose read or write permission.

Copy the key immediately

The complete signals_… secret is shown once. Store it in a secret manager and send it as a Bearer token.

HTTP
Authorization: Bearer signals_your_secret_key
OperationMinimum permission
List media and database informationread
Download database and metadataread
Upload or remove mediawrite
Rebuild the media databasewrite

Download metadata

Metadata maps returned IDs to filenames, durations, and project tags. Download it with the same read key used for the database:

cURL
curl -L \
  -H "Authorization: Bearer $SIGNALS_API_KEY" \
  -o metadata.json \
  https://signals.example.com/api/orgs/acme/projects/:project-slug/metadata

Roles and API-key permissions

Owners and administrators manage the organization. Members can work with projects and generate API keys. Viewers have read-only access. API keys add a second boundary through organization or project scope and read or write permission.

Troubleshooting

The database download returns 404

Build the media database at least once. The download endpoint returns 404 until a completed version exists.

An API request returns 401 or 403

Confirm the Bearer prefix, key scope, permission level, organization slug, and project slug.

Android cannot load the database

Verify the asset was copied to a readable file path and that the runtime and database use compatible formats.

A newly added creative is not recognized

Rebuild the project database and distribute the new database version to the application.

Ready to build?

Create a workspace and generate your first private recognition catalog.

Start building