Audio and video creatives, normalized and prepared for recognition.
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.
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.
A versioned catalog built from the project’s current media.
Team roles and API keys can be limited to an organization or project.
Quickstart
- 1Create an organization
Sign up, name your organization, and invite anyone who will manage media or releases.
- 2Create a project
Choose a clear catalog name such as “Retail campaign” or “Broadcast ads — India.”
- 3Add media
Upload the source audio or video files your application needs to recognize.
- 4Build 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.
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 -X POST \
-H "Authorization: Bearer $SIGNALS_API_KEY" \
-F "[email protected]" \
https://signals.example.com/api/orgs/acme/projects/:project-slug/mediaGenerate 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 -X POST \
-H "Authorization: Bearer $SIGNALS_API_KEY" \
https://signals.example.com/api/orgs/acme/projects/:project-slug/offline-db/rebuildDownload 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 -L \
-H "Authorization: Bearer $SIGNALS_API_KEY" \
-o offline.acrf \
https://signals.example.com/api/orgs/acme/projects/:project-slug/offline-dbShip 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.
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.
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.
# 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.wavRecognize from a microphone
Use the device list to select an audio input, then keep the command running to evaluate short rolling capture windows locally.
# 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 2Online 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.
- 1Upload the creative
Send audio or video with a scoped write key. Signals normalizes and fingerprints it for the project.
- 2Build the catalog
Trigger a project build once the release media is ready.
- 3Download the release
Fetch the matching database and metadata with a scoped read key, then distribute them to your runtimes.
curl -X POST \
-H "Authorization: Bearer $SIGNALS_API_KEY" \
-F "[email protected]" \
https://signals.example.com/api/orgs/acme/projects/:project-slug/mediacurl -X POST \
-H "Authorization: Bearer $SIGNALS_API_KEY" \
https://signals.example.com/api/orgs/acme/projects/:project-slug/offline-db/rebuildcurl -L \
-H "Authorization: Bearer $SIGNALS_API_KEY" \
-o offline.acrf \
https://signals.example.com/api/orgs/acme/projects/:project-slug/offline-dbStream 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.
.signals-stream-discovery-windows-amd64.exe --config .config.ymlConfigure 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.
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: confirmedscte35auto 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.
# 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: neverMonitor 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.
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: confirmedMonitor 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.
streams:
- id: news-hls
enabled: true
url: https://media.example.com/live/channel.m3u8
input_args: []
scte35: auto
retain_video: candidateRun 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.
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: neverEvents 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.
webhook:
url: https://operations.example.com/signals/events
bearer_token: replace-with-a-secret
retry_seconds: 10The 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.
# 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.
{
"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.
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.
The complete signals_… secret is shown once. Store it in a secret manager and send it as a Bearer token.
Authorization: Bearer signals_your_secret_keyreadreadwritewriteDownload metadata
Metadata maps returned IDs to filenames, durations, and project tags. Download it with the same read key used for the database:
curl -L \
-H "Authorization: Bearer $SIGNALS_API_KEY" \
-o metadata.json \
https://signals.example.com/api/orgs/acme/projects/:project-slug/metadataRoles 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
Build the media database at least once. The download endpoint returns 404 until a completed version exists.
Confirm the Bearer prefix, key scope, permission level, organization slug, and project slug.
Verify the asset was copied to a readable file path and that the runtime and database use compatible formats.
Rebuild the project database and distribute the new database version to the application.
Create a workspace and generate your first private recognition catalog.
Signals