Draft · Pattern Analysis

How We Match Patterns Across Different Scales

A layman’s overview, a technical blueprint, and working code

Layman’s Explanation

Think of each musical scale as a set of points on a clock. Instead of hours, the clock marks musical distances ("cents") around the circle. Two scales look similar if their points land in similar places — even if one is slightly shifted or stretched. We compare scales by:

We then rank scales by similarity and show "nearby" relatives. This works across tunings and different numbers of notes.

Technical Overview

Representation

Features

Distances

Python Snippets (Working Skeleton)

import math

def wrap_cents(c, period=1200.0):
    x = c % period
    return x + period if x < 0 else x

def interval_histogram(cents, period=1200.0, bin_width=50.0):
    vals = sorted(wrap_cents(c, period) for c in cents)
    k = len(vals)
    if k < 2:
        bins = int(math.ceil((period/2)/bin_width))
        return [0.0] * bins
    diffs = []
    for i in range(k):
        for j in range(i+1, k):
            d = abs(vals[j] - vals[i])
            diffs.append(min(d, period - d))
    bins = [0.0] * int(math.ceil((period/2)/bin_width))
    for d in diffs:
        idx = min(int(d // bin_width), len(bins)-1)
        bins[idx] += 1.0
    s = sum(bins) or 1.0
    return [b/s for b in bins]

def jensen_shannon_distance(p, q):
    def _norm(v):
        s = sum(v) or 1.0
        return [x/s for x in v]
    def _kl(a, b):
        eps = 1e-12
        s = 0.0
        for ai, bi in zip(a, b):
            ai = max(ai, eps); bi = max(bi, eps)
            s += ai * math.log(ai/bi)
        return s
    p = _norm(p); q = _norm(q); m = [(pi+qi)*0.5 for pi,qi in zip(p,q)]
    return math.sqrt(0.5*_kl(p,m) + 0.5*_kl(q,m))

How to Run (Local)

Create a Python venv and run the analysis script to index assets/source/scl, compute features, and print nearest neighbors per scale (uses only Python standard library).

python scripts/scale_analyze.py --scl-dir assets/source/scl --bin-width 50 --topk 8 --limit 200

Roadmap