Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sample-reader

Measures the audio itself, not the filename.

Loom's existing layer (extract_sound_sources.py) reads sample names out of .als files. Nowhere was the audio itself being measured. This tool fills that gap: it opens an audio file and says what is inside it — and when it cannot say, it does not guess, it writes down why.

Three commands

# 1) Read and measure individual files
python3 -m samplereader read "/some/folder" "/some/file.wav"

# 2) Build a reference profile from our own material
python3 -m samplereader profile "/source/records" --label source-records --out data/profile.json

# 3) Rank candidates against that profile (lower distance = closer)
python3 -m samplereader match data/profile.json "/candidate/folder" --top 25

--workers N reads in parallel (default: cores − 1), --limit N caps the file count, --json gives machine-readable output.

What it measures

field meaning
duration_s, sample_rate, channels, subtype read from the header, not decoded
peak_dbfs, rms_dbfs, crest_db, clipped_samples level
noise_floor_dbfs quietest 5%, digital silence excluded. High on an old transfer (hiss), low on a modern render
silence_share how much of the file is exact silence
stereo_width 0.0 = both channels identical (a mono record in a stereo file)
tempo_bpm, tempo_source tempo and how it was found (loop_length / beat_tracker)
chop_bpm, in_chop_range octave-folded, and whether it lands in the 68–98 window
onset_rate_hz attacks per second — chop density
key, key_confidence Krumhansl-Kessler; if the winner does not clearly beat the runner-up, no key is returned
centroid_hz, rolloff85_hz, bandwidth_hz brightness
low_ratio, air_ratio share of energy below 120 Hz and above 8 kHz — the signature of an old recording is the absence at the top
harmonic_ratio HPSS separation: loops score high, drum breaks low

Tempo: why it is computed from duration

The first version used beat tracking. Measured against 156 library files with the BPM in their filename — 31% correct. The reason: those files are 1–2 bars, i.e. 2.3–5.3 seconds. A bar is four beats; a beat tracker needs eight.

On a short loop the tempo falls exactly out of the duration: bpm = bars × 4 × 60 / duration. Duration does not settle the octave (2.67 s is both one bar at 90 and two bars at 180) — an autocorrelation hint picks that. Measured result:

method accuracy
beat tracking (start_bpm=120) 31%
beat tracking (start_bpm=82) 48%
autocorrelation median 49%
loop length + autocorrelation octave choice 82%

The reference set was itself checked: file durations sit on exact bar counts (1.000, 2.000, 4.000), so the BPM values in the names are real.

To reproduce:

python3 scripts/benchmark_tempo.py "$HOME/Music/Ableton/Factory Packs/Chop and Swing" --step 3 --cap 280

Scoring is octave-tolerant, because that is what the tool has to get right: the chop is folded to 68–98 anyway, so reading 140 as 70 is a hit.

When it refuses to answer

A wrong BPM is worse than no BPM — the next step will look at it and pick a record.

  • file shorter than 1.5 s → one_shot(...), no tempo and no key
  • no plausible bar count between 55 and 210 → no_plausible_bar_count(...)
  • beat tracker found fewer than eight beats, or the pulse wobbles → too_few_beats / unsteady_pulse
  • the top key does not clearly beat the second → ambiguous(margin=...)
  • profile built from fewer than 8 files → the profile is not built, thin_evidence
  • candidate and profile share fewer than 5 dimensions → not scored

Known limit: Ableton's own codec

Most .aif files in the Factory Packs are in Ableton's own AIFF-C compression (able tag). libsndfile, ffmpeg and CoreAudio all fail to open them — only Live can. This is not a bug but a fact about the library; the tool reports it separately as ableton_compressed rather than folding it into a generic "corrupt file" error. In the 280-file measurement above, 98 were unreadable for this reason.

Our own material, measured

data/profile_kaynak_plaklar.json — the chop source records and their demucs stems (Tövbe, Felekten Beter, Bir Araya Gelemeyiz, Haram), 12 files.

  • chop tempo median 84.6, interquartile 78.3 – 89.1 — but see the warning below
  • every measured key is minor, most often E min
  • air_ratio median 0.0011 — almost nothing above 8 kHz
  • centroid median 1680 Hz — dark recordings

12 files barely clears the threshold of 8. The profile is valid as it stands but narrow; it sharpens as more source records are measured.

Warning — tempo on long files is unverified. Those 12 files run for minutes, so their tempo came through beat_tracker, not loop_length, and that path's accuracy was only measured on short loops. Compared against the projects' own settings there is at least one outright error: the Tövbe project is at 140 BPM (folded 70), while the reader says 103.36 for the same source. Treat long-file tempo values as unverified.

Stronger evidence for tempo habit is the .als files themselves — not a guess, but the number written into Live:

python3 scripts/project_tempos.py

200 .als files, 29 still on the untouched 120 default (not counted), 171 projects with a chosen tempo: folded median 86, Q1 70, Q3 98, 53% inside the 68–98 window.

What we have actually used on disk

python3 scripts/used_samples_report.py --out data/used_samples.json

Joins Loom's .als extraction with a disk scan: which sample appeared in which project, where it sits on disk, which pack it came from, and how the ones that can be measured sound. Result in data/used_samples.json. Multisample families collapse to one row — otherwise a 36-note instrument looks like 36 habits.

Tests

python3 tests/test_samplereader.py

16 tests, all on synthesised signals (a click loop at a known BPM, a tone at a known frequency, exact silence). None of them needs the user's audio library.

Dependencies

Installed on the machine: numpy, soundfile, librosa 0.10.1, scipy 1.17.

read.py carries two compatibility patches, both explained in comments: scipy.signal.hann, which librosa 0.10.1 calls and SciPy 1.13 removed, is put back; and the autocorrelation tempo estimator is resolved once because of lazy loading.

Not done

The layer that finds source records on the internet. Awaiting a decision — there is no Logs/, the discussion is in chat.

About

Measures the audio itself, not the filename: tempo, key, brightness, analogue signature - and refuses to answer when the evidence is thin.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages