How it is built
Architecture overview
A collecting society (GEMA in Germany, PRS in the UK, ASCAP in the US) licenses the use of its members' compositions and pays the money back to the right people. CISAC, the confederation of such societies, describes itself as a network of 225 societies in 110 countries (source: CISAC website). The software problem is a very large join under uncertainty. On one side is a repertoire: very many musical works, each with a chain of writers and publishers and their percentage shares, which change over time. On the other side is usage: reports from streaming services, broadcasters, venues and radio monitoring, with very many lines in which a work is named only by a title, an artist and perhaps a recording code. The system matches each usage line to a work, works out who is owed what under the licence and territory rules, rounds and converts money so that the parts add up exactly, and pays out through a ledger that can be reconciled.
The idea that makes it work is that money is computed only after identity is settled, and identity is a confidence-scored match, not a lookup. Exact identifiers (ISRC for a recording, ISWC for a work, IPI for a rights holder) resolve the majority of lines cheaply; probabilistic matching handles the rest; a human queue handles what remains. The share arithmetic is then deterministic integer math over a bitemporal registry, which makes a distribution reproducible and a correction possible after the fact.
Parts:
- Repertoire registry — works, shares, mandates, as-of dates —
bitemporal-data(no page yet), MVCC,merkle-dag(no page yet) - Normalisation — canonical text for titles, names and codes — Text normalization, Unicode text tokenization
- Usage ingest — bulk reports from services, validated and stored by column —
atomic-file-replacement(no page yet), External merge sort, Columnar storage, Vectorized execution, CRC-32C checksums - Matching — usage line to work —
record-linkage(no page yet), Entity resolution, Inverted index, Top-k selection, Near-duplicate detection with MinHash, Loss functions - Rules — which licence, tariff and territory apply to a line — Rete rules engine, Hierarchical state machine
- Distribution engine — shares per work, per party, per period —
data-shuffle(no page yet),rights-chain-share-resolution(no page yet),largest-remainder-rounding(no page yet) - Money — representation, currency, ledger — Money representation,
currency-cross-rate-triangulation(no page yet), Double-entry ledger, Reconciliation - Workflow and reliability — monthly runs, retries, ownership of jobs — DAG workflow execution, Durable job queue, Fencing tokens, fsync and direct I/O
- Compute — vector instructions and hashing — SIMD programming, Cryptographic hashes, Memory ordering
members ─► register works, shares ─► repertoire registry (bitemporal)
│ as-of lookups
services, broadcasters, venues, radio ▼
─► usage files (DDEX DSR, CSV) ─► ingest ─► normalise ─► match ──► matched lines
│ │ exact codes (ISRC, ISWC) │
│ └ fuzzy + model ► review queue
▼ ▼
rules (licence, tariff, territory) ► distribution (shares)
│ integer money
▼
reconcile ◄── ledger (double entry) ◄── statements ► payouts
How the parts fit together
Repertoire registry
Every distribution is "who owned what share of which work on the day of use". A work has writers and publishers with shares that must add up to the whole; shares change when a contract ends or a dispute is settled, often retroactively. The registry therefore keeps two time axes: when the fact is true in the world and when the society learned it (bitemporal-data, no page yet). A distribution run asks for the repertoire as of a date and as known on a date, which is what makes a corrected run reproducible. MVCC gives consistent snapshots to the long-running job while members keep editing, an Interval tree answers "which agreements cover this date", and Time partitioning keeps old periods out of the hot path. Standards carry the same data between societies: DDEX publishes a Musical Work Data and Rights message suite and a Recording Data and Rights suite, among others (source: DDEX standards page). A Content-addressed storage layer for each registered document, hashed with Cryptographic hashes, proves later which version of a split sheet was used (merkle-dag, no page yet, covers chaining versions).
Identity and normalisation
Titles arrive as "Song (feat. X) - Remastered 2011", with different Unicode forms of the same accent, quote marks and punctuation. Text normalization maps each to a canonical form before anything is compared, and Unicode text tokenization splits multilingual text into index terms. Codes are checked, not trusted: an identifier with a bad check digit or length is flagged at ingest. Fast hash functions fingerprint normalised keys for joins and dedupe.
Identifier families matter more than any algorithm. MusicBrainz, an open database, lists ISRC on recordings, ISWC on works and IPI on artists and labels as fields (source: MusicBrainz Database documentation). ISRC (recording) and ISWC (work) are ISO standards, ISO 3901 and ISO 15707, cited by number only; IPI is listed among the CISAC services (source: CISAC website). The chain recording to work is the first lookup: a service reports an ISRC, the registry maps it to a recording, and the recording to the work it performs.
Usage ingest
Usage files are large, regular and tabular. DDEX defines a Digital Sales Reporting message suite for exactly this report (source: DDEX standards page); a society also receives broadcaster cue sheets and spreadsheets. Ingest writes each file atomically (atomic-file-replacement, no page yet), validates it, and stores lines in Columnar storage, because matching and aggregation touch few columns of many rows. Block-level min/max and dictionaries (zone-maps, dictionary-encoding, both no page yet) skip work, and Vectorized execution processes a column at a time. Files bigger than memory are sorted by External merge sort with a k-way-merge (no page yet) of runs, so that lines for the same work sit next to each other. A CRC-32C checksums value per file part detects a truncated transfer before a month of money depends on it.
Matching: from a line to a work
Matching has three tiers. Exact codes: a hash lookup from ISRC or ISWC to a work, handled with a Swiss table in memory. Text search: the title and artist are searched in an Inverted index of work titles and writers; candidate lists come from Posting-list intersection and are ranked by BM25 ranking, keeping the best few with Top-k selection. Probabilistic linkage: the formal framework is Fellegi and Sunter's, which treats matching as a statistical decision over field-by-field comparisons (Fellegi and Sunter, A Theory for Record Linkage, JASA, 1969; record-linkage, no page yet, with fuzzy-string-matching, no page yet). Entity resolution generalises it to clustering of records into entities; Union-find merges pairwise matches into groups, Near-duplicate detection with MinHash generates candidate pairs without comparing all of them, and Embeddings can represent a title and artist as a vector for nearest-neighbour search. A trained scorer (a regression or small network) combines these signals; its calibration decides the threshold between automatic match and human review.
Lines with no usable text or code, typical for radio and venue sound, are identified from the audio. Wang's audio search algorithm uses a combinatorially hashed time-frequency constellation, identifying a short segment against a database of over a million tracks, with search times of milliseconds per query (Wang, An Industrial-Strength Audio Search Algorithm, ISMIR 2003). Chromaprint, the AcoustID client library, extracts fingerprints from any audio source (source: AcoustID).
Rules
A usage line becomes a payable item only under a licence: a service's blanket licence, a broadcaster's tariff, a venue's per-event fee. Rules combine territory, rights type (performing, mechanical), usage type and dates, and they change by agreement. A Rete rules engine matches many rules against the facts of a line without re-evaluating everything for each line; a Hierarchical state machine tracks a claim or dispute through states with inherited transitions (registered, matched, disputed, held, paid). The United States split is an example of how rights types separate: The MLC administers blanket mechanical licences for eligible streaming and download services in the US, and collects and pays mechanical royalties (source: The MLC website).
Distribution engine
For each period the engine joins matched lines with the registry: work to parties, parties to shares, minus society deductions. It is a Hash join on work identifier followed by a group-by on party, spread over workers by Range partitioning or a shuffle (data-shuffle, no page yet). An Arena allocator holds the per-run structures and frees them at the end of the run. Where a work has several rights holders, shares multiply down the chain: a publisher's share of a writer's share times the work's share of the line (rights-chain-share-resolution, no page yet). The result must be exactly the amount received, so allocation uses largest-remainder-rounding (no page yet) on integer amounts.
Money
Amounts are stored as integers in minor units with an explicit currency (Money representation). Foreign usage is converted with a rate set per period; cross rates are derived through a base currency with a defined rounding order (currency-cross-rate-triangulation, no page yet). The ledger is an append-only Double-entry ledger: each distribution run posts balanced entries (receipts, deductions, party balances, payouts), so the total of all party balances plus the society's own always equals what was received. Reconciliation compares the ledger with bank statements and with the reports from other societies, which exchange money under reciprocal agreements.
Workflow and reliability
A monthly or quarterly run is a graph of jobs: ingest, normalise, match, price, distribute, post, export statements. A DAG workflow execution engine releases a job when its inputs exist; jobs are leased from a Durable job queue, retried with Exponential backoff with jitter, and moved to a Dead-letter queue after repeated failure. Every job step has an Idempotency keys value so that a retried job posts to the ledger once. A Leases value keeps two workers from running the same partition, and Fencing tokens make the ledger reject a write from a worker that lost its lease. The run state sits behind a Write-ahead log, and final files are made durable with fsync and direct I/O.
Data and control flow
One quarterly distribution for a streaming service:
- The service delivers a usage report (a DDEX DSR file or a CSV). Ingest checks checksum and schema and stores the file under its content hash.
- Normalisation produces canonical title, artist and codes; lines are written to columnar storage grouped by territory.
- Matching: codes first, then text search, then probabilistic scoring. Matched lines carry a confidence; lines under the threshold go to the review queue and are held, not paid.
- The rules engine assigns the licence, rights type and rate; an unlicensed line is parked with a reason.
- The distribution engine reads the registry as of the usage date, resolves shares down the rights chain, applies deductions and rounds with a largest-remainder rule.
- Posting: one balanced ledger transaction per party per period, keyed by run and party so a retry is a no-op.
- Statements and payouts are generated, bank files are exported, and reconciliation closes the period.
A late correction:
- A member's share on a work is corrected retroactively; the registry records the new fact with the date it became known.
- A job selects affected past lines by work and date range (interval lookup).
- It recomputes only the difference against the earlier distribution and posts adjusting entries in the current period; the past ledger is never edited.
Scaling
In the order you hit them:
- Matching volume. Very many lines, mostly repeats of the same recordings. Lever: deduplicate by normalised key first and match each unique key once, then fan the result back out with a hash join.
- Text-search quality. More recall brings more review work. Lever: tighter blocking, a calibrated model, active learning on reviewer decisions.
- Join and group-by size. Lever: partition by work, columnar scans, vectorised operators, SIMD for the hot loops.
- Registry hot spots. Popular works have many parties. Lever: cache share chains per work and date, invalidate on registry change.
- Run latency. A late file delays payout. Lever: DAG parallelism, per-territory partitions, incremental runs.
- Review queue. Humans are the slowest part. Lever: rank by amount, auto-accept above a confidence, report match rate by source to the service.
Failure modes
| Failure | Symptom | Cause | Fix |
|---|---|---|---|
| Wrong work matched | royalties paid to the wrong writers | fuzzy match above threshold on a common title | calibrated threshold, check artist and duration, dispute process with clawback |
| Unmatched usage | large "unidentified" pool | missing codes, new works not yet registered | ISRC to work table from labels, audio fingerprinting, hold for later matching |
| Shares do not sum to 100 % | distribution rejected or leaks money | data entry errors, overlapping agreements | validate at registration; block activation of an invalid chain |
| Rounding drift | cents missing between receipts and payouts | independent rounding per party | largest-remainder allocation; ledger check that debits equal credits |
| Double posting | party paid twice | job retried after a timeout | idempotency key per run, party and period; fencing on the ledger writer |
| Late data changes history | a closed period no longer matches | retroactive registry edit overwrote old data | bitemporal records; post adjustments in the current period |
| Currency mismatch | balances in the wrong currency | implicit currency, wrong rate date | money type with explicit currency; rate table with effective dates |
| Truncated usage file | month missing for one service | interrupted transfer | checksum and line-count control totals before ingest |
| Duplicate usage file | usage counted twice | file resent | content hash on ingest; reject known hashes |
| Disputed work frozen | money held for long | competing claims on the same work | hold in suspense with an age limit; escalate to the society's committee |
Real implementations
| name | what it is | repo | licence | notes |
|---|---|---|---|---|
| MusicBrainz | open music metadata database with recordings, works, ISRC and ISWC fields | metabrainz/musicbrainz-server | GPL-2.0-or-later (server; data CC0 for core data per the database docs) | a reference for identifier relations, not a royalty system |
| Chromaprint / AcoustID | audio fingerprint library and service | acoustid/chromaprint | MIT own code; whole build LGPL-2.1 because of bundled FFmpeg parts (per LICENSE.md) | recording identification |
| Splink | probabilistic record linkage in Python | moj-analytical-services/splink | MIT | probabilistic record linkage; term-frequency adjustments |
| dedupe | machine-learning fuzzy matching and entity resolution | dedupeio/dedupe | MIT | Python library |
The alternative: a small society or a publisher with a handful of catalogues can run on spreadsheets, a relational database and a few scripts, with human review of every unmatched line. A purpose-built engine pays off when usage files reach many millions of lines and the pending queue costs more than the software.
Sources
- CISAC, website; DDEX, standards and knowledge base
- MusicBrainz, Database documentation; AcoustID, Chromaprint
- I. P. Fellegi, A. B. Sunter, A Theory for Record Linkage, J. Am. Stat. Assoc., 1969. DOI
- A. Li-Chun Wang, An Industrial-Strength Audio Search Algorithm, ISMIR 2003. PDF
- The Mechanical Licensing Collective, website
- Splink: documentation; dedupe: repository
- GEMA: gema.de; PRS for Music: prsformusic.com
- ISO 3901 (ISRC) and ISO 15707 (ISWC), standards with no free public text