Deterministic musical hash — turn a file's SHA-256 into ≤30 s of pleasant, music-theory-correct audio.
Project description
mhash
A deterministic musical fingerprint for any file. Pipe in some bytes, get back ≤30 seconds of pleasant, music-theory-correct audio. Same hash → identical track.
Like an identicon — but for your ears.
pip install mhash
mhash some/file # plays a unique 30 s song for that file
╭─[ mhash ]──────────────────────────────────────────────────────────────────────╮
│ hash 10d59bff…89f1 source LICENSE mime text/plain │
│ mood M3 sub-flavor 1 tempo 106.6 BPM key G ionian meter 4/4 │
│ form theme_var groove straight_4_4 voicing close_triad curve terraces │
│ matrix 4floor_house humanize machine (jitter ±0) mix balanced │
╰────────────────────────────────────────────────────────────────────────────────────╯
bars A · A · A · A · Avar · Avar · Avar · Avar
energy ▃▃▄▄▅▆▆▇
chords Gmaj | Dmaj | Emin | Cmaj | Gmaj | Dmaj | Emin | Cmaj
roman I | V | vi | IV | I | V | vi | IV
arrangement (rows × sections):
A B fill
drums ◆ ◆ █
bass ◆ ◆ ▪
comp ▣ ▣ ·
lead ◆ ◆ ·
riser ▪ ▪ ·
L1 drums ch10 kit=latin-conga low=lc-d2-tumbao / high=lc-d4-peak-mambo
▶ perc_1 X··X ··X· ··X· X···
▶ tom_high ···· ··XX ···· ··XX
▶ tom_mid X··· X··· ···· X···
L2 bass ch1 prog 032 Acoustic Bass pat=bossa_bass
L3 comp ch2 prog 004 Electric Piano 1 pat=dotted_8th_pad role=piano_stab
L4 lead ch3 prog 011 Vibraphone motif=m44_fnk_01
L5 pad ch4 prog 089 Warm Pad
L6 counter ch5 prog 073 Flute mode=parallel_6th
fx Chorus(0.7Hz) → Delay(375ms) → Reverb(0.55) audio 44100/16-bit @ -16 LUFS
rendered notes 359 peak-poly 12 midi 3.1 KB wav 4.1 MB dur 18.0s took 0.82s
The dashboard shows everything the renderer is doing: the I-V-vi-IV roman numerals next to chord names, an arrangement matrix for which layers play in which form sections (· silent, ▪ sparse, ▣ normal, █ dense, ◆ energy-scaled), a one-bar drum-pattern grid for L1, and a render-stats footer with note count, peak polyphony, and timings.
Why
You can show someone an identicon and they'll spot a wrong digit at a glance. mhash does the same with sound: a deterministic, musical, memorable signature of a file's contents. Different files → different songs. Same file → exact same song, every time, on every machine.
Install
python3 -m venv .venv && source .venv/bin/activate
pip install mhash
brew install fluid-synth # mac; apt install fluidsynth on debian/ubuntu
mhash some/file # auto-downloads a SoundFont (~50 MB) on first run
What you can do
mhash path/to/file # hash + render + play (default)
mhash -o path/to/file # write path/to/file.mhash.wav, no playback
mhash --out my.wav path/to/file # write to a specific path
mhash --mood M14 path/to/file # force a mood (M0..M14)
mhash - # read stdin
cat foo.bin | mhash # ditto, auto-detected
mhash -c 4 some.iso # chunk mode: split into 4-MB pieces,
# play one song per chunk in order
mhash --sf fluidr3 path/to/file # try the FluidR3 SoundFont (auto-downloads ~141 MB)
mhash --sf ~/my.sf2 path/to/file # use any .sf2/.sf3 you already have
mhash -q path/to/file # silence dashboard + progress (just play)
# Power CLI (full flag surface — emit midi/mp3/flac, scores, etc.):
mhash --audio --mp3 --score path/to/file
# Per-mood demo: one sample per M0..M14, stitched into showcase/showcase.wav:
python3 -m mhash.showcase --score
A taste of the spectrum
The same file rendered through three very different moods (forced via --mood).
--mood M14 — chiptune (LSDj-flavored)
╭─[ mhash ]───────────────────────────────────────────────────────────────────╮
│ hash 7c496489…65e5 source README.md mime text/plain │
│ mood M14 sub-flavor 1 tempo 153.7 BPM key C mixolydian meter 4/4 │
│ form pyramid groove straight_4_4 voicing sus_open curve arc │
│ matrix breakdown_pad humanize groove-loose (jitter ±9) mix lead_forward │
╰─────────────────────────────────────────────────────────────────────────────────╯
bars i · A · A · B · B · B · B · A · A · o
energy ▃▄▅▆▆▆▆▅▄▃
chords Amin | Fmaj | Cmaj | Gmaj | Amin | Fmaj | Cmaj | Gmaj | Amin | Fmaj
roman vi | IV | I | V | vi | IV | I | V | vi | IV
L1 drums kit=trap-808 esc=linear_add
▶ hat_closed X·X· X·X· X·X· X·X·
▶ kick X··· ···· ··X· ····
▶ snare ···· ···· X··· ····
L2 bass prog 039 Synth Bass 2 pat=montuno_bass
L3 comp prog 080 Square Lead pat=jazz_freddie role=synth_arp
L4 lead prog 084 Charang Lead motif=m44_pop_08
L5 pad prog 088 New-Age Pad
fx HiShelf(-4dB) → Drive(2dB) → Reverb(0.10)
rendered notes 325 peak-poly 10 midi 3.1 KB wav 4.3 MB dur 15.6s
--mood M0 — ambient
╭─[ mhash ]─────────────────────────────────────────────────────────────────╮
│ hash 10d59bff…89f1 source LICENSE mime text/plain │
│ mood M0 sub-flavor 1 tempo 65.0 BPM key G ionian meter 4/4 │
│ form pulse_only groove straight_4_4 voicing power curve flat_mid │
│ matrix bass_lead_duo humanize machine (jitter ±0) mix balanced │
╰───────────────────────────────────────────────────────────────────────────────╯
bars A · A · A · A · A · A · A
energy ▄▄▄▄▄▄▄
chords Gmaj | Dmaj | Gmaj | Dmaj | Gmaj | Dmaj | Gmaj
roman I | V | I | V | I | V | I
L1 drums kit=acoustic-studio esc=reverse_cymbal_sweep
▶ hat_closed XXXX XXXX XXXX XXXX
▶ kick X··· ·X·· ··X· ····
▶ snare ···· X··· ···· X···
L2 bass prog 038 Synth Bass 1 pat=halfnote_fifth_walk
L3 comp prog 091 Choir Pad pat=sustain_whole role=harp_gliss
L5 pad prog 088 New-Age Pad
L7 drone prog 089 Warm Pad tonic+5th pedal
fx Reverb(0.85) → Chorus(0.6Hz) → LoShelf(-1dB)
rendered notes 64 peak-poly 8 midi 0.9 KB wav 5.3 MB dur 25.9s
--mood M11 — lofi (jazzy)
╭─[ mhash ]────────────────────────────────────────────────────────────────╮
│ hash c32a49aa…2e01 source BYTES.md mime text/plain │
│ mood M11 sub-flavor 0 tempo 70.1 BPM key A# dorian meter 4/4 │
│ form AABA groove dilla_feel voicing open_triad curve arc │
│ matrix band_basic humanize groove-loose (jitter ±9) mix lead_forward │
╰──────────────────────────────────────────────────────────────────────────────╯
bars A · A · A · A · B · B · A · A
energy ▃▄▅▆▆▅▄▃
chords A#maj | Gmin | Cmin | Fmaj | A#maj | Gmin | Cmin | Fmaj
roman I | vi | ii | V | I | vi | ii | V
L1 drums kit=jazz-kit esc=reverse_cymbal_sweep
▶ ride X··X X··· X··X X···
▶ kick X··· ···· X··· ····
▶ snare ···· X··· ···· X···
▶ hat_closed ···· X··· ···· X···
L2 bass prog 038 Synth Bass 1 pat=root_fifth_alt
L3 comp prog 024 Nylon Guitar pat=montuno_2_3 role=piano_arp
L4 lead prog 071 Clarinet motif=m44_pop_04
L6 counter prog 005 Electric Piano 2 mode=parallel_3rd
fx Drive(6dB) → HiShelf(-3dB) → Chorus(0.4Hz) → Reverb(0.40)
rendered notes 193 peak-poly 9 midi 1.9 KB wav 5.3 MB dur 27.4s
How it works
- Hash. SHA-256 of the input bytes.
- Decode. Walk a deterministic HKDF-SHA256 byte stream over 32 macro decisions: mood, tempo, key, mode, time-signature, form, energy curve, chord progression, voicing, drum kit / pattern, bass pattern, comp role, melody motif, contour, FX, mix… A lookup table is pre-filtered by all prior decisions, so every combination produces music-theory-correct output.
- MIDI. Render 9 simultaneous layers — drums (ch10), bass, comp, lead, pad, counter-melody, drone, riser, ear-candy stabs.
- Audio.
fluidsynthsynthesizes the MIDI through a General-MIDI SoundFont;pedalboardapplies a per-mood FX chain (reverb / delay / chorus / saturation / EQ);pyloudnormnormalises to -16 LUFS. - Play. macOS
afplay, Linuxpaplay/aplay, Windows PowerShell SoundPlayer.
15 moods (M0 ambient → M14 gameboy/chiptune), 16 forms, 16 energy curves, 16 grooves, 12 drum kits, 39 chord progressions, 10 voicing styles, 8 escalation algorithms, ~2200 GM samples — all selected deterministically from the hash.
The MIME type of the input file biases the mood selection, so .json and .wav and .mkv tend to sound recognizably different even before content kicks in. (Override with --mime off for pure content-driven output.)
Setup notes
pip install mhashpulls every Python dep (mido,numpy,python-magic,pyloudnorm,pedalboard,soundfile).- Optional extras:
pip install mhash[quality]formosqitopsychoacoustic scoring;pip install mhash[dev]for the test suite +build/twine. - The
fluidsynthsystem binary is required for audio render. macOS:brew install fluid-synth. Debian/Ubuntu:apt install fluidsynth. Windows:scoop install fluidsynth. - The default GM SoundFont (
MS-Basic.sf3, ~50 MB) is auto-downloaded on firstmhashrun intoassets/v1/sf2/. To skip the per-render OGG-decode tax, the installer also produces an uncompressedMS-Basic.sf2(~440 MB additional disk, ~12× faster renders). Override viaSOUNDHASH_SOUNDFONT=/path/to/your.sf2.
Dev
git clone https://github.com/tabledevil/mhash
cd mhash
pip install -e ".[dev]"
pytest -q # 23 fast tests
mhash --self-test # baseline MIDI SHA check
python -m build # builds dist/*.whl + .tar.gz
See DESIGN.md for the full spec, BYTES.md for the byte-by-byte map of how each SHA-256 byte drives a musical decision, AESTHETICS.md for per-mood design intent, and CHANGELOG.md for the journey.
License
MIT. © 2026 tabledevil.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file songhash-0.1.0.tar.gz.
File metadata
- Download URL: songhash-0.1.0.tar.gz
- Upload date:
- Size: 138.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9965786da945b8469b879a03cec8b2c2bcd016c47393dd09515b625754dad4f1
|
|
| MD5 |
317021611fae6a076a304c8fe8d373df
|
|
| BLAKE2b-256 |
e79a2fb0f0b777cb519d6a92ded0cfe3530eb40b9d25cedce1c51de8646ebc7c
|
File details
Details for the file songhash-0.1.0-py3-none-any.whl.
File metadata
- Download URL: songhash-0.1.0-py3-none-any.whl
- Upload date:
- Size: 144.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
df5b719d6c4c83bac8079aeed5ff475c8cfd51916b37c4ec6036ed29f6884853
|
|
| MD5 |
bdd5d122132e329fb68d1bc002e95213
|
|
| BLAKE2b-256 |
1cce843e822bd8cdc35e226540b61d89abc96b3ed488cccdfc81d5eeb5ae33e0
|